mirror of https://github.com/BTCPrivate/copay.git
bwc
This commit is contained in:
parent
04fb7ba032
commit
320de62f13
2
.ctags
2
.ctags
|
@ -1,2 +0,0 @@
|
|||
--exclude=js/copayBundle.js
|
||||
--exclude=lib/bitcore/browser/bundle.js
|
|
@ -1,85 +1,82 @@
|
|||
# from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||
lib-cov
|
||||
*.seed
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.gz
|
||||
*.sw*
|
||||
*.sig
|
||||
tags
|
||||
pids
|
||||
logs
|
||||
results
|
||||
build
|
||||
|
||||
node_modules
|
||||
|
||||
# extras
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.project
|
||||
peerdb.json
|
||||
|
||||
npm-debug.log
|
||||
.nodemonignore
|
||||
|
||||
.DS_Store
|
||||
public/lib/*
|
||||
public/js/angularjs-all.js
|
||||
public/js/main.js
|
||||
public/js/vendors.js
|
||||
|
||||
public/css/main.css
|
||||
|
||||
README.html
|
||||
|
||||
lib/*
|
||||
!lib/socket.io.js
|
||||
!lib/sjcl.js
|
||||
|
||||
js/copayBundle.js
|
||||
js/copayMain.js
|
||||
css/copay.min.css
|
||||
css/vendors.min.css
|
||||
|
||||
# translation
|
||||
po/*
|
||||
!po/*.po
|
||||
js/translations.js
|
||||
src/js/translations.js
|
||||
|
||||
webapp
|
||||
browser-extensions/chrome/copay-chrome-extension
|
||||
browser-extensions/chrome/copay-chrome-extension.zip
|
||||
browser-extensions/firefox/firefox-addon
|
||||
browser-extensions/firefox/data
|
||||
browser-extensions/firefox/copay.xpi
|
||||
version.js
|
||||
!js/controllers/version.js
|
||||
# version
|
||||
src/js/version.js
|
||||
|
||||
# cordova
|
||||
cordova/project/*
|
||||
cordova/*.keystore
|
||||
|
||||
coverage/
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
shell/bin/linux
|
||||
shell/bin/darwin
|
||||
shell/bin/win32
|
||||
# readme
|
||||
README.html
|
||||
|
||||
shell/scripts/bin
|
||||
shell/scripts/build
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
dist/darwin
|
||||
dist/linux
|
||||
dist/windows
|
||||
dist/web
|
||||
dist/*.dmg
|
||||
dist/*.tar.gz
|
||||
dist/*.exe
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
doc/
|
||||
/node_modules
|
||||
/*-cov
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# Commenting this out is preferred by some people, see
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
|
||||
node_modules
|
||||
bower_components
|
||||
|
||||
# Users Environment Variables
|
||||
.lock-wscript
|
||||
|
||||
# OSX
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear on external disk
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# VIM ignore
|
||||
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
*~
|
||||
|
||||
# copay public
|
||||
public/icons/*
|
||||
public/css/*
|
||||
public/lib/*
|
||||
public/js/*
|
||||
|
|
15
.jshint
15
.jshint
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"camelcase": true,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"freeze": true,
|
||||
"indent": 2,
|
||||
"newcap": true,
|
||||
"quotmark": "single",
|
||||
"maxdepth": 3,
|
||||
"maxstatements": 15,
|
||||
"maxlen": 80,
|
||||
"eqnull": true,
|
||||
"funcscope": true,
|
||||
"node": true
|
||||
}
|
256
Gruntfile.js
256
Gruntfile.js
|
@ -1,9 +1,8 @@
|
|||
module.exports = function(grunt) {
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
// Project Configuration
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
release: {
|
||||
options: {
|
||||
bump: true,
|
||||
|
@ -26,11 +25,11 @@ module.exports = function(grunt) {
|
|||
}
|
||||
},
|
||||
exec: {
|
||||
prod: {
|
||||
command: 'node ./util/build.js'
|
||||
version: {
|
||||
command: 'node ./util/version.js'
|
||||
},
|
||||
dev: {
|
||||
command: 'node ./util/build.js -d'
|
||||
clear: {
|
||||
command: 'rm -Rf bower_components node_modules'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -44,52 +43,22 @@ module.exports = function(grunt) {
|
|||
files: ['README.md'],
|
||||
tasks: ['markdown']
|
||||
},
|
||||
scripts: {
|
||||
files: [
|
||||
'js/models/*.js',
|
||||
'js/util/*.js',
|
||||
'js/plugins/*.js',
|
||||
'js/*.js',
|
||||
'!js/copayBundle.js',
|
||||
'!js/copayMain.js'
|
||||
],
|
||||
tasks: ['exec:dev']
|
||||
},
|
||||
css: {
|
||||
files: ['css/src/*.css'],
|
||||
tasks: ['cssmin:desktop']
|
||||
files: ['src/css/*.css'],
|
||||
tasks: ['concat:css']
|
||||
},
|
||||
main: {
|
||||
files: [
|
||||
'js/init.js',
|
||||
'js/app.js',
|
||||
'js/directives.js',
|
||||
'js/filters.js',
|
||||
'js/routes.js',
|
||||
'js/services/*.js',
|
||||
'js/controllers/*.js'
|
||||
'src/js/init.js',
|
||||
'src/js/app.js',
|
||||
'src/js/directives/*.js',
|
||||
'src/js/filters/*.js',
|
||||
'src/js/routes.js',
|
||||
'src/js/services/*.js',
|
||||
'src/js/models/*.js',
|
||||
'src/js/controllers/*.js'
|
||||
],
|
||||
tasks: ['concat:main']
|
||||
},
|
||||
config: {
|
||||
files: ['config.js'],
|
||||
tasks: ['exec:dev', 'concat:main']
|
||||
},
|
||||
test: {
|
||||
files: ['test/**/*.js'],
|
||||
tasks: ['mochaTest']
|
||||
}
|
||||
},
|
||||
mochaTest: {
|
||||
tests: {
|
||||
options: {
|
||||
require: 'setup/node.js',
|
||||
reporter: 'spec',
|
||||
mocha: require('mocha')
|
||||
},
|
||||
src: [
|
||||
'test/*.js',
|
||||
]
|
||||
tasks: ['concat:js']
|
||||
}
|
||||
},
|
||||
markdown: {
|
||||
|
@ -97,72 +66,80 @@ module.exports = function(grunt) {
|
|||
files: [{
|
||||
expand: true,
|
||||
src: 'README.md',
|
||||
dest: '.',
|
||||
dest: './doc',
|
||||
ext: '.html'
|
||||
}]
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
vendors: {
|
||||
src: [
|
||||
'lib/moment/min/moment.min.js',
|
||||
'lib/qrcode-generator/js/qrcode.js',
|
||||
'lib/lodash/dist/lodash.js',
|
||||
'lib/bitcore.js',
|
||||
'lib/file-saver/FileSaver.js',
|
||||
'lib/socket.io-client/socket.io.js',
|
||||
'lib/sjcl.js',
|
||||
'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js',
|
||||
'lib/moment/lang/*.js'
|
||||
],
|
||||
dest: 'lib/vendors.js'
|
||||
options: {
|
||||
sourceMap: false,
|
||||
sourceMapStyle: 'link' // embed, link, inline
|
||||
},
|
||||
angular: {
|
||||
src: [
|
||||
'lib/angular/angular.min.js',
|
||||
'lib/angular-route/angular-route.min.js',
|
||||
'lib/angular-moment/angular-moment.js',
|
||||
'lib/angular-qrcode/qrcode.js',
|
||||
'lib/ng-idle/angular-idle.min.js',
|
||||
'lib/angular-foundation/mm-foundation.min.js',
|
||||
'lib/angular-foundation/mm-foundation-tpls.min.js',
|
||||
'lib/angular-gettext/dist/angular-gettext.min.js',
|
||||
'lib/angular-load/angular-load.min.js',
|
||||
'lib/angular-gravatar/build/md5.min.js',
|
||||
'lib/angular-gravatar/build/angular-gravatar.min.js',
|
||||
'lib/angular-touch/angular-touch.min.js'
|
||||
// If you add libs here, remember to add it too to karma.conf
|
||||
'bower_components/fastclick/lib/fastclick.js',
|
||||
'bower_components/qrcode-generator/js/qrcode.js',
|
||||
'bower_components/qrcode-decoder-js/lib/qrcode-decoder.js',
|
||||
'bower_components/moment/min/moment-with-locales.js',
|
||||
'bower_components/angular/angular.js',
|
||||
'bower_components/angular-ui-router/release/angular-ui-router.js',
|
||||
'bower_components/angular-local-storage/dist/angular-local-storage.js',
|
||||
'bower_components/angular-foundation/mm-foundation.js',
|
||||
'bower_components/angular-foundation/mm-foundation-tpls.js',
|
||||
'bower_components/angular-animate/angular-animate.js',
|
||||
'bower_components/angular-moment/angular-moment.js',
|
||||
'bower_components/ng-lodash/build/ng-lodash.js',
|
||||
'bower_components/angular-qrcode/qrcode.js',
|
||||
'bower_components/angular-gettext/dist/angular-gettext.js',
|
||||
'bower_components/angular-touch/angular-touch.js',
|
||||
'bower_components/angular-bitcore-wallet-client/angular-bitcore-wallet-client.js',
|
||||
'bower_components/angular-ui-switch/angular-ui-switch.js'
|
||||
],
|
||||
dest: 'lib/angularjs-all.js'
|
||||
dest: 'public/lib/angular.js'
|
||||
},
|
||||
main: {
|
||||
js: {
|
||||
src: [
|
||||
'js/app.js',
|
||||
'js/directives.js',
|
||||
'js/filters.js',
|
||||
'js/routes.js',
|
||||
'js/services/*.js',
|
||||
'js/controllers/*.js',
|
||||
'js/translations.js',
|
||||
'js/init.js',
|
||||
'src/js/app.js',
|
||||
'src/js/routes.js',
|
||||
'src/js/directives/*.js',
|
||||
'src/js/filters/*.js',
|
||||
'src/js/models/*.js',
|
||||
'src/js/services/*.js',
|
||||
'src/js/controllers/*.js',
|
||||
'src/js/translations.js',
|
||||
'src/js/version.js',
|
||||
'src/js/init.js'
|
||||
],
|
||||
dest: 'js/copayMain.js'
|
||||
dest: 'public/js/copay.js'
|
||||
},
|
||||
css: {
|
||||
src: ['src/css/*.css'],
|
||||
dest: 'public/css/copay.css'
|
||||
},
|
||||
foundation: {
|
||||
src: [
|
||||
'bower_components/angular/angular-csp.css',
|
||||
'bower_components/foundation/css/foundation.css',
|
||||
'bower_components/animate.css/animate.css',
|
||||
'bower_components/angular-ui-switch/angular-ui-switch.css'
|
||||
],
|
||||
dest: 'public/css/foundation.css',
|
||||
}
|
||||
},
|
||||
cssmin: {
|
||||
desktop: {
|
||||
copay: {
|
||||
files: {
|
||||
'css/copay.min.css': ['css/src/*.css'],
|
||||
'public/css/copay.css': ['src/css/*.css'],
|
||||
}
|
||||
},
|
||||
mobile: {
|
||||
foundation: {
|
||||
files: {
|
||||
'css/copay.min.css': ['css/src/*.css', '!css/src/desktop.css', '!css/src/animation.css'],
|
||||
}
|
||||
},
|
||||
vendors: {
|
||||
files: {
|
||||
'css/vendors.min.css': ['css/foundation.min.css', 'css/foundation-icons.css']
|
||||
'public/css/foundation.css': [
|
||||
'bower_components/angular/angular-csp.css',
|
||||
'bower_components/foundation/css/foundation.css',
|
||||
'bower_components/animate.css/animate.css'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -172,16 +149,15 @@ module.exports = function(grunt) {
|
|||
},
|
||||
prod: {
|
||||
files: {
|
||||
'js/copayMain.js': ['js/copayMain.js'],
|
||||
'lib/angularjs-all.js': ['lib/angularjs-all.js'],
|
||||
'lib/vendors.js': ['lib/vendors.js']
|
||||
'public/js/copay.js': ['public/js/copay.js'],
|
||||
'public/lib/angular.js': ['public/lib/angular.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
nggettext_extract: {
|
||||
pot: {
|
||||
files: {
|
||||
'po/template.pot': ['index.html', 'views/*.html', 'views/**/*.html']
|
||||
'po/template.pot': ['public/index.html', 'public/views/*.html', 'public/views/**/*.html']
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -191,70 +167,58 @@ module.exports = function(grunt) {
|
|||
module: 'copayApp'
|
||||
},
|
||||
files: {
|
||||
'js/translations.js': ['po/*.po']
|
||||
'src/js/translations.js': ['po/*.po']
|
||||
}
|
||||
},
|
||||
},
|
||||
copy: {
|
||||
dist: {
|
||||
files: [
|
||||
{
|
||||
src: [
|
||||
'index.html',
|
||||
'init.js',
|
||||
'config.js',
|
||||
'popup.html',
|
||||
'css/vendors.min.css',
|
||||
'css/copay.min.css',
|
||||
'js/copayBundle.js',
|
||||
'js/copayMain.js',
|
||||
'lib/vendors.js',
|
||||
'lib/angularjs-all.js',
|
||||
'font/**',
|
||||
'img/**',
|
||||
'sound/**',
|
||||
'views/**'
|
||||
],
|
||||
dest: 'dist/web/'
|
||||
icons: {
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: 'bower_components/foundation-icon-fonts/foundation-icons.*',
|
||||
dest: 'public/icons/'
|
||||
}
|
||||
],
|
||||
},
|
||||
karma: {
|
||||
unit: {
|
||||
configFile: 'test/karma.conf.js'
|
||||
},
|
||||
jsdoc: {
|
||||
dist: {
|
||||
src: ['js/models/*.js', 'js/plugins/*.js'],
|
||||
prod: {
|
||||
configFile: 'test/karma.conf.js',
|
||||
singleRun: true
|
||||
}
|
||||
},
|
||||
coveralls: {
|
||||
options: {
|
||||
destination: 'doc',
|
||||
configure: 'jsdoc.conf.json',
|
||||
template: './node_modules/grunt-jsdoc/node_modules/ink-docstrap/template',
|
||||
theme: 'flatly'
|
||||
}
|
||||
debug: false,
|
||||
coverageDir: 'coverage/report-lcov',
|
||||
dryRun: true,
|
||||
force: true,
|
||||
recursive: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-cssmin');
|
||||
grunt.loadNpmTasks('grunt-angular-gettext');
|
||||
grunt.loadNpmTasks('grunt-markdown');
|
||||
grunt.loadNpmTasks('grunt-release');
|
||||
grunt.loadNpmTasks('grunt-exec');
|
||||
grunt.loadNpmTasks('grunt-karma');
|
||||
grunt.loadNpmTasks('grunt-karma-coveralls');
|
||||
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'exec:dev', 'nggettext_compile', 'concat', 'cssmin:desktop', 'cssmin:vendors'
|
||||
]);
|
||||
grunt.registerTask('mobile', [
|
||||
'exec:dev', 'nggettext_compile', 'concat', 'cssmin:mobile', 'cssmin:vendors'
|
||||
]);
|
||||
grunt.registerTask('dist', [
|
||||
'exec:prod', 'nggettext_compile', 'concat', 'cssmin:desktop', 'cssmin:vendors', 'uglify', 'copy:dist'
|
||||
]);
|
||||
grunt.registerTask('dist-dbg', [
|
||||
'exec:prod', 'nggettext_compile', 'concat', 'cssmin:desktop', 'cssmin:vendors', 'copy:dist'
|
||||
]);
|
||||
grunt.registerTask('dist-mobile', [
|
||||
'exec:prod', 'nggettext_compile', 'concat', 'cssmin:mobile', 'cssmin:vendors', 'uglify', 'copy:dist'
|
||||
]);
|
||||
grunt.registerTask('dist-mobile-dbg', [
|
||||
'exec:dev', 'nggettext_compile', 'concat', 'cssmin:mobile', 'cssmin:vendors', 'copy:dist'
|
||||
'nggettext_compile', 'exec:version', 'concat', 'copy'
|
||||
]);
|
||||
grunt.registerTask('prod', [
|
||||
'exec:prod', 'nggettext_compile', 'concat', 'cssmin:desktop', 'cssmin:vendors', 'uglify'
|
||||
'default', 'uglify'
|
||||
]);
|
||||
grunt.registerTask('translate', ['nggettext_extract']);
|
||||
grunt.registerTask('docs', ['jsdoc']);
|
||||
grunt.registerTask('test', ['karma:unit']);
|
||||
grunt.registerTask('test-coveralls', ['karma:prod', 'coveralls']);
|
||||
};
|
||||
|
|
18
Makefile
18
Makefile
|
@ -21,8 +21,8 @@ cordova-base:
|
|||
# release-android: cordova-base
|
||||
# make -C cordova release-android
|
||||
#
|
||||
wp8:
|
||||
cordova/build.sh WP8
|
||||
wp8-prod:
|
||||
cordova/build.sh WP8 --clear
|
||||
cordova/wp/fix-svg.sh
|
||||
echo -e "\a"
|
||||
|
||||
|
@ -31,7 +31,7 @@ wp8-debug:
|
|||
cordova/wp/fix-svg.sh
|
||||
echo -e "\a"
|
||||
|
||||
ios:
|
||||
ios-prod:
|
||||
cordova/build.sh IOS --clear
|
||||
cd cordova/project && cordova build ios
|
||||
open cordova/project/platforms/ios/Copay.xcodeproj
|
||||
|
@ -41,10 +41,14 @@ ios-debug:
|
|||
cd cordova/project && cordova build ios
|
||||
open cordova/project/platforms/ios/Copay.xcodeproj
|
||||
|
||||
android:
|
||||
cordova/build.sh ANDROID --dbgjs --clear
|
||||
cd cordova/project && cordova run android
|
||||
|
||||
android-prod:
|
||||
cordova/build.sh ANDROID --clear
|
||||
cd cordova/project && cordova build android --release
|
||||
|
||||
android-debug:
|
||||
cordova/build.sh ANDROID --dbgjs --clear
|
||||
cd cordova/project && cordova run android
|
||||
|
||||
android-debug-fast:
|
||||
cordova/build.sh ANDROID --dbgjs
|
||||
cd cordova/project && cordova run android
|
||||
|
|
53
app.js
53
app.js
|
@ -1,54 +1,13 @@
|
|||
var express = require('express');
|
||||
var http = require('http');
|
||||
var path = require('path');
|
||||
var app = express();
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// This is not really necesarry, just to simulate
|
||||
// Content Security Policy from Google Chrome Apps.
|
||||
//
|
||||
// app.use(function(req, res, next){
|
||||
// res.header("Content-Security-Policy", "script-src 'self';object-src 'none';media-src 'self';frame-src 'none';font-src 'self' data:");
|
||||
// next();
|
||||
// });
|
||||
var port = process.env.PORT || 3000;
|
||||
app.listen(port);
|
||||
console.log("App listening on port " + port);
|
||||
|
||||
app.use('/', express.static(__dirname + '/'));
|
||||
app.get('*', function(req, res) {
|
||||
return res.sendFile(__dirname + '/' + 'index.html');
|
||||
res.sendFile(path.join(__dirname, 'public'));
|
||||
});
|
||||
|
||||
app.start = function(port, callback) {
|
||||
|
||||
app.set('port', port);
|
||||
app.use(express.static(__dirname));
|
||||
|
||||
if (process.env.USE_HTTPS) {
|
||||
var path = require('path');
|
||||
|
||||
var bc = path.dirname(require.resolve('bitcore/package.json'));
|
||||
var pserver = require(bc + '/examples/PayPro/server.js');
|
||||
|
||||
pserver.removeListener('request', pserver.app);
|
||||
|
||||
// pserver.options['no-tx'] = true;
|
||||
// pserver.options['discovery'] = true;
|
||||
|
||||
pserver.on('request', function(req, res) {
|
||||
if (req.url.indexOf('/-/') === 0) {
|
||||
return pserver.app(req, res);
|
||||
}
|
||||
return app(req, res);
|
||||
});
|
||||
|
||||
pserver.listen(port, function() {
|
||||
callback('https://localhost:' + port);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app.listen(port, function() {
|
||||
callback('http://localhost:' + port);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = app;
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
|
||||
# Warning
|
||||
|
||||
This NEEDS to be updated.
|
||||
|
||||
## Running in the Native Shell
|
||||
|
||||
Copay can be executed from within a "native" application shell, providing some
|
26
bower.json
26
bower.json
|
@ -3,19 +3,23 @@
|
|||
"keywords": [
|
||||
"copay",
|
||||
"wallet",
|
||||
"multisignature"
|
||||
"multisignature",
|
||||
"bircore"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "~1.3.0",
|
||||
"angular": "~1.3.13",
|
||||
"angular-foundation": "*",
|
||||
"angular-route": "~1.3.0",
|
||||
"angular-qrcode": "~3.1.0",
|
||||
"angular-mocks": "~1.3.0",
|
||||
"angular-gettext": "~1.1.0",
|
||||
"mocha": "~1.18.2",
|
||||
"chai": "~1.9.1",
|
||||
"sjcl": "1.0.0",
|
||||
"file-saver": "*",
|
||||
"angular-animate": "~1.3.13",
|
||||
"angular-touch": "~1.3.0",
|
||||
"angular-qrcode": "~5.1.0",
|
||||
"angular-gettext": "~2.0.5",
|
||||
"animate.css": "~3.2.0",
|
||||
"foundation": "zurb/bower-foundation#~5.5.1",
|
||||
"foundation-icon-fonts": "*",
|
||||
"ng-lodash": "~0.2.0",
|
||||
"angular-moment": "~0.9.0",
|
||||
"angular-bitcore-wallet-client": "^0.0.14",
|
||||
"angular-ui-router": "~0.2.13",
|
||||
"qrcode-decoder-js": "*",
|
||||
"angular-moment": "~0.7.1",
|
||||
"socket.io-client": ">=1.0.0",
|
||||
|
@ -26,6 +30,6 @@
|
|||
"angular-touch": "~1.3.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "~1.3.0"
|
||||
"qrcode-generator": "0.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
Development
|
||||
|
||||
* Just run:
|
||||
|
||||
```
|
||||
$ sh chrome/build.sh
|
||||
```
|
||||
|
||||
* The ZIP file is *chrome/copay-chrome-extension.zip*
|
|
@ -1,64 +0,0 @@
|
|||
#! /bin/bash
|
||||
|
||||
# Description: This script compiles and copy the needed files to later package the application for Chrome
|
||||
|
||||
OpenColor="\033["
|
||||
Red="1;31m"
|
||||
Yellow="1;33m"
|
||||
Green="1;32m"
|
||||
CloseColor="\033[0m"
|
||||
|
||||
# Check function OK
|
||||
checkOK() {
|
||||
if [ $? != 0 ]; then
|
||||
echo "${OpenColor}${Red}* ERROR. Exiting...${CloseColor}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Configs
|
||||
BUILDDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
APPDIR="$BUILDDIR/copay-chrome-extension"
|
||||
ZIPFILE="copay-chrome-extension.zip"
|
||||
VERSION=`cut -d '"' -f2 $BUILDDIR/../../version.js|head -n 1`
|
||||
|
||||
# Move to the build directory
|
||||
cd $BUILDDIR
|
||||
|
||||
# Create/Clean temp dir
|
||||
echo "${OpenColor}${Green}* Checking temp dir...${CloseColor}"
|
||||
if [ -d $APPDIR ]; then
|
||||
rm -rf $APPDIR
|
||||
fi
|
||||
|
||||
mkdir -p $APPDIR
|
||||
|
||||
# Re-compile copayBundle.js
|
||||
echo "${OpenColor}${Green}* Generating copay bundle...${CloseColor}"
|
||||
grunt prod
|
||||
checkOK
|
||||
|
||||
# Copy all chrome-extension files
|
||||
echo "${OpenColor}${Green}* Copying all chrome-extension files...${CloseColor}"
|
||||
sed "s/APP_VERSION/$VERSION/g" manifest.json > $APPDIR/manifest.json
|
||||
checkOK
|
||||
|
||||
|
||||
INCLUDE=`cat ../include`
|
||||
cd $BUILDDIR/../..
|
||||
LIBS=`cat index.html |grep -o -E 'src="([^"#]+)"' | cut -d'"' -f2|grep lib`
|
||||
echo "LIBS: $LIBS"
|
||||
|
||||
CMD="rsync -rLRv --exclude-from $BUILDDIR/../exclude $INCLUDE $LIBS $APPDIR"
|
||||
echo $CMD
|
||||
$CMD
|
||||
checkOK
|
||||
|
||||
# Zipping chrome-extension
|
||||
echo "${OpenColor}${Green}* Zipping all chrome-extension files...${CloseColor}"
|
||||
cd $BUILDDIR
|
||||
rm $ZIPFILE
|
||||
zip -qr $ZIPFILE "`basename $APPDIR`"
|
||||
checkOK
|
||||
|
||||
echo "${OpenColor}${Yellow}\nThe Chrome Extension is ready at $BUILDDIR/copay-chrome-extension.zip${CloseColor}"
|
|
@ -1,8 +0,0 @@
|
|||
chrome.app.runtime.onLaunched.addListener(function() {
|
||||
chrome.app.window.create('index.html', {
|
||||
'bounds': {
|
||||
'width': 1200,
|
||||
'height': 800
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Copay",
|
||||
"description": "A secure Bitcoin wallet for friends and companies",
|
||||
"version": "APP_VERSION",
|
||||
"permissions": [
|
||||
"storage",
|
||||
"notifications",
|
||||
"videoCapture"
|
||||
],
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["initial.js"]
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"128": "img/icons/icon.png"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 30 KiB |
|
@ -1,19 +0,0 @@
|
|||
lib/socket.io
|
||||
lib/*/test
|
||||
lib/*/demo
|
||||
lib/sjcl/
|
||||
lib/angular/angular.js
|
||||
lib/moment/lang
|
||||
lib/moment/min/*lang*
|
||||
lib/moment/moment.js
|
||||
lib/angular/angular.min.js.gzip
|
||||
lib/bitcore/node_modules
|
||||
lib/bitcore/.git
|
||||
lib/bitcore/docs
|
||||
lib/bitcore/lib
|
||||
lib/bitcore/examples
|
||||
lib/bitcore/coverage
|
||||
lib/bitcore/build
|
||||
.git
|
||||
tests
|
||||
.*
|
|
@ -1,16 +0,0 @@
|
|||
css
|
||||
font
|
||||
img
|
||||
js
|
||||
sound
|
||||
views
|
||||
config.js
|
||||
init.js
|
||||
initial.js
|
||||
version.js
|
||||
index.html
|
||||
popup.html
|
||||
lib/angular/angular-csp.css
|
||||
lib/angular/angular.min.js.map
|
||||
lib/angular-route/angular-route.min.js.map
|
||||
lib/angular-touch/angular-touch.min.js.map
|
96
config.js
96
config.js
|
@ -1,96 +0,0 @@
|
|||
'use strict';
|
||||
var defaultConfig = {
|
||||
// DEFAULT network (livenet or testnet)
|
||||
networkName: 'livenet',
|
||||
logLevel: 'info',
|
||||
|
||||
|
||||
// wallet limits
|
||||
limits: {
|
||||
totalCopayers: 6,
|
||||
mPlusN: 100,
|
||||
},
|
||||
|
||||
// network layer config
|
||||
network: {
|
||||
testnet: {
|
||||
url: 'https://test-insight.bitpay.com:443',
|
||||
transports: ['polling'],
|
||||
},
|
||||
livenet: {
|
||||
url: 'https://insight.bitpay.com:443',
|
||||
transports: ['polling'],
|
||||
},
|
||||
},
|
||||
|
||||
// wallet default config
|
||||
wallet: {
|
||||
requiredCopayers: 2,
|
||||
totalCopayers: 3,
|
||||
spendUnconfirmed: true,
|
||||
reconnectDelay: 5000,
|
||||
idleDurationMin: 4,
|
||||
settings: {
|
||||
unitName: 'bits',
|
||||
unitToSatoshi: 100,
|
||||
unitDecimals: 2,
|
||||
alternativeName: 'US Dollar',
|
||||
alternativeIsoCode: 'USD',
|
||||
}
|
||||
},
|
||||
|
||||
// local encryption/security config
|
||||
passphraseConfig: {
|
||||
iterations: 5000,
|
||||
storageSalt: 'mjuBtGybi/4=',
|
||||
},
|
||||
|
||||
rates: {
|
||||
url: 'https://insight.bitpay.com:443/api/rates',
|
||||
},
|
||||
|
||||
verbose: 1,
|
||||
|
||||
plugins: {
|
||||
//LocalStorage: true,
|
||||
// EncryptedLocalStorage: true,
|
||||
//GoogleDrive: true,
|
||||
//InsightStorage: true
|
||||
EncryptedInsightStorage: true,
|
||||
},
|
||||
|
||||
// This can be changed on the UX > Settings > Insight livenet
|
||||
EncryptedInsightStorage: {
|
||||
url: 'https://insight.bitpay.com:443/api/email',
|
||||
//url: 'http://localhost:3001/api/email'
|
||||
|
||||
// This KDF parameters are for the passphrase for Insight authentication
|
||||
// Are not related to encryption itself.
|
||||
//
|
||||
// WARN: Changing this parameters would prevent accesing previously created profiles.
|
||||
iterations: 1000,
|
||||
salt: 'jBbYTj8zTrOt6V',
|
||||
},
|
||||
|
||||
minPasswordStrength: 4,
|
||||
|
||||
/*
|
||||
GoogleDrive: {
|
||||
home: 'copay',
|
||||
|
||||
|
||||
// This clientId was generated at:
|
||||
// https://console.developers.google.com/project
|
||||
// To run Copay with Google Drive at your domain you need
|
||||
// to generata your own Id.
|
||||
// for localhost:3001 you can use you can:
|
||||
//
|
||||
clientId: '232630733383-a35gcnovnkgka94394i88gq60vtjb4af.apps.googleusercontent.com',
|
||||
|
||||
// for copay.io:
|
||||
// clientId: '1036948132229-biqm3b8sirik9lt5rtvjo9kjjpotn4ac.apps.googleusercontent.com',
|
||||
},
|
||||
*/
|
||||
};
|
||||
if (typeof module !== 'undefined')
|
||||
module.exports = defaultConfig;
|
26
copay.js
26
copay.js
|
@ -1,26 +0,0 @@
|
|||
// core
|
||||
module.exports.PublicKeyRing = require('./js/models/PublicKeyRing');
|
||||
module.exports.TxProposal = require('./js/models/TxProposal');
|
||||
module.exports.TxProposals = require('./js/models/TxProposals');
|
||||
module.exports.PrivateKey = require('./js/models/PrivateKey');
|
||||
module.exports.HDPath = require('./js/models/HDPath');
|
||||
module.exports.HDParams = require('./js/models/HDParams');
|
||||
module.exports.Async = require('./js/models/Async');
|
||||
module.exports.Insight = require('./js/models/Insight');
|
||||
module.exports.RateService = require('./js/models/RateService');
|
||||
module.exports.Identity = require('./js/models/Identity');
|
||||
module.exports.Wallet = require('./js/models/Wallet');
|
||||
module.exports.Compatibility = require('./js/models/Compatibility');
|
||||
module.exports.PluginManager = require('./js/models/PluginManager');
|
||||
|
||||
|
||||
module.exports.crypto = require('./js/util/crypto');
|
||||
module.exports.logger = require('./js/util/log');
|
||||
module.exports.csv = require('./js/util/csv');
|
||||
|
||||
module.exports.version = require('./version').version;
|
||||
module.exports.commitHash = require('./version').commitHash;
|
||||
|
||||
|
||||
module.exports.defaultConfig = require('./config');
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="v0.9.7" android:windowSoftInputMode="adjustPan" package="com.bitpay.copay" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="v0.10.0" android:windowSoftInputMode="adjustPan" package="com.bitpay.copay" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application android:hardwareAccelerated="true" android:icon="@drawable/copay" android:label="@string/app_name">
|
||||
|
@ -9,7 +9,7 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:clearTaskOnLaunch="true" android:configChanges="orientation|keyboardHidden" android:exported="false" android:name="com.google.zxing.client.android.CaptureActivity" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden">
|
||||
<activity android:clearTaskOnLaunch="true" android:configChanges="orientation|screenSize|keyboardHidden" android:exported="false" android:name="com.google.zxing.client.android.CaptureActivity" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden">
|
||||
<intent-filter>
|
||||
<action android:name="com.phonegap.plugins.barcodescanner.SCAN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
|
|
@ -15,7 +15,6 @@ checkOK() {
|
|||
# Configs
|
||||
BUILDDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT="$BUILDDIR/project"
|
||||
VERSION=`cut -d '"' -f2 $BUILDDIR/../version.js`
|
||||
|
||||
CURRENT_OS=$1
|
||||
|
||||
|
@ -115,27 +114,39 @@ if [ ! -d $PROJECT ]; then
|
|||
cordova plugin add hu.dpal.phonegap.plugins.spinnerdialog
|
||||
checkOK
|
||||
|
||||
cordova plugin add org.apache.cordova.dialogs
|
||||
checkOK
|
||||
|
||||
cordova plugin add org.apache.cordova.network-information
|
||||
checkOK
|
||||
|
||||
cordova plugin add org.apache.cordova.console
|
||||
checkok
|
||||
|
||||
cordova plugin add hu.dpal.phonegap.plugins.uniquedeviceid
|
||||
checkok
|
||||
|
||||
fi
|
||||
|
||||
if $DBGJS
|
||||
then
|
||||
echo "${OpenColor}${Green}* Generating copay bundle (debug js)...${CloseColor}"
|
||||
cd $BUILDDIR/..
|
||||
grunt dist-mobile-dbg
|
||||
grunt
|
||||
checkOK
|
||||
else
|
||||
echo "${OpenColor}${Green}* Generating copay bundle...${CloseColor}"
|
||||
cd $BUILDDIR/..
|
||||
grunt dist-mobile
|
||||
grunt prod
|
||||
checkOK
|
||||
fi
|
||||
|
||||
echo "${OpenColor}${Green}* Copying files...${CloseColor}"
|
||||
cd $BUILDDIR/..
|
||||
cp -af dist/web/** $PROJECT/www
|
||||
cp -af public/** $PROJECT/www
|
||||
checkOK
|
||||
|
||||
sed "s/<\!-- PLACEHOLDER: CORDOVA SRIPT -->/<script type='text\/javascript' charset='utf-8' src='cordova.js'><\/script>/g" index.html > $PROJECT/www/index.html
|
||||
sed "s/<\!-- PLACEHOLDER: CORDOVA SRIPT -->/<script type='text\/javascript' charset='utf-8' src='cordova.js'><\/script>/g" public/index.html > $PROJECT/www/index.html
|
||||
checkOK
|
||||
|
||||
cd $BUILDDIR
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
|
||||
<Capability Name="ID_CAP_NETWORKING" />
|
||||
<Capability Name="ID_CAP_ISV_CAMERA" />
|
||||
<Capability Name="ID_CAP_IDENTITY_DEVICE" />
|
||||
</Capabilities>
|
||||
<Tasks>
|
||||
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
|
||||
|
|
|
@ -1,594 +0,0 @@
|
|||
/*
|
||||
* Foundation Icons v 3.0
|
||||
* Made by ZURB 2013 http://zurb.com/playground/foundation-icon-fonts-3
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: "foundation-icons";
|
||||
src: url("../font/foundation-icons.eot");
|
||||
src: url("../font/foundation-icons.eot?#iefix") format("embedded-opentype"),
|
||||
url("../font/foundation-icons.woff") format("woff"),
|
||||
url("../font/foundation-icons.ttf") format("truetype"),
|
||||
url("../font/foundation-icons.svg#fontcustom") format("svg");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.fi-address-book:before,
|
||||
.fi-alert:before,
|
||||
.fi-align-center:before,
|
||||
.fi-align-justify:before,
|
||||
.fi-align-left:before,
|
||||
.fi-align-right:before,
|
||||
.fi-anchor:before,
|
||||
.fi-annotate:before,
|
||||
.fi-archive:before,
|
||||
.fi-arrow-down:before,
|
||||
.fi-arrow-left:before,
|
||||
.fi-arrow-right:before,
|
||||
.fi-arrow-up:before,
|
||||
.fi-arrows-compress:before,
|
||||
.fi-arrows-expand:before,
|
||||
.fi-arrows-in:before,
|
||||
.fi-arrows-out:before,
|
||||
.fi-asl:before,
|
||||
.fi-asterisk:before,
|
||||
.fi-at-sign:before,
|
||||
.fi-background-color:before,
|
||||
.fi-battery-empty:before,
|
||||
.fi-battery-full:before,
|
||||
.fi-battery-half:before,
|
||||
.fi-bitcoin-circle:before,
|
||||
.fi-bitcoin:before,
|
||||
.fi-blind:before,
|
||||
.fi-bluetooth:before,
|
||||
.fi-bold:before,
|
||||
.fi-book-bookmark:before,
|
||||
.fi-book:before,
|
||||
.fi-bookmark:before,
|
||||
.fi-braille:before,
|
||||
.fi-burst-new:before,
|
||||
.fi-burst-sale:before,
|
||||
.fi-burst:before,
|
||||
.fi-calendar:before,
|
||||
.fi-camera:before,
|
||||
.fi-check:before,
|
||||
.fi-checkbox:before,
|
||||
.fi-clipboard-notes:before,
|
||||
.fi-clipboard-pencil:before,
|
||||
.fi-clipboard:before,
|
||||
.fi-clock:before,
|
||||
.fi-closed-caption:before,
|
||||
.fi-cloud:before,
|
||||
.fi-comment-minus:before,
|
||||
.fi-comment-quotes:before,
|
||||
.fi-comment-video:before,
|
||||
.fi-comment:before,
|
||||
.fi-comments:before,
|
||||
.fi-compass:before,
|
||||
.fi-contrast:before,
|
||||
.fi-credit-card:before,
|
||||
.fi-crop:before,
|
||||
.fi-crown:before,
|
||||
.fi-css3:before,
|
||||
.fi-database:before,
|
||||
.fi-die-five:before,
|
||||
.fi-die-four:before,
|
||||
.fi-die-one:before,
|
||||
.fi-die-six:before,
|
||||
.fi-die-three:before,
|
||||
.fi-die-two:before,
|
||||
.fi-dislike:before,
|
||||
.fi-dollar-bill:before,
|
||||
.fi-dollar:before,
|
||||
.fi-download:before,
|
||||
.fi-eject:before,
|
||||
.fi-elevator:before,
|
||||
.fi-euro:before,
|
||||
.fi-eye:before,
|
||||
.fi-fast-forward:before,
|
||||
.fi-female-symbol:before,
|
||||
.fi-female:before,
|
||||
.fi-filter:before,
|
||||
.fi-first-aid:before,
|
||||
.fi-flag:before,
|
||||
.fi-folder-add:before,
|
||||
.fi-folder-lock:before,
|
||||
.fi-folder:before,
|
||||
.fi-foot:before,
|
||||
.fi-foundation:before,
|
||||
.fi-graph-bar:before,
|
||||
.fi-graph-horizontal:before,
|
||||
.fi-graph-pie:before,
|
||||
.fi-graph-trend:before,
|
||||
.fi-guide-dog:before,
|
||||
.fi-hearing-aid:before,
|
||||
.fi-heart:before,
|
||||
.fi-home:before,
|
||||
.fi-html5:before,
|
||||
.fi-indent-less:before,
|
||||
.fi-indent-more:before,
|
||||
.fi-info:before,
|
||||
.fi-italic:before,
|
||||
.fi-key:before,
|
||||
.fi-laptop:before,
|
||||
.fi-layout:before,
|
||||
.fi-lightbulb:before,
|
||||
.fi-like:before,
|
||||
.fi-link:before,
|
||||
.fi-list-bullet:before,
|
||||
.fi-list-number:before,
|
||||
.fi-list-thumbnails:before,
|
||||
.fi-list:before,
|
||||
.fi-lock:before,
|
||||
.fi-loop:before,
|
||||
.fi-magnifying-glass:before,
|
||||
.fi-mail:before,
|
||||
.fi-male-female:before,
|
||||
.fi-male-symbol:before,
|
||||
.fi-male:before,
|
||||
.fi-map:before,
|
||||
.fi-marker:before,
|
||||
.fi-megaphone:before,
|
||||
.fi-microphone:before,
|
||||
.fi-minus-circle:before,
|
||||
.fi-minus:before,
|
||||
.fi-mobile-signal:before,
|
||||
.fi-mobile:before,
|
||||
.fi-monitor:before,
|
||||
.fi-mountains:before,
|
||||
.fi-music:before,
|
||||
.fi-next:before,
|
||||
.fi-no-dogs:before,
|
||||
.fi-no-smoking:before,
|
||||
.fi-page-add:before,
|
||||
.fi-page-copy:before,
|
||||
.fi-page-csv:before,
|
||||
.fi-page-delete:before,
|
||||
.fi-page-doc:before,
|
||||
.fi-page-edit:before,
|
||||
.fi-page-export-csv:before,
|
||||
.fi-page-export-doc:before,
|
||||
.fi-page-export-pdf:before,
|
||||
.fi-page-export:before,
|
||||
.fi-page-filled:before,
|
||||
.fi-page-multiple:before,
|
||||
.fi-page-pdf:before,
|
||||
.fi-page-remove:before,
|
||||
.fi-page-search:before,
|
||||
.fi-page:before,
|
||||
.fi-paint-bucket:before,
|
||||
.fi-paperclip:before,
|
||||
.fi-pause:before,
|
||||
.fi-paw:before,
|
||||
.fi-paypal:before,
|
||||
.fi-pencil:before,
|
||||
.fi-photo:before,
|
||||
.fi-play-circle:before,
|
||||
.fi-play-video:before,
|
||||
.fi-play:before,
|
||||
.fi-plus:before,
|
||||
.fi-pound:before,
|
||||
.fi-power:before,
|
||||
.fi-previous:before,
|
||||
.fi-price-tag:before,
|
||||
.fi-pricetag-multiple:before,
|
||||
.fi-print:before,
|
||||
.fi-prohibited:before,
|
||||
.fi-projection-screen:before,
|
||||
.fi-puzzle:before,
|
||||
.fi-quote:before,
|
||||
.fi-record:before,
|
||||
.fi-refresh:before,
|
||||
.fi-results-demographics:before,
|
||||
.fi-results:before,
|
||||
.fi-rewind-ten:before,
|
||||
.fi-rewind:before,
|
||||
.fi-rss:before,
|
||||
.fi-safety-cone:before,
|
||||
.fi-save:before,
|
||||
.fi-share:before,
|
||||
.fi-sheriff-badge:before,
|
||||
.fi-shield:before,
|
||||
.fi-shopping-bag:before,
|
||||
.fi-shopping-cart:before,
|
||||
.fi-shuffle:before,
|
||||
.fi-skull:before,
|
||||
.fi-social-500px:before,
|
||||
.fi-social-adobe:before,
|
||||
.fi-social-amazon:before,
|
||||
.fi-social-android:before,
|
||||
.fi-social-apple:before,
|
||||
.fi-social-behance:before,
|
||||
.fi-social-bing:before,
|
||||
.fi-social-blogger:before,
|
||||
.fi-social-delicious:before,
|
||||
.fi-social-designer-news:before,
|
||||
.fi-social-deviant-art:before,
|
||||
.fi-social-digg:before,
|
||||
.fi-social-dribbble:before,
|
||||
.fi-social-drive:before,
|
||||
.fi-social-dropbox:before,
|
||||
.fi-social-evernote:before,
|
||||
.fi-social-facebook:before,
|
||||
.fi-social-flickr:before,
|
||||
.fi-social-forrst:before,
|
||||
.fi-social-foursquare:before,
|
||||
.fi-social-game-center:before,
|
||||
.fi-social-github:before,
|
||||
.fi-social-google-plus:before,
|
||||
.fi-social-hacker-news:before,
|
||||
.fi-social-hi5:before,
|
||||
.fi-social-instagram:before,
|
||||
.fi-social-joomla:before,
|
||||
.fi-social-lastfm:before,
|
||||
.fi-social-linkedin:before,
|
||||
.fi-social-medium:before,
|
||||
.fi-social-myspace:before,
|
||||
.fi-social-orkut:before,
|
||||
.fi-social-path:before,
|
||||
.fi-social-picasa:before,
|
||||
.fi-social-pinterest:before,
|
||||
.fi-social-rdio:before,
|
||||
.fi-social-reddit:before,
|
||||
.fi-social-skillshare:before,
|
||||
.fi-social-skype:before,
|
||||
.fi-social-smashing-mag:before,
|
||||
.fi-social-snapchat:before,
|
||||
.fi-social-spotify:before,
|
||||
.fi-social-squidoo:before,
|
||||
.fi-social-stack-overflow:before,
|
||||
.fi-social-steam:before,
|
||||
.fi-social-stumbleupon:before,
|
||||
.fi-social-treehouse:before,
|
||||
.fi-social-tumblr:before,
|
||||
.fi-social-twitter:before,
|
||||
.fi-social-vimeo:before,
|
||||
.fi-social-windows:before,
|
||||
.fi-social-xbox:before,
|
||||
.fi-social-yahoo:before,
|
||||
.fi-social-yelp:before,
|
||||
.fi-social-youtube:before,
|
||||
.fi-social-zerply:before,
|
||||
.fi-social-zurb:before,
|
||||
.fi-sound:before,
|
||||
.fi-star:before,
|
||||
.fi-stop:before,
|
||||
.fi-strikethrough:before,
|
||||
.fi-subscript:before,
|
||||
.fi-superscript:before,
|
||||
.fi-tablet-landscape:before,
|
||||
.fi-tablet-portrait:before,
|
||||
.fi-target-two:before,
|
||||
.fi-target:before,
|
||||
.fi-telephone-accessible:before,
|
||||
.fi-telephone:before,
|
||||
.fi-text-color:before,
|
||||
.fi-thumbnails:before,
|
||||
.fi-ticket:before,
|
||||
.fi-torso-business:before,
|
||||
.fi-torso-female:before,
|
||||
.fi-torso:before,
|
||||
.fi-torsos-all-female:before,
|
||||
.fi-torsos-all:before,
|
||||
.fi-torsos-female-male:before,
|
||||
.fi-torsos-male-female:before,
|
||||
.fi-torsos:before,
|
||||
.fi-trash:before,
|
||||
.fi-trees:before,
|
||||
.fi-trophy:before,
|
||||
.fi-underline:before,
|
||||
.fi-universal-access:before,
|
||||
.fi-unlink:before,
|
||||
.fi-unlock:before,
|
||||
.fi-upload-cloud:before,
|
||||
.fi-upload:before,
|
||||
.fi-usb:before,
|
||||
.fi-video:before,
|
||||
.fi-volume-none:before,
|
||||
.fi-volume-strike:before,
|
||||
.fi-volume:before,
|
||||
.fi-web:before,
|
||||
.fi-wheelchair:before,
|
||||
.fi-widget:before,
|
||||
.fi-wrench:before,
|
||||
.fi-x-circle:before,
|
||||
.fi-x:before,
|
||||
.fi-yen:before,
|
||||
.fi-zoom-in:before,
|
||||
.fi-zoom-out:before {
|
||||
font-family: "foundation-icons";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
.fi-address-book:before { content: "\f100"; }
|
||||
.fi-alert:before { content: "\f101"; }
|
||||
.fi-align-center:before { content: "\f102"; }
|
||||
.fi-align-justify:before { content: "\f103"; }
|
||||
.fi-align-left:before { content: "\f104"; }
|
||||
.fi-align-right:before { content: "\f105"; }
|
||||
.fi-anchor:before { content: "\f106"; }
|
||||
.fi-annotate:before { content: "\f107"; }
|
||||
.fi-archive:before { content: "\f108"; }
|
||||
.fi-arrow-down:before { content: "\f109"; }
|
||||
.fi-arrow-left:before { content: "\f10a"; }
|
||||
.fi-arrow-right:before { content: "\f10b"; }
|
||||
.fi-arrow-up:before { content: "\f10c"; }
|
||||
.fi-arrows-compress:before { content: "\f10d"; }
|
||||
.fi-arrows-expand:before { content: "\f10e"; }
|
||||
.fi-arrows-in:before { content: "\f10f"; }
|
||||
.fi-arrows-out:before { content: "\f110"; }
|
||||
.fi-asl:before { content: "\f111"; }
|
||||
.fi-asterisk:before { content: "\f112"; }
|
||||
.fi-at-sign:before { content: "\f113"; }
|
||||
.fi-background-color:before { content: "\f114"; }
|
||||
.fi-battery-empty:before { content: "\f115"; }
|
||||
.fi-battery-full:before { content: "\f116"; }
|
||||
.fi-battery-half:before { content: "\f117"; }
|
||||
.fi-bitcoin-circle:before { content: "\f118"; }
|
||||
.fi-bitcoin:before { content: "\f119"; }
|
||||
.fi-blind:before { content: "\f11a"; }
|
||||
.fi-bluetooth:before { content: "\f11b"; }
|
||||
.fi-bold:before { content: "\f11c"; }
|
||||
.fi-book-bookmark:before { content: "\f11d"; }
|
||||
.fi-book:before { content: "\f11e"; }
|
||||
.fi-bookmark:before { content: "\f11f"; }
|
||||
.fi-braille:before { content: "\f120"; }
|
||||
.fi-burst-new:before { content: "\f121"; }
|
||||
.fi-burst-sale:before { content: "\f122"; }
|
||||
.fi-burst:before { content: "\f123"; }
|
||||
.fi-calendar:before { content: "\f124"; }
|
||||
.fi-camera:before { content: "\f125"; }
|
||||
.fi-check:before { content: "\f126"; }
|
||||
.fi-checkbox:before { content: "\f127"; }
|
||||
.fi-clipboard-notes:before { content: "\f128"; }
|
||||
.fi-clipboard-pencil:before { content: "\f129"; }
|
||||
.fi-clipboard:before { content: "\f12a"; }
|
||||
.fi-clock:before { content: "\f12b"; }
|
||||
.fi-closed-caption:before { content: "\f12c"; }
|
||||
.fi-cloud:before { content: "\f12d"; }
|
||||
.fi-comment-minus:before { content: "\f12e"; }
|
||||
.fi-comment-quotes:before { content: "\f12f"; }
|
||||
.fi-comment-video:before { content: "\f130"; }
|
||||
.fi-comment:before { content: "\f131"; }
|
||||
.fi-comments:before { content: "\f132"; }
|
||||
.fi-compass:before { content: "\f133"; }
|
||||
.fi-contrast:before { content: "\f134"; }
|
||||
.fi-credit-card:before { content: "\f135"; }
|
||||
.fi-crop:before { content: "\f136"; }
|
||||
.fi-crown:before { content: "\f137"; }
|
||||
.fi-css3:before { content: "\f138"; }
|
||||
.fi-database:before { content: "\f139"; }
|
||||
.fi-die-five:before { content: "\f13a"; }
|
||||
.fi-die-four:before { content: "\f13b"; }
|
||||
.fi-die-one:before { content: "\f13c"; }
|
||||
.fi-die-six:before { content: "\f13d"; }
|
||||
.fi-die-three:before { content: "\f13e"; }
|
||||
.fi-die-two:before { content: "\f13f"; }
|
||||
.fi-dislike:before { content: "\f140"; }
|
||||
.fi-dollar-bill:before { content: "\f141"; }
|
||||
.fi-dollar:before { content: "\f142"; }
|
||||
.fi-download:before { content: "\f143"; }
|
||||
.fi-eject:before { content: "\f144"; }
|
||||
.fi-elevator:before { content: "\f145"; }
|
||||
.fi-euro:before { content: "\f146"; }
|
||||
.fi-eye:before { content: "\f147"; }
|
||||
.fi-fast-forward:before { content: "\f148"; }
|
||||
.fi-female-symbol:before { content: "\f149"; }
|
||||
.fi-female:before { content: "\f14a"; }
|
||||
.fi-filter:before { content: "\f14b"; }
|
||||
.fi-first-aid:before { content: "\f14c"; }
|
||||
.fi-flag:before { content: "\f14d"; }
|
||||
.fi-folder-add:before { content: "\f14e"; }
|
||||
.fi-folder-lock:before { content: "\f14f"; }
|
||||
.fi-folder:before { content: "\f150"; }
|
||||
.fi-foot:before { content: "\f151"; }
|
||||
.fi-foundation:before { content: "\f152"; }
|
||||
.fi-graph-bar:before { content: "\f153"; }
|
||||
.fi-graph-horizontal:before { content: "\f154"; }
|
||||
.fi-graph-pie:before { content: "\f155"; }
|
||||
.fi-graph-trend:before { content: "\f156"; }
|
||||
.fi-guide-dog:before { content: "\f157"; }
|
||||
.fi-hearing-aid:before { content: "\f158"; }
|
||||
.fi-heart:before { content: "\f159"; }
|
||||
.fi-home:before { content: "\f15a"; }
|
||||
.fi-html5:before { content: "\f15b"; }
|
||||
.fi-indent-less:before { content: "\f15c"; }
|
||||
.fi-indent-more:before { content: "\f15d"; }
|
||||
.fi-info:before { content: "\f15e"; }
|
||||
.fi-italic:before { content: "\f15f"; }
|
||||
.fi-key:before { content: "\f160"; }
|
||||
.fi-laptop:before { content: "\f161"; }
|
||||
.fi-layout:before { content: "\f162"; }
|
||||
.fi-lightbulb:before { content: "\f163"; }
|
||||
.fi-like:before { content: "\f164"; }
|
||||
.fi-link:before { content: "\f165"; }
|
||||
.fi-list-bullet:before { content: "\f166"; }
|
||||
.fi-list-number:before { content: "\f167"; }
|
||||
.fi-list-thumbnails:before { content: "\f168"; }
|
||||
.fi-list:before { content: "\f169"; }
|
||||
.fi-lock:before { content: "\f16a"; }
|
||||
.fi-loop:before { content: "\f16b"; }
|
||||
.fi-magnifying-glass:before { content: "\f16c"; }
|
||||
.fi-mail:before { content: "\f16d"; }
|
||||
.fi-male-female:before { content: "\f16e"; }
|
||||
.fi-male-symbol:before { content: "\f16f"; }
|
||||
.fi-male:before { content: "\f170"; }
|
||||
.fi-map:before { content: "\f171"; }
|
||||
.fi-marker:before { content: "\f172"; }
|
||||
.fi-megaphone:before { content: "\f173"; }
|
||||
.fi-microphone:before { content: "\f174"; }
|
||||
.fi-minus-circle:before { content: "\f175"; }
|
||||
.fi-minus:before { content: "\f176"; }
|
||||
.fi-mobile-signal:before { content: "\f177"; }
|
||||
.fi-mobile:before { content: "\f178"; }
|
||||
.fi-monitor:before { content: "\f179"; }
|
||||
.fi-mountains:before { content: "\f17a"; }
|
||||
.fi-music:before { content: "\f17b"; }
|
||||
.fi-next:before { content: "\f17c"; }
|
||||
.fi-no-dogs:before { content: "\f17d"; }
|
||||
.fi-no-smoking:before { content: "\f17e"; }
|
||||
.fi-page-add:before { content: "\f17f"; }
|
||||
.fi-page-copy:before { content: "\f180"; }
|
||||
.fi-page-csv:before { content: "\f181"; }
|
||||
.fi-page-delete:before { content: "\f182"; }
|
||||
.fi-page-doc:before { content: "\f183"; }
|
||||
.fi-page-edit:before { content: "\f184"; }
|
||||
.fi-page-export-csv:before { content: "\f185"; }
|
||||
.fi-page-export-doc:before { content: "\f186"; }
|
||||
.fi-page-export-pdf:before { content: "\f187"; }
|
||||
.fi-page-export:before { content: "\f188"; }
|
||||
.fi-page-filled:before { content: "\f189"; }
|
||||
.fi-page-multiple:before { content: "\f18a"; }
|
||||
.fi-page-pdf:before { content: "\f18b"; }
|
||||
.fi-page-remove:before { content: "\f18c"; }
|
||||
.fi-page-search:before { content: "\f18d"; }
|
||||
.fi-page:before { content: "\f18e"; }
|
||||
.fi-paint-bucket:before { content: "\f18f"; }
|
||||
.fi-paperclip:before { content: "\f190"; }
|
||||
.fi-pause:before { content: "\f191"; }
|
||||
.fi-paw:before { content: "\f192"; }
|
||||
.fi-paypal:before { content: "\f193"; }
|
||||
.fi-pencil:before { content: "\f194"; }
|
||||
.fi-photo:before { content: "\f195"; }
|
||||
.fi-play-circle:before { content: "\f196"; }
|
||||
.fi-play-video:before { content: "\f197"; }
|
||||
.fi-play:before { content: "\f198"; }
|
||||
.fi-plus:before { content: "\f199"; }
|
||||
.fi-pound:before { content: "\f19a"; }
|
||||
.fi-power:before { content: "\f19b"; }
|
||||
.fi-previous:before { content: "\f19c"; }
|
||||
.fi-price-tag:before { content: "\f19d"; }
|
||||
.fi-pricetag-multiple:before { content: "\f19e"; }
|
||||
.fi-print:before { content: "\f19f"; }
|
||||
.fi-prohibited:before { content: "\f1a0"; }
|
||||
.fi-projection-screen:before { content: "\f1a1"; }
|
||||
.fi-puzzle:before { content: "\f1a2"; }
|
||||
.fi-quote:before { content: "\f1a3"; }
|
||||
.fi-record:before { content: "\f1a4"; }
|
||||
.fi-refresh:before { content: "\f1a5"; }
|
||||
.fi-results-demographics:before { content: "\f1a6"; }
|
||||
.fi-results:before { content: "\f1a7"; }
|
||||
.fi-rewind-ten:before { content: "\f1a8"; }
|
||||
.fi-rewind:before { content: "\f1a9"; }
|
||||
.fi-rss:before { content: "\f1aa"; }
|
||||
.fi-safety-cone:before { content: "\f1ab"; }
|
||||
.fi-save:before { content: "\f1ac"; }
|
||||
.fi-share:before { content: "\f1ad"; }
|
||||
.fi-sheriff-badge:before { content: "\f1ae"; }
|
||||
.fi-shield:before { content: "\f1af"; }
|
||||
.fi-shopping-bag:before { content: "\f1b0"; }
|
||||
.fi-shopping-cart:before { content: "\f1b1"; }
|
||||
.fi-shuffle:before { content: "\f1b2"; }
|
||||
.fi-skull:before { content: "\f1b3"; }
|
||||
.fi-social-500px:before { content: "\f1b4"; }
|
||||
.fi-social-adobe:before { content: "\f1b5"; }
|
||||
.fi-social-amazon:before { content: "\f1b6"; }
|
||||
.fi-social-android:before { content: "\f1b7"; }
|
||||
.fi-social-apple:before { content: "\f1b8"; }
|
||||
.fi-social-behance:before { content: "\f1b9"; }
|
||||
.fi-social-bing:before { content: "\f1ba"; }
|
||||
.fi-social-blogger:before { content: "\f1bb"; }
|
||||
.fi-social-delicious:before { content: "\f1bc"; }
|
||||
.fi-social-designer-news:before { content: "\f1bd"; }
|
||||
.fi-social-deviant-art:before { content: "\f1be"; }
|
||||
.fi-social-digg:before { content: "\f1bf"; }
|
||||
.fi-social-dribbble:before { content: "\f1c0"; }
|
||||
.fi-social-drive:before { content: "\f1c1"; }
|
||||
.fi-social-dropbox:before { content: "\f1c2"; }
|
||||
.fi-social-evernote:before { content: "\f1c3"; }
|
||||
.fi-social-facebook:before { content: "\f1c4"; }
|
||||
.fi-social-flickr:before { content: "\f1c5"; }
|
||||
.fi-social-forrst:before { content: "\f1c6"; }
|
||||
.fi-social-foursquare:before { content: "\f1c7"; }
|
||||
.fi-social-game-center:before { content: "\f1c8"; }
|
||||
.fi-social-github:before { content: "\f1c9"; }
|
||||
.fi-social-google-plus:before { content: "\f1ca"; }
|
||||
.fi-social-hacker-news:before { content: "\f1cb"; }
|
||||
.fi-social-hi5:before { content: "\f1cc"; }
|
||||
.fi-social-instagram:before { content: "\f1cd"; }
|
||||
.fi-social-joomla:before { content: "\f1ce"; }
|
||||
.fi-social-lastfm:before { content: "\f1cf"; }
|
||||
.fi-social-linkedin:before { content: "\f1d0"; }
|
||||
.fi-social-medium:before { content: "\f1d1"; }
|
||||
.fi-social-myspace:before { content: "\f1d2"; }
|
||||
.fi-social-orkut:before { content: "\f1d3"; }
|
||||
.fi-social-path:before { content: "\f1d4"; }
|
||||
.fi-social-picasa:before { content: "\f1d5"; }
|
||||
.fi-social-pinterest:before { content: "\f1d6"; }
|
||||
.fi-social-rdio:before { content: "\f1d7"; }
|
||||
.fi-social-reddit:before { content: "\f1d8"; }
|
||||
.fi-social-skillshare:before { content: "\f1d9"; }
|
||||
.fi-social-skype:before { content: "\f1da"; }
|
||||
.fi-social-smashing-mag:before { content: "\f1db"; }
|
||||
.fi-social-snapchat:before { content: "\f1dc"; }
|
||||
.fi-social-spotify:before { content: "\f1dd"; }
|
||||
.fi-social-squidoo:before { content: "\f1de"; }
|
||||
.fi-social-stack-overflow:before { content: "\f1df"; }
|
||||
.fi-social-steam:before { content: "\f1e0"; }
|
||||
.fi-social-stumbleupon:before { content: "\f1e1"; }
|
||||
.fi-social-treehouse:before { content: "\f1e2"; }
|
||||
.fi-social-tumblr:before { content: "\f1e3"; }
|
||||
.fi-social-twitter:before { content: "\f1e4"; }
|
||||
.fi-social-vimeo:before { content: "\f1e5"; }
|
||||
.fi-social-windows:before { content: "\f1e6"; }
|
||||
.fi-social-xbox:before { content: "\f1e7"; }
|
||||
.fi-social-yahoo:before { content: "\f1e8"; }
|
||||
.fi-social-yelp:before { content: "\f1e9"; }
|
||||
.fi-social-youtube:before { content: "\f1ea"; }
|
||||
.fi-social-zerply:before { content: "\f1eb"; }
|
||||
.fi-social-zurb:before { content: "\f1ec"; }
|
||||
.fi-sound:before { content: "\f1ed"; }
|
||||
.fi-star:before { content: "\f1ee"; }
|
||||
.fi-stop:before { content: "\f1ef"; }
|
||||
.fi-strikethrough:before { content: "\f1f0"; }
|
||||
.fi-subscript:before { content: "\f1f1"; }
|
||||
.fi-superscript:before { content: "\f1f2"; }
|
||||
.fi-tablet-landscape:before { content: "\f1f3"; }
|
||||
.fi-tablet-portrait:before { content: "\f1f4"; }
|
||||
.fi-target-two:before { content: "\f1f5"; }
|
||||
.fi-target:before { content: "\f1f6"; }
|
||||
.fi-telephone-accessible:before { content: "\f1f7"; }
|
||||
.fi-telephone:before { content: "\f1f8"; }
|
||||
.fi-text-color:before { content: "\f1f9"; }
|
||||
.fi-thumbnails:before { content: "\f1fa"; }
|
||||
.fi-ticket:before { content: "\f1fb"; }
|
||||
.fi-torso-business:before { content: "\f1fc"; }
|
||||
.fi-torso-female:before { content: "\f1fd"; }
|
||||
.fi-torso:before { content: "\f1fe"; }
|
||||
.fi-torsos-all-female:before { content: "\f1ff"; }
|
||||
.fi-torsos-all:before { content: "\f200"; }
|
||||
.fi-torsos-female-male:before { content: "\f201"; }
|
||||
.fi-torsos-male-female:before { content: "\f202"; }
|
||||
.fi-torsos:before { content: "\f203"; }
|
||||
.fi-trash:before { content: "\f204"; }
|
||||
.fi-trees:before { content: "\f205"; }
|
||||
.fi-trophy:before { content: "\f206"; }
|
||||
.fi-underline:before { content: "\f207"; }
|
||||
.fi-universal-access:before { content: "\f208"; }
|
||||
.fi-unlink:before { content: "\f209"; }
|
||||
.fi-unlock:before { content: "\f20a"; }
|
||||
.fi-upload-cloud:before { content: "\f20b"; }
|
||||
.fi-upload:before { content: "\f20c"; }
|
||||
.fi-usb:before { content: "\f20d"; }
|
||||
.fi-video:before { content: "\f20e"; }
|
||||
.fi-volume-none:before { content: "\f20f"; }
|
||||
.fi-volume-strike:before { content: "\f210"; }
|
||||
.fi-volume:before { content: "\f211"; }
|
||||
.fi-web:before { content: "\f212"; }
|
||||
.fi-wheelchair:before { content: "\f213"; }
|
||||
.fi-widget:before { content: "\f214"; }
|
||||
.fi-wrench:before { content: "\f215"; }
|
||||
.fi-x-circle:before { content: "\f216"; }
|
||||
.fi-x:before { content: "\f217"; }
|
||||
.fi-yen:before { content: "\f218"; }
|
||||
.fi-zoom-in:before { content: "\f219"; }
|
||||
.fi-zoom-out:before { content: "\f21a"; }
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -1,181 +0,0 @@
|
|||
.head .menu a.dropdown:hover,
|
||||
.head .menu a.dropdown.hover {
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
.head .menu ul.hover {
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
.head .menu ul li a:hover {
|
||||
background-color: #F8F8FB;
|
||||
color: #2C3E50;
|
||||
}
|
||||
|
||||
.col3 a.selected:hover {
|
||||
background-color: #213140;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.col3 a:hover {
|
||||
background-color: #16A085;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #2980b9;
|
||||
}
|
||||
|
||||
a.button-setup.add-wallet:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
a.button-setup:hover {
|
||||
color: #666;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
.createProfile .button-setup a:hover, .home .button-setup a:hover, .settings .button-setup a:hover, .import-profile .button-setup a:hover {
|
||||
background: #3C4E60;
|
||||
}
|
||||
|
||||
.name-wallet i:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
ul.pagination li.current a:hover, ul.pagination li.current a:focus {
|
||||
background: #16A085;
|
||||
}
|
||||
|
||||
.button.outline.light-gray:hover {
|
||||
background: rgba(206,213,220,0.40);
|
||||
color: #4B6178;
|
||||
}
|
||||
|
||||
.button.outline.dark-gray:hover {
|
||||
background: rgba(255,255,255,.1);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.secondary:hover,
|
||||
button.secondary:focus,
|
||||
.button.secondary:hover,
|
||||
.button.secondary:focus {
|
||||
background-color: #2980B9;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.primary:hover,
|
||||
button.primary:focus,
|
||||
.button.primary:hover,
|
||||
.button.primary:focus {
|
||||
background-color: #16A085;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.warning:hover,
|
||||
button.warning:focus,
|
||||
.button.warning:hover,
|
||||
.button.warning:focus {
|
||||
background-color: #82251A;
|
||||
color: #e6e6e6;
|
||||
}
|
||||
|
||||
button.white:hover,
|
||||
button.white:focus,
|
||||
.button.white:hover,
|
||||
.button.white:focus {
|
||||
background-color: #E0E0E0;
|
||||
color: #2C3E50;
|
||||
}
|
||||
|
||||
button.black:hover,
|
||||
button.black:focus,
|
||||
.button.black:hover,
|
||||
.button.black:focus {
|
||||
background-color: #213140;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button.gray:hover,
|
||||
button.gray:focus,
|
||||
.button.gray:hover,
|
||||
.button.gray:focus {
|
||||
background-color: #E0E5E5;
|
||||
color: #2C3E50;
|
||||
}
|
||||
|
||||
.wallet-selection.wallets li:hover {
|
||||
background-color: #3C4E60;
|
||||
}
|
||||
|
||||
.wallet-selection.wallets a.wallet-item:hover,
|
||||
.side-nav.wallets a.wallet-item:hover {
|
||||
color: #7A8C9E;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.side-nav li.nav-item:hover {
|
||||
background-color: #3C4E60;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fi-trash.text-gray:hover {
|
||||
color: #CA5649;
|
||||
}
|
||||
|
||||
a:hover .photo-container {
|
||||
background: #34495E;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.side-nav li.active:hover a {
|
||||
background-color: #F8F8FB;
|
||||
}
|
||||
|
||||
.side-nav li:hover a {
|
||||
background-color: #3C4E60;
|
||||
}
|
||||
|
||||
a.text-gray:hover {color: #2C3E50;}
|
||||
a.text-black:hover {color: #213140;}
|
||||
a.text-primary:hover {color: #50E3C2;}
|
||||
a.text-secondary:hover {color: #4A90E2;}
|
||||
a.text-white:hover {color: #ccc;}
|
||||
a.text-warning:hover {color: #FD7262;}
|
||||
|
||||
.w-popup-menu li:hover{
|
||||
background-color: #34B191;
|
||||
}
|
||||
.w-popup-menu li:hover .w-popup-icon {
|
||||
color: transparent;
|
||||
}
|
||||
.w-popup-menu li:hover .w-popup-main, .w-popup-menu li:hover .w-popup-sub {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.createProfile a.text-gray:hover,
|
||||
.home a.text-gray:hover,
|
||||
.import-profile a.text-gray:hover,
|
||||
.settings a.text-gray:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tabs dd>a:hover {
|
||||
background: #1C2B39;
|
||||
}
|
||||
|
||||
.tabs dd.active a:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.reveal-modal .close-reveal-modal:hover, dialog .close-reveal-modal:hover {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
.reveal-modal .close-reveal-modal:hover, dialog .close-reveal-modal:hover {
|
||||
background-color: #7A8C9E;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
|
@ -1,532 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* Copay mobile CSS
|
||||
*
|
||||
*/
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
|
||||
/* disabling text selection */
|
||||
body {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
input {
|
||||
-webkit-user-select: auto !important;
|
||||
-khtml-user-select: auto !important;
|
||||
-moz-user-select: auto !important;
|
||||
-ms-user-select: auto !important;
|
||||
user-select: auto !important;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-ms-user-drag: none;
|
||||
user-drag: none;
|
||||
}
|
||||
/* END disabling text selection */
|
||||
|
||||
.inner-wrap {
|
||||
-webkit-transition-duration: 250ms;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
-ms-content-zooming: none;
|
||||
}
|
||||
|
||||
.loading-screen {
|
||||
background-color: #2C3E50;
|
||||
}
|
||||
|
||||
.logo-setup {
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page, .main, .setup-page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-left: 0;
|
||||
padding: 60px 0 80px 0;
|
||||
}
|
||||
|
||||
/* Fix IE 10 */
|
||||
.extra-margin-bottom {
|
||||
display: block
|
||||
height: 75px
|
||||
overflow: hidden
|
||||
clear: both
|
||||
}
|
||||
|
||||
.main {
|
||||
height: 99%
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
/* Fix IE 11 */
|
||||
_:-ms-fullscreen, :root .extra-margin-bottom {
|
||||
display: block;
|
||||
height: 75px;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
_:-ms-fullscreen, :root .main {
|
||||
height: 99%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Fix Firefox */
|
||||
@-moz-document url-prefix() {
|
||||
.extra-margin-bottom {
|
||||
display: block;
|
||||
height: 75px;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 5;
|
||||
background: #2C3E50;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 5;
|
||||
background: #2C3E50;
|
||||
}
|
||||
|
||||
.home, .settings, .createProfile, .import-profile {
|
||||
overflow: hidden;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.modal-mobile {
|
||||
padding: 20px .5rem;
|
||||
}
|
||||
|
||||
.reveal-modal {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.reveal-modal .close-reveal-modal, dialog .close-reveal-modal {
|
||||
padding: 0.8rem;
|
||||
position: relative;
|
||||
background: #E4E8EC;
|
||||
top: 0;
|
||||
right: 0;
|
||||
line-height: 0;
|
||||
color: #7A8C9E;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: #2C3E50;
|
||||
height: 120px;
|
||||
width: 100%;
|
||||
line-height: 110%;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
header .photo-container {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.amount {
|
||||
background-color: #F1F3F5;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 2.4rem 1rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.scroll-section {
|
||||
position: absolute;
|
||||
top: 120px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.col2 {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.col2 a.wallet-item {
|
||||
padding: 0.15rem 0 0.3rem;
|
||||
}
|
||||
|
||||
.col3 a {
|
||||
height: 45px;
|
||||
padding: 12px 5px;
|
||||
}
|
||||
|
||||
.status {
|
||||
bottom: 65px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.right-off-canvas-menu {
|
||||
background-color: #213140 ;
|
||||
}
|
||||
|
||||
.left-off-canvas-menu {
|
||||
background: #E4E8EC;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.off-canvas-wrap,.inner-wrap{
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.home-wallet .avatar-wallet {
|
||||
padding: 1.7rem;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
position: absolute;
|
||||
top: -43px;
|
||||
font-size: 35px;
|
||||
border: 3px solid #fff;
|
||||
}
|
||||
|
||||
.home-wallet .wallet-info {
|
||||
margin-left: 100px;
|
||||
}
|
||||
|
||||
.copayers {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
overflow-y: none;
|
||||
}
|
||||
|
||||
ul.copayer-list img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.hide-tab-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-bar h1 {
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
ul.off-canvas-list li a {
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
font-weight: 100;
|
||||
border-bottom: transparent;
|
||||
padding: 0.66667rem 1rem;
|
||||
color: #7A8C9E;
|
||||
border-top: 1px solid rgba(255,255,255,0.25);
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
ul.off-canvas-list li a i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
ul.off-canvas-list li a.wallet-item {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
ul.off-canvas-list li a i {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.buttons-sidebar {
|
||||
color: #4B6178;
|
||||
}
|
||||
|
||||
.buttons-sidebar .col1 i {
|
||||
background-color: #fff;
|
||||
color: #4B6178;
|
||||
}
|
||||
|
||||
.bottombar-item a {
|
||||
color: #7A8C9E;
|
||||
padding: 0.5rem 0;
|
||||
display: block;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.bottombar-item a.active {
|
||||
color: #E4E8EC;
|
||||
background-color: #213140;
|
||||
border-bottom: 3px solid #1ABC9C;
|
||||
}
|
||||
|
||||
.box-founds {
|
||||
background-color: #213140;
|
||||
}
|
||||
|
||||
.left-small {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.right-small {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.right-small a, .left-small a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.panel.last-transactions {
|
||||
margin-bottom: 3.25rem;
|
||||
}
|
||||
|
||||
.addresses .list-addr contact {
|
||||
margin-left: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
a.missing-copayers {
|
||||
bottom: -34px;
|
||||
text-align: center;
|
||||
padding: .7rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button, button {
|
||||
padding: 1rem 1.2rem 1.0625rem;
|
||||
}
|
||||
|
||||
.box-setup {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.footer-setup {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 80%;
|
||||
padding: 0.15rem 0.2rem;
|
||||
}
|
||||
|
||||
.sidebar-mobile-wallets {
|
||||
background: #E4E8EC;
|
||||
}
|
||||
|
||||
.sidebar-mobile-wallets .avatar-wallet {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.sidebar-mobile-wallets li {
|
||||
overflow: hidden;
|
||||
padding:.6rem 0.4rem;
|
||||
border-bottom: 1px solid #DDE2E7;
|
||||
}
|
||||
|
||||
.sidebar-mobile-wallets li.nav-item.selected {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all vendors hover / shadow / fade
|
||||
*/
|
||||
|
||||
.tooltip {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.move-right .exit-off-canvas,
|
||||
.move-left .exit-off-canvas {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
ul.off-canvas-list li a:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.side-nav li a:not(.button) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a, button, .button, input, textarea, select, .reveal-modal {
|
||||
-webkit-box-shadow: none !important;
|
||||
-moz-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
-webkit-tap-highlight-color:rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.modal.fade {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.modal.fade .modal-dialog, .modal.in .modal-dialog, .reveal-modal-bg {
|
||||
-webkit-transition: none !important;
|
||||
transition: none !important;
|
||||
-webkit-transform: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.move-right .close-menu {
|
||||
cursor: pointer;
|
||||
box-shadow: none;
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: rgba(255,255,255,.2);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1002;
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
.pin {
|
||||
margin: 0 auto;
|
||||
width: 35%;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pin-box {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
width: 25%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pin-numbers {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
height: 72%;
|
||||
margin-bottom: 1.5%;
|
||||
}
|
||||
|
||||
.pin-button-bar {
|
||||
height: 25%;
|
||||
}
|
||||
|
||||
a.pin-button {
|
||||
margin: 2.5%;
|
||||
display: inline-block;
|
||||
color: #CED5DC;
|
||||
font-size: 210%;
|
||||
font-weight: 100;
|
||||
border: 1px solid #4B6178;
|
||||
border-radius: 100%;
|
||||
width: 70px;
|
||||
padding-top: 1rem;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
a.pin-button:active {
|
||||
color: #fff;
|
||||
background-color: #3E5367;
|
||||
}
|
||||
|
||||
.icon-circle, .icon-circle-active {
|
||||
color: #1ABC9C;
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
|
||||
.tab-bar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.modal-mobile {
|
||||
padding: 60px 0.5rem;
|
||||
}
|
||||
|
||||
.tx-comment {
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.reveal-modal {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.reveal-modal-bg {
|
||||
position: absolute;
|
||||
height: 0;
|
||||
width: 0;
|
||||
background: none;
|
||||
display: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* notifications */
|
||||
|
||||
.dr-notification {
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border-bottom: 1.5px solid #D6DBE0;
|
||||
}
|
||||
|
||||
.dr-notification-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dr-notification-container.top {
|
||||
top: 2.813rem;
|
||||
}
|
||||
|
||||
.dr-notification-wrapper {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dr-notification-close-btn {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.dr-notification-image {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.dr-notification-text {
|
||||
font-size: 12px;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
}
|
||||
|
Binary file not shown.
|
@ -1,970 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2013-8-23: Created.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
Created by FontForge 20120731 at Fri Aug 23 09:25:55 2013
|
||||
By Jordan Humphreys
|
||||
Created by Jordan Humphreys with FontForge 2.0 (http://fontforge.sf.net)
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="fontcustom" horiz-adv-x="369" >
|
||||
<font-face
|
||||
font-family="fontcustom"
|
||||
font-weight="500"
|
||||
font-stretch="normal"
|
||||
units-per-em="512"
|
||||
panose-1="2 0 6 3 0 0 0 0 0 0"
|
||||
ascent="448"
|
||||
descent="-64"
|
||||
bbox="-0.584459 -64.25 512.25 448.25"
|
||||
underline-thickness="25.6"
|
||||
underline-position="-51.2"
|
||||
unicode-range="U+F100-F21A"
|
||||
/>
|
||||
<missing-glyph />
|
||||
<glyph glyph-name="uniF100" unicode="" horiz-adv-x="340"
|
||||
d="M330 287v0h-24v-30h24c6 0 10 -4 10 -10v-30c0 -6 -4 -10 -10 -10h-24v-29h24c6 0 10 -5 10 -11v-30c0 -6 -4 -10 -10 -10h-24v-29h24c6 0 10 -5 10 -11v-30c0 -6 -4 -10 -10 -10h-24v-9c0 -9 -7 -16 -16 -16h-274c-9 0 -16 7 -16 16v308c0 9 7 16 16 16h274
|
||||
c9 0 16 -7 16 -16v-9h24c6 0 10 -4 10 -10v-30c0 -6 -4 -10 -10 -10zM234 122v0v44c0 4 -3 8 -6 10l-55 26c13 8 21 24 21 42c0 26 -18 48 -41 48s-41 -22 -41 -48c0 -19 9 -34 22 -42l-56 -26c-3 -2 -5 -6 -5 -10v-44c0 -6 4 -11 9 -11h142c5 0 10 5 10 11z" />
|
||||
<glyph glyph-name="uniF101" unicode="" horiz-adv-x="425"
|
||||
d="M423 31c1 -2 2 -4 2 -7c0 -10 -9 -18 -19 -18v0h-388v0c-10 0 -18 8 -18 18c0 4 1 9 4 12v0l191 331h1c3 6 8 11 16 11c7 0 13 -3 16 -9l193 -334c0 -1 1 -1 1 -2l1 -2v0zM213 40c14 0 26 12 26 27s-12 26 -26 26c-15 0 -27 -11 -27 -26s12 -27 27 -27zM239 273v0v0v0v0
|
||||
c0 6 -5 10 -11 10v0h-32v0c-5 0 -10 -4 -10 -10v-1v-144v0v0c0 -6 5 -11 11 -11h31v0v0c6 0 11 5 11 11v0v0v145z" />
|
||||
<glyph glyph-name="uniF102" unicode="" horiz-adv-x="355"
|
||||
d="M337 370c10 0 18 -7 18 -17v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM312 235c0 -10 -8 -17 -18 -17v0h-233c-10 0 -18 7 -18 17v16c0 10 8 17 18 17h233v0c10 0 18 -7 18 -17v0v-16v0zM320 64c10 0 17 -7 17 -17v0v-16v0
|
||||
c0 -10 -7 -17 -17 -17v0h-285c-10 0 -18 7 -18 17v16c0 10 8 17 18 17h285v0zM294 149v0v-16v0c0 -10 -7 -17 -17 -17v0v0h-199c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h199v0v0c10 0 17 -7 17 -17z" />
|
||||
<glyph glyph-name="uniF103" unicode="" horiz-adv-x="355"
|
||||
d="M337 369c10 0 18 -7 18 -17v-16c0 -10 -8 -18 -18 -18h-320c-10 0 -17 8 -17 18v16c0 10 7 17 17 17h320zM337 268c10 0 18 -7 18 -17v-16c0 -10 -8 -18 -18 -18h-320c-10 0 -17 8 -17 18v16c0 10 7 17 17 17h320zM337 167c10 0 18 -8 18 -18v-16c0 -10 -8 -17 -18 -17
|
||||
h-320c-10 0 -17 7 -17 17v16c0 10 7 18 17 18h320zM337 66c10 0 18 -8 18 -18v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 18 17 18h320z" />
|
||||
<glyph glyph-name="uniF104" unicode="" horiz-adv-x="355"
|
||||
d="M337 370c10 0 18 -7 18 -17v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM17 218c-10 0 -17 7 -17 17v16c0 10 7 18 17 18h234v0c10 0 18 -8 18 -18v0v-16v0c0 -10 -8 -17 -18 -17v0h-234zM302 64c10 0 18 -7 18 -17v0v-16v0
|
||||
c0 -10 -8 -17 -18 -17v0h-285c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h285v0zM17 116c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h199v0v0c10 0 18 -7 18 -17v0v-16v0c0 -10 -8 -17 -18 -17v0v0h-199z" />
|
||||
<glyph glyph-name="uniF105" unicode="" horiz-adv-x="355"
|
||||
d="M337 370c10 0 18 -7 18 -17v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM337 268c10 0 18 -7 18 -17v0v-16v0c0 -10 -8 -17 -18 -17v0h-233c-10 0 -18 7 -18 17v16c0 10 8 17 18 17h233v0zM337 64c10 0 18 -7 18 -17v0v-16v0
|
||||
c0 -10 -8 -17 -18 -17v0h-285c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h285v0v0zM355 149v0v-16v0c0 -10 -8 -17 -18 -17v0v0h-199c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h199v0v0c10 0 18 -7 18 -17z" />
|
||||
<glyph glyph-name="uniF106" unicode="" horiz-adv-x="393"
|
||||
d="M392 104c1 -1 1 -3 1 -4c0 -4 -4 -8 -8 -8h-21c-33 -58 -95 -97 -167 -97s-134 39 -167 97h-22c-4 0 -8 4 -8 8c0 2 1 4 2 5l47 82c1 3 4 5 7 5s6 -2 7 -4v0l48 -84v0c1 -1 2 -3 2 -4c0 -4 -5 -8 -9 -8h-12c20 -22 48 -38 79 -44v150h-46c-4 0 -8 4 -8 8v0v35v0
|
||||
c0 4 4 8 8 8v0h46v27c-20 10 -34 30 -34 54c0 33 27 59 60 59s59 -26 59 -59c0 -24 -14 -44 -34 -54v-27h47v0c4 0 8 -4 8 -8v0v-35v0c0 -4 -4 -8 -8 -8h-47v-150c31 6 59 22 79 44h-12c-4 0 -8 4 -8 8c0 2 1 4 2 5l46 82c1 3 5 5 8 5s6 -2 7 -4v0l48 -84v0zM197 296
|
||||
c18 0 33 16 33 34s-15 33 -33 33s-34 -15 -34 -33s16 -34 34 -34z" />
|
||||
<glyph glyph-name="uniF107" unicode="" horiz-adv-x="388"
|
||||
d="M360 98c17 -9 28 -27 28 -47c0 -29 -24 -54 -53 -54c-22 0 -42 14 -50 34h-182c-8 -20 -28 -34 -50 -34c-29 0 -53 25 -53 54c0 20 11 38 28 47v188c-17 9 -28 27 -28 47c0 29 24 54 53 54c18 0 34 -10 44 -24h194c10 14 26 24 44 24c29 0 53 -25 53 -54
|
||||
c0 -20 -11 -38 -28 -47v-188zM79 98c7 -4 13 -10 18 -16h195c5 6 10 12 17 16v188c-11 6 -18 15 -23 26h-184c-5 -11 -12 -20 -23 -26v-188z" />
|
||||
<glyph glyph-name="uniF108" unicode="" horiz-adv-x="401"
|
||||
d="M401 279v0v-247v0c0 -12 -9 -21 -21 -21h-1h-357h-1c-12 0 -21 9 -21 21v0v247v1c0 5 2 10 5 14v0l42 72v0c2 4 6 7 11 7v0v0h284v0h1c6 0 12 -4 13 -10l40 -69v-1c3 -4 5 -8 5 -13v-1zM269 154c1 2 2 4 1 6s-4 3 -6 3h-30v81c0 3 -3 6 -6 6h-55c-3 0 -6 -3 -6 -6v-81
|
||||
h-30c-2 0 -5 -1 -6 -3s0 -4 1 -6l64 -90c1 -2 3 -2 5 -2v0c2 0 4 0 5 2zM39 301h323l-27 47h-269z" />
|
||||
<glyph glyph-name="uniF109" unicode="" horiz-adv-x="292"
|
||||
d="M2 188c-3 4 -3 9 -1 13s7 7 12 7h62v167c0 7 6 13 13 13h116c7 0 12 -6 12 -13v-167h63c5 0 9 -3 11 -7s2 -9 -1 -13l-132 -187c-2 -3 -7 -5 -11 -5v0c-4 0 -8 2 -10 5z" />
|
||||
<glyph glyph-name="uniF10A" unicode="" horiz-adv-x="393"
|
||||
d="M192 335c4 3 9 3 13 1s7 -6 7 -11v-62h168c7 0 13 -6 13 -13v-116c0 -7 -6 -13 -13 -13h-168v-62c0 -5 -3 -9 -7 -11s-9 -2 -13 1l-187 132c-3 2 -5 7 -5 11v0c0 4 2 8 5 10z" />
|
||||
<glyph glyph-name="uniF10B" unicode="" horiz-adv-x="393"
|
||||
d="M201 49c-4 -3 -10 -3 -14 -1s-7 6 -7 11v62h-167c-7 0 -13 6 -13 13v116c0 7 6 13 13 13h167v62c0 5 3 9 7 11s10 2 14 -1l186 -132c3 -2 6 -7 6 -11v0c0 -4 -3 -8 -6 -10z" />
|
||||
<glyph glyph-name="uniF10C" unicode="" horiz-adv-x="292"
|
||||
d="M289 196c3 -4 3 -9 1 -13s-6 -7 -11 -7h-63v-167c0 -7 -5 -13 -12 -13h-116c-7 0 -13 6 -13 13v167h-62c-5 0 -10 3 -12 7s-2 9 1 13l133 187c2 3 6 5 10 5v0c4 0 9 -2 11 -5z" />
|
||||
<glyph glyph-name="uniF10D" unicode="" horiz-adv-x="512"
|
||||
d="M201 -5c-1 -3 -4 -6 -7 -7s-7 0 -9 2l-30 31l-83 -82c-4 -4 -9 -4 -13 0l-56 56c-4 4 -4 9 0 13l82 83l-31 30c-2 2 -3 6 -2 9s4 6 7 7l157 26c3 0 6 0 8 -2v0c2 -2 3 -5 3 -8zM311 389c1 3 4 6 7 7s7 0 9 -2l30 -31l83 82c4 4 9 4 13 0l56 -56c4 -4 4 -9 0 -13l-82 -83
|
||||
l31 -30c2 -2 3 -6 2 -9s-4 -6 -7 -7l-157 -26c-3 0 -6 0 -8 2v0c-2 2 -3 5 -3 8z" />
|
||||
<glyph glyph-name="uniF10E" unicode="" horiz-adv-x="512"
|
||||
d="M26 104c1 3 3 6 6 7s7 0 9 -2l31 -31l83 83c4 4 8 4 12 0l57 -57c4 -4 4 -9 0 -13l-82 -82l30 -31c2 -2 4 -6 3 -9s-4 -5 -7 -6l-158 -27c-3 0 -5 1 -7 3v0c-2 2 -3 4 -3 7zM486 280c-1 -3 -3 -6 -6 -7s-7 0 -9 2l-31 31l-83 -83c-4 -4 -8 -4 -12 0l-57 57
|
||||
c-4 4 -4 9 0 13l82 82l-30 31c-2 2 -4 6 -3 9s4 5 7 6l158 27c3 0 5 -1 7 -3v0c2 -2 3 -4 3 -7z" />
|
||||
<glyph glyph-name="uniF10F" unicode="" horiz-adv-x="512"
|
||||
d="M184 -10c-1 -3 -2 -5 -5 -6s-7 0 -9 2l-28 28l-76 -76c-3 -3 -8 -3 -11 0l-53 53c-3 3 -3 8 0 11l76 76l-28 28c-2 2 -3 6 -2 9s3 4 6 5l145 25c3 0 5 -1 7 -3v0c2 -2 2 -4 2 -7zM328 394c1 3 2 5 5 6s7 0 9 -2l28 -28l76 76c3 3 8 3 11 0l53 -53c3 -3 3 -8 0 -11
|
||||
l-76 -76l28 -28c2 -2 3 -6 2 -9s-3 -4 -6 -5l-145 -25c-3 0 -5 1 -7 3v0c-2 2 -2 4 -2 7zM458 120c3 -1 5 -2 6 -5s0 -7 -2 -9l-28 -28l76 -76c3 -3 3 -8 0 -11l-53 -53c-3 -3 -8 -3 -11 0l-76 76l-28 -28c-2 -2 -6 -3 -9 -2s-4 3 -5 6l-25 145c0 3 1 5 3 7v0c2 2 4 2 7 2z
|
||||
M54 264c-3 1 -5 2 -6 5s0 7 2 9l28 28l-76 76c-3 3 -3 8 0 11l53 53c3 3 8 3 11 0l76 -76l28 28c2 2 6 3 9 2s4 -3 5 -6l25 -145c0 -3 -1 -5 -3 -7v0c-2 -2 -4 -2 -7 -2z" />
|
||||
<glyph glyph-name="uniF110" unicode="" horiz-adv-x="512"
|
||||
d="M24 90c1 3 3 6 6 7s6 0 8 -2l28 -28l76 75c3 3 9 3 12 0l52 -52c3 -3 3 -9 0 -12l-76 -75l28 -29c2 -2 3 -5 2 -8s-3 -4 -6 -5l-144 -25c-3 0 -6 0 -8 2v0c-2 2 -2 5 -2 8zM488 294c-1 -3 -3 -6 -6 -7s-6 0 -8 2l-28 28l-76 -75c-3 -3 -9 -3 -12 0l-52 52c-3 3 -3 9 0 12
|
||||
l76 75l-28 29c-2 2 -3 5 -2 8s3 4 6 5l144 25c3 0 6 0 8 -2v0c2 -2 2 -5 2 -8zM358 -40c-3 1 -6 3 -7 6s0 6 2 8l28 28l-75 76c-3 3 -3 9 0 12l52 52c3 3 9 3 12 0l75 -76l29 28c2 2 5 3 8 2s4 -3 5 -6l25 -144c0 -3 0 -6 -2 -8v0c-2 -2 -5 -2 -8 -2zM154 424
|
||||
c3 -1 6 -3 7 -6s0 -6 -2 -8l-28 -28l75 -76c3 -3 3 -9 0 -12l-52 -52c-3 -3 -9 -3 -12 0l-75 76l-29 -28c-2 -2 -5 -3 -8 -2s-4 3 -5 6l-25 144c0 3 0 6 2 8v0c2 2 5 2 8 2z" />
|
||||
<glyph glyph-name="uniF111" unicode="" horiz-adv-x="440"
|
||||
d="M94 315c2 1 3 2 5 3c17 15 37 27 59 34c11 3 23 4 35 5c2 0 6 -2 7 -4s0 -6 -2 -8c-3 -3 -6 -5 -10 -6c-15 -5 -29 -9 -44 -14c-3 -1 -6 -6 -9 -8c-7 -6 -14 -10 -21 -16l-3 -3v-1c4 2 7 3 11 4c12 4 23 9 35 11c10 2 21 -1 32 -2c2 0 5 -2 7 -4c8 -6 15 -13 23 -19
|
||||
c3 -2 3 -4 1 -7c-5 -7 -11 -8 -18 -4c-6 3 -10 7 -15 11c-3 2 -5 4 -9 3c-7 -1 -15 -1 -22 -2c-3 0 -5 -1 -7 -2c-10 -7 -20 -15 -30 -22c-2 -1 -3 -4 -3 -7c1 0 2 0 3 1c8 5 17 6 26 7c3 0 6 1 9 1c12 2 20 -3 29 -10c7 -5 14 -10 21 -16c4 -3 6 -8 6 -13
|
||||
c0 -9 0 -18 -3 -27c0 -1 -1 -3 0 -3c6 -4 2 -9 1 -13c-3 -9 -7 -18 -10 -27c-3 -8 -9 -13 -18 -16c-21 -6 -41 -13 -62 -20c-8 -3 -17 -4 -26 -2c-17 4 -33 2 -49 -5c-7 -3 -15 -7 -23 -11c-9 13 -14 27 -17 41c-4 17 -4 35 -2 52c0 1 2 3 3 4c17 7 25 21 31 37
|
||||
c7 19 9 40 21 57c13 19 23 39 40 55c8 8 16 18 23 27c3 4 5 4 8 -1s3 -11 -1 -16c-10 -13 -21 -26 -31 -39c-1 -1 -1 -2 -2 -4zM167 170c4 0 8 -1 10 3c4 9 11 16 18 22c2 2 2 3 1 5c-4 7 -6 14 -10 21c-1 2 -3 4 -5 5c-6 4 -12 8 -19 12c-2 1 -4 2 -6 1
|
||||
c-5 -2 -10 -5 -15 -7c-4 -2 -9 -3 -13 -5c-11 -4 -14 -13 -17 -23c-2 -6 -1 -11 4 -16c6 -6 12 -13 17 -20c2 -2 3 -2 6 -1c10 3 19 5 29 3zM346 69c-3 -3 -7 -4 -10 -7c-15 -14 -34 -24 -53 -30c-11 -4 -23 -5 -35 -6c-7 -1 -10 7 -6 12c3 3 7 7 11 8c12 4 25 7 37 11
|
||||
c5 2 9 5 13 8c8 6 16 13 24 19c1 1 2 2 2 4c-4 -2 -9 -4 -13 -6c-11 -4 -21 -9 -32 -10s-22 1 -33 2c-2 0 -5 2 -7 4c-7 5 -13 11 -20 16s-6 12 3 15c3 1 8 1 11 -1c6 -3 11 -8 17 -12c2 -1 4 -2 6 -2c8 0 15 1 22 2c3 0 7 1 10 3c9 7 19 14 28 21c2 1 2 4 3 7
|
||||
c-12 -8 -26 -8 -39 -9c-9 -1 -16 1 -23 7c-9 7 -19 14 -28 21c-2 2 -4 5 -4 8c0 9 1 18 2 27c0 3 2 6 -1 9c-1 1 0 3 0 4c1 4 3 7 4 11c3 7 5 13 7 20c3 9 9 15 19 18c21 6 42 13 62 20c9 3 17 4 26 2c18 -5 35 -2 52 6c6 3 12 7 19 10c8 -9 12 -21 15 -32
|
||||
c6 -20 6 -40 4 -61c0 -5 -4 -5 -7 -6c-10 -4 -17 -13 -21 -22c-5 -10 -8 -21 -11 -32c-5 -15 -10 -30 -19 -43c-7 -10 -13 -20 -20 -30c-6 -8 -13 -15 -19 -23l-21 -24c-4 -4 -5 -4 -8 1s-2 10 2 16c10 13 20 25 30 38c1 1 1 3 2 5c-1 0 -1 1 -1 1zM245 186
|
||||
c3 -8 6 -16 10 -24c1 -2 2 -3 4 -4c6 -4 13 -8 19 -12c2 -1 4 -2 7 -1c8 3 15 7 23 10s14 6 17 14c2 4 3 9 4 13c1 5 1 9 -3 13c-7 7 -13 14 -19 21c-1 1 -3 1 -4 1c-11 -3 -22 -5 -33 -3c-2 0 -6 -1 -7 -3c-7 -8 -12 -17 -18 -25z" />
|
||||
<glyph glyph-name="uniF112" unicode="" horiz-adv-x="343"
|
||||
d="M338 140c4 -3 6 -7 4 -12v0v-2v0h-1l-28 -49v0c-3 -5 -9 -6 -14 -3c-1 0 -1 0 -2 1l-87 50v-103v0v0c0 -5 -4 -9 -9 -10v0h-58v0c-6 0 -10 4 -10 10v3v100l-89 -51v0v0c-4 -3 -10 -2 -13 2v0l-30 51v0c-3 5 -1 11 4 14c1 0 1 1 2 1l88 50l-90 51v1v0v0v0c-5 3 -7 8 -4 13
|
||||
v0l29 49v0v1c3 5 9 7 14 4v0l89 -52v103v0c0 5 5 10 10 10v0h57v0v0c6 0 10 -4 10 -10v0v-103l90 51v0c5 3 10 2 13 -3v0l28 -49v0l1 -1c3 -5 1 -11 -4 -14v0l-89 -51l89 -51v0v-1z" />
|
||||
<glyph glyph-name="uniF113" unicode="" horiz-adv-x="384"
|
||||
d="M384 192v-9v0c0 -76 -46 -102 -85 -102c-27 0 -75 22 -78 39v3c-16 -22 -41 -36 -65 -36c-44 0 -70 31 -70 75c0 60 50 111 102 111c27 0 47 -13 56 -30l4 18v0c1 3 4 5 7 5h37c4 0 7 -2 7 -6l-1 -2h1l-22 -101c0 -2 -1 -8 -1 -11c0 -14 8 -21 17 -21c18 0 39 29 39 80
|
||||
c-7 72 -67 128 -140 128c-78 0 -141 -63 -141 -141s63 -141 141 -141c19 0 37 3 53 10v0c1 0 2 1 3 1c2 0 4 -1 5 -2v0l34 -21v0c3 -1 4 -4 4 -7c0 -4 -2 -6 -5 -7c-28 -16 -60 -25 -94 -25c-106 0 -192 86 -192 192s86 192 192 192s192 -86 192 -192zM226 157l11 52
|
||||
c-5 10 -16 23 -36 23c-36 0 -62 -32 -62 -65c0 -23 14 -40 37 -40c24 0 40 16 50 30z" />
|
||||
<glyph glyph-name="uniF114" unicode=""
|
||||
d="M122 53h133l17 -45h-167zM136 98l52 138l53 -138h-105zM333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-3l-106 270v0c-2 5 -7 9 -13 9h-45c-6 0 -11 -4 -13 -9v0l-106 -270h-11c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297z" />
|
||||
<glyph glyph-name="uniF115" unicode="" horiz-adv-x="484"
|
||||
d="M476 236c5 0 8 -4 8 -9v-62c0 -5 -3 -9 -8 -9h-40v-95v0v0c0 -5 -4 -9 -9 -9h-418c-5 0 -9 4 -9 9v0v0v262v0v0c0 5 4 9 9 9h418c5 0 9 -4 9 -9v0v0v-87h40zM385 103v177h-333v-177h333z" />
|
||||
<glyph glyph-name="uniF116" unicode="" horiz-adv-x="484"
|
||||
d="M476 236c5 0 8 -4 8 -9v-62c0 -5 -3 -9 -8 -9h-40v-95v0v0c0 -5 -4 -9 -9 -9h-418c-5 0 -9 4 -9 9v0v0v262v0v0c0 5 4 9 9 9h418c5 0 9 -4 9 -9v0v0v-87h40zM385 103v177h-333v-177h333zM78 128v129h281v-129h-281z" />
|
||||
<glyph glyph-name="uniF117" unicode="" horiz-adv-x="484"
|
||||
d="M476 236c5 0 8 -4 8 -9v-62c0 -5 -3 -9 -8 -9h-40v-95v0v0c0 -5 -4 -9 -9 -9h-418c-5 0 -9 4 -9 9v0v0v262v0v0c0 5 4 9 9 9h418c5 0 9 -4 9 -9v0v0v-87h40zM385 103v177h-333v-177h333zM276 255l-50 -126h-149v126h199z" />
|
||||
<glyph glyph-name="uniF118" unicode="" horiz-adv-x="384"
|
||||
d="M191 253c12 -3 50 -8 44 -33c-6 -24 -43 -12 -55 -9zM175 190c14 -4 60 -11 53 -38c-7 -26 -50 -13 -64 -9zM238 378c103 -26 166 -129 140 -232s-129 -166 -232 -140s-166 129 -140 232s129 166 232 140zM277 219c4 26 -16 40 -43 49l9 34l-21 6l-8 -34
|
||||
c-6 1 -11 3 -17 4l8 34l-21 5l-9 -34c-5 1 -9 2 -13 3v0l-29 7l-6 -22l16 -4c9 -2 9 -9 9 -13l-9 -39c1 0 1 -1 2 -1c-1 0 -1 1 -2 1l-14 -55c-1 -3 -4 -6 -10 -5l-15 3l-11 -24l28 -6c5 -1 10 -3 15 -4l-9 -35l21 -6l9 35c6 -2 12 -3 17 -4l-9 -35l21 -5l9 35
|
||||
c36 -7 63 -4 74 28c9 26 0 41 -19 51c14 3 24 12 27 31z" />
|
||||
<glyph glyph-name="uniF119" unicode="" horiz-adv-x="260"
|
||||
d="M213 205c31 -7 51 -26 47 -67c-5 -51 -44 -65 -98 -68v-47v0v0c0 -4 -2 -7 -6 -7v0v0v0h-20v0v0c-4 0 -6 3 -6 7v0v46c-8 0 -17 1 -26 1v-47v0v0c0 -4 -2 -7 -6 -7v0v0v0h-20v0v0c-4 0 -6 3 -6 7v0v47h-23h-42v0c-3 0 -6 4 -6 7v0v0l5 27h1c1 3 3 5 6 5v0v0h23
|
||||
c9 0 12 7 13 11v85v0v61c-1 7 -6 14 -19 14h-24v0v0c-4 0 -6 2 -6 6v22c0 4 2 6 6 6v0h45v0h21v47v0v0c0 4 2 7 6 7v0v0v0v0h20v0v0c4 0 6 -3 6 -7v0v-46c9 0 17 1 26 1v45v0v0c0 4 2 7 6 7v0v0v0v0h20v0v0c4 0 6 -3 6 -7v0v-47c42 -4 75 -16 79 -54c3 -28 -9 -45 -28 -55z
|
||||
M105 277v-64c18 0 76 -5 76 32c0 38 -58 32 -76 32zM105 109c22 0 91 -5 91 35c0 42 -69 36 -91 36v-71z" />
|
||||
<glyph glyph-name="uniF11A" unicode="" horiz-adv-x="342"
|
||||
d="M91 347c0 24 12 36 36 36s37 -12 37 -36s-13 -37 -37 -37s-36 13 -36 37zM341 15c1 -1 1 -2 1 -3c0 -4 -2 -6 -6 -6c-2 0 -4 1 -5 3v0v0v1l-107 184h-3c-3 0 -6 1 -8 2v0v0v1h-1l-35 20v0v0v0v0l-23 23v-76l45 -44v0l49 -85c3 -4 4 -8 4 -13c0 -6 -2 -11 -6 -15
|
||||
s-9 -6 -15 -6c-8 0 -15 4 -19 11v0l-47 81l-37 37l-41 -72v0l-49 -49c-4 -5 -10 -8 -17 -8c-6 0 -11 2 -15 6s-6 9 -6 15s2 12 7 16v0l45 45l36 62v0v96l-19 -18v-52v0c0 -4 -1 -8 -4 -11s-7 -4 -11 -4c-3 0 -6 1 -8 2s-5 4 -6 6s-2 4 -2 7v0v62h1h-1c0 1 1 2 2 3v0l54 55
|
||||
c7 8 17 11 27 11c12 0 21 -4 28 -13l47 -47l31 -18c6 -3 9 -7 9 -14c0 -3 0 -6 -2 -9l107 -186v0z" />
|
||||
<glyph glyph-name="uniF11B" unicode="" horiz-adv-x="239"
|
||||
d="M231 120c5 -4 8 -10 8 -17c0 -5 -3 -10 -6 -14v0l-1 -1v0l-102 -102v0c-4 -4 -10 -7 -16 -7c-12 0 -21 9 -21 21v0v0v1v0v124l-55 -55c-4 -5 -9 -7 -16 -7c-12 0 -22 10 -22 22c0 6 2 11 6 15v0v0v0l87 86v11l-86 86c-4 4 -7 9 -7 15c0 12 10 22 22 22c6 0 12 -3 16 -7
|
||||
l55 -55v119c-1 2 -1 4 -1 6c0 12 9 22 21 22c7 0 13 -3 17 -8l101 -101c5 -4 8 -9 8 -16c0 -5 -3 -10 -6 -14v0v0c-1 -1 -1 -2 -2 -3l-72 -71zM186 103l-50 51v-101zM136 230l50 50l-50 50v-100z" />
|
||||
<glyph glyph-name="uniF11C" unicode="" horiz-adv-x="242"
|
||||
d="M186 197c31 -5 56 -35 56 -71c0 -44 -29 -79 -85 -79h-144c-7 0 -13 6 -13 13v264c0 7 6 13 13 13h140c55 0 83 -35 83 -74c0 -36 -23 -60 -50 -66zM62 283v-61h77c21 0 34 11 34 30c0 18 -13 31 -34 31h-77zM142 101c23 0 37 12 37 33c0 18 -13 33 -37 33h-80v-66h80z
|
||||
" />
|
||||
<glyph glyph-name="uniF11D" unicode="" horiz-adv-x="328"
|
||||
d="M328 334v-327v0c0 -5 -3 -8 -8 -8v0v0h-312v0c-4 0 -8 4 -8 8v0v0v0v1v65v303v1c0 5 3 8 8 8h153v-66v-84v-29c0 -2 2 -4 4 -4c1 0 2 0 3 1v0l29 30h1c1 1 1 1 2 1s2 0 3 -1v0l29 -29c1 -1 2 -2 3 -2c2 0 4 2 4 4v29v84v66h29v0h1c5 0 8 -3 8 -8v-319v0c0 -5 -3 -8 -8 -8
|
||||
h-1v0h-242v-19c1 -4 4 -7 8 -7h261v0c5 0 8 4 8 9v0v309h17v0c4 0 8 -4 8 -8v0z" />
|
||||
<glyph glyph-name="uniF11E" unicode="" horiz-adv-x="328"
|
||||
d="M328 334v-327v0c0 -5 -3 -8 -8 -8v0v0h-312v0c-4 0 -8 4 -8 8v0v0v0v1v65v303v1c0 5 3 8 8 8h260v0h1c5 0 8 -3 8 -8v-319v0c0 -5 -3 -8 -8 -8h-1v0h-242v-19c1 -4 4 -7 8 -7h261v0c5 0 8 4 8 9v0v309h17v0c4 0 8 -4 8 -8v0zM25 326v0v-64c0 -5 4 -8 9 -8h209v0
|
||||
c5 0 8 3 8 8v0v64v0c0 5 -3 8 -8 8h-209c-5 0 -9 -3 -9 -8z" />
|
||||
<glyph glyph-name="uniF11F" unicode="" horiz-adv-x="218"
|
||||
d="M203 381c9 0 15 -7 15 -16v-34v-235v-82c0 -6 -5 -11 -11 -11c-3 0 -7 2 -9 4l-81 82v0c-2 2 -5 3 -8 3s-5 -1 -7 -3v0l-83 -83v0c-2 -2 -5 -3 -8 -3c-6 0 -11 5 -11 11v82v235v34c0 9 6 16 15 16h188z" />
|
||||
<glyph glyph-name="uniF120" unicode="" horiz-adv-x="265"
|
||||
d="M0 325c0 24 12 36 36 36s35 -12 35 -36s-11 -35 -35 -35s-36 11 -36 35zM194 325c0 24 11 36 35 36s36 -12 36 -36s-12 -35 -36 -35s-35 11 -35 35zM0 192c0 24 12 36 36 36s35 -12 35 -36s-11 -36 -35 -36s-36 12 -36 36zM194 192c0 24 11 36 35 36s36 -12 36 -36
|
||||
s-12 -36 -36 -36s-35 12 -35 36zM0 59c0 24 12 36 36 36s35 -12 35 -36s-11 -36 -35 -36s-36 12 -36 36zM194 59c0 24 11 36 35 36s36 -12 36 -36s-12 -36 -36 -36s-35 12 -35 36z" />
|
||||
<glyph glyph-name="uniF121" unicode="" horiz-adv-x="395"
|
||||
d="M395 192c0 -5 -3 -9 -6 -11v0l-48 -28l27 -46v0c2 -4 1 -9 -1 -13s-6 -6 -10 -6v0h-54v-55h-1c0 -4 -2 -8 -6 -10s-9 -3 -13 -1v0l-47 27l-27 -47v0c-2 -3 -6 -6 -11 -6s-9 3 -11 6v0l-27 46l-49 -28v0c-4 -2 -8 -1 -12 1s-7 6 -7 10v0v55h-55v0c-4 0 -8 3 -10 7
|
||||
s-3 8 -1 12v0l27 48l-47 28v0c-3 2 -6 6 -6 11s3 9 6 11v0l48 28l-27 46v0c-2 4 -1 9 1 13s6 6 10 6v0h54v55v0c0 4 3 8 7 10s8 3 12 1v0l48 -27l27 47v0c2 3 6 6 11 6s9 -3 11 -6v0l27 -46l49 28v0c4 2 8 1 12 -1s7 -6 7 -10v0v-55h55v0c4 0 8 -3 10 -7s3 -8 1 -12v-1
|
||||
l-27 -47l47 -28v0c3 -2 6 -6 6 -11zM165 134l12 7l-34 59l-12 -7l21 -37l-49 21l-13 -8l34 -59l13 8l-22 38zM189 148l42 24l-7 11l-29 -17l-8 13l29 17l-6 11l-29 -17l-7 13l29 17l-6 11l-42 -24zM286 204l13 7l-17 69l-14 -8l14 -49l-36 36l-9 -5l13 -49l-35 36l-14 -8
|
||||
l51 -49l13 8l-13 46z" />
|
||||
<glyph glyph-name="uniF122" unicode="" horiz-adv-x="395"
|
||||
d="M167 163l-5 31l24 -20zM395 192c0 -5 -3 -9 -6 -11v0l-48 -28l27 -46v0c2 -4 1 -9 -1 -13s-6 -6 -10 -6v0h-54v-55h-1c0 -4 -2 -8 -6 -10s-9 -3 -13 -1v0l-47 27l-27 -47v0c-2 -3 -6 -6 -11 -6s-9 3 -11 6v0l-27 46l-49 -28v0c-4 -2 -8 -1 -12 1s-7 6 -7 10v0v55h-55v0
|
||||
c-4 0 -8 3 -10 7s-3 8 -1 12v0l27 48l-47 28v0c-3 2 -6 6 -6 11s3 9 6 11v0l48 28l-27 46v0c-2 4 -1 9 1 13s6 6 10 6v0h54v55v0c0 4 3 8 7 10s8 3 12 1v0l48 -27l27 47v0c2 3 6 6 11 6s9 -3 11 -6v0l27 -46l49 28v0c4 2 8 1 12 -1s7 -6 7 -10v0v-55h55v0c4 0 8 -3 10 -7
|
||||
s3 -8 1 -12v-1l-27 -47l47 -28v0c3 -2 6 -6 6 -11zM134 114c16 9 18 22 12 33c-13 22 -43 -4 -48 5c-2 4 0 8 5 11c6 3 14 5 21 3l1 14c-9 2 -18 0 -27 -5c-14 -8 -18 -21 -12 -31c13 -22 42 4 48 -6c2 -3 1 -8 -6 -12c-8 -5 -17 -5 -24 -3l-1 -14c9 -2 20 -2 31 5zM205 157
|
||||
l14 8l-57 46l-15 -9l11 -72l14 8l-2 12l25 15zM224 168l38 22l-7 11l-25 -15l-27 48l-13 -7zM270 195l42 24l-6 11l-29 -17l-8 13l28 17l-6 11l-29 -17l-7 13l29 17l-6 11l-42 -24z" />
|
||||
<glyph glyph-name="uniF123" unicode="" horiz-adv-x="395"
|
||||
d="M395 192c0 -5 -3 -9 -6 -11v0l-48 -28l27 -46v0c2 -4 1 -9 -1 -13s-6 -6 -10 -6v0h-54v-55h-1c0 -4 -2 -8 -6 -10s-9 -3 -13 -1v0l-47 27l-27 -47v0c-2 -3 -6 -6 -11 -6s-9 3 -11 6v0l-27 46l-49 -28v0c-4 -2 -8 -1 -12 1s-7 6 -7 10v0v55h-55v0c-4 0 -8 3 -10 7
|
||||
s-3 8 -1 12v0l27 48l-47 28v0c-3 2 -6 6 -6 11s3 9 6 11v0l48 28l-27 46v0c-2 4 -1 9 1 13s6 6 10 6v0h54v55v0c0 4 3 8 7 10s8 3 12 1v0l48 -27l27 47v0c2 3 6 6 11 6s9 -3 11 -6v0l27 -46l49 28v0c4 2 8 1 12 -1s7 -6 7 -10v0v-55h55v0c4 0 8 -3 10 -7s3 -8 1 -12v-1
|
||||
l-27 -47l47 -28v0c3 -2 6 -6 6 -11z" />
|
||||
<glyph glyph-name="uniF124" unicode="" horiz-adv-x="326"
|
||||
d="M320 246c4 0 6 -3 6 -7v-225c0 -4 -2 -6 -6 -6h-314c-4 0 -6 2 -6 6v225c0 4 2 7 6 7h314zM109 55c34 0 56 18 56 44c0 18 -13 26 -25 31c14 7 21 17 21 31c0 18 -18 30 -44 30c-15 0 -27 -2 -37 -7c-1 -1 -2 -2 -2 -3l3 -22c0 -1 1 -2 2 -2h3c8 4 16 6 24 6
|
||||
c15 0 15 -6 15 -9c0 -5 -3 -11 -24 -13c-2 0 -3 -1 -3 -3v-22c0 -2 1 -3 3 -3c25 -1 30 -7 30 -16c0 -8 -9 -13 -20 -13s-19 1 -29 5c-1 0 -2 1 -3 0s-1 -1 -1 -2l-3 -22c0 -1 1 -3 2 -4c9 -4 21 -6 32 -6zM245 61v0v125c0 2 -1 3 -3 3h-13h-2l-38 -19c-1 -1 -2 -2 -2 -3
|
||||
l3 -22c0 -1 1 -3 2 -3c1 -1 2 0 3 0l17 7v-88c0 -2 1 -4 3 -4h27c2 0 3 2 3 4zM320 347c4 0 6 -2 6 -6v-66c0 -4 -2 -7 -6 -7h-314c-4 0 -6 3 -6 7v66c0 4 2 6 6 6h34v-16c0 -17 9 -34 36 -34s36 17 36 34v16h102v-16c0 -17 9 -34 36 -34s36 17 36 34v16h34zM76 318
|
||||
c-11 0 -15 4 -15 13v16v16c0 9 4 13 15 13s15 -4 15 -13v-16v-16c0 -9 -4 -13 -15 -13zM250 318c-11 0 -15 4 -15 13v16v16c0 9 4 13 15 13s15 -4 15 -13v-16v-16c0 -9 -4 -13 -15 -13z" />
|
||||
<glyph glyph-name="uniF125" unicode="" horiz-adv-x="415"
|
||||
d="M159 175c0 32 17 49 49 49s48 -17 48 -49s-16 -48 -48 -48s-49 16 -49 48zM396 324c5 0 9 -1 13 -5s6 -9 6 -14v-259c0 -5 -2 -10 -6 -14s-8 -5 -13 -5v-1h-377v1c-5 0 -9 1 -13 5s-6 9 -6 14v259c0 5 2 10 6 14s8 5 13 5v0h85v15c0 5 2 9 6 13s8 6 13 6h168
|
||||
c5 0 10 -2 14 -6s5 -8 5 -13v-15h86v0zM209 77c18 0 34 4 49 13s27 21 36 36s13 31 13 49c0 27 -10 51 -29 70s-42 29 -69 29s-51 -10 -70 -29s-29 -43 -29 -70s10 -50 29 -69s43 -29 70 -29zM389 248v0v51h-75v-51h75z" />
|
||||
<glyph glyph-name="uniF126" unicode="" horiz-adv-x="397"
|
||||
d="M393 293c5 -5 5 -13 0 -18l-250 -250c-2 -2 -6 -4 -9 -4v1l-1 -1c-3 0 -6 2 -8 4l-121 121c-2 2 -4 6 -4 9s2 7 4 9l66 66c5 5 12 5 17 0l47 -47l176 176c2 2 5 4 8 4s7 -2 9 -4z" />
|
||||
<glyph glyph-name="uniF127" unicode="" horiz-adv-x="381"
|
||||
d="M379 323c3 -3 3 -8 0 -11l-209 -209c-2 -2 -4 -3 -6 -3v0v0c-2 0 -4 1 -6 3l-81 80c-2 2 -3 4 -3 6s1 4 3 6l44 44c3 3 9 3 12 0l31 -31l159 160c2 2 4 2 6 2s4 0 6 -2zM277 173l51 52v-185v0c0 -14 -12 -26 -26 -26v0h-1v0v0h-275v0v0v0v0c-14 0 -26 12 -26 26v0v276v0
|
||||
c0 14 12 26 26 26h1v0h234l-51 -51h-159v-226h226v108z" />
|
||||
<glyph glyph-name="uniF128" unicode="" horiz-adv-x="315"
|
||||
d="M315 360v0v-362v0v0c0 -15 -12 -27 -27 -27h-1h-259v0h-1c-15 0 -27 12 -27 27v0v0v362v0v0c0 15 12 27 27 27v0h75v17v1c0 5 3 8 8 8v0v0h95v0c5 0 8 -3 8 -8v0v0v-18h75v0c15 0 27 -12 27 -27v0v0zM264 22v0v314h-25v-16v0c0 -5 -5 -9 -10 -9h-143v0v0
|
||||
c-5 0 -10 4 -10 9v1v15h-25v-314h213zM102 237v0v0v-13v0v0c0 -3 -3 -6 -6 -6h-1v0h-12v0c-3 0 -7 3 -7 6v0v0v13c0 3 4 7 7 7h12v-1l1 1c3 0 6 -4 6 -7zM239 237v0v0v-13v0v0c0 -3 -4 -6 -7 -6h-98v0c-3 0 -7 3 -7 6v0v0v13c0 3 4 7 7 7h98v0c3 0 7 -4 7 -7zM102 186v0v0
|
||||
v-13v0v0c0 -3 -3 -6 -6 -6h-1v0h-12v0c-3 0 -7 3 -7 6v0v0v13c0 3 4 7 7 7h12v-1l1 1c3 0 6 -4 6 -7zM239 186v0v0v-13v0v0c0 -3 -4 -6 -7 -6h-98v0c-3 0 -7 3 -7 6v0v0v13c0 3 4 7 7 7h98v0c3 0 7 -4 7 -7zM102 135v0v0v-13v0v0c0 -3 -3 -6 -6 -6h-1v0h-12v0
|
||||
c-3 0 -7 3 -7 6v0v0v13c0 3 4 6 7 6h12v0h1c3 0 6 -3 6 -6zM239 135v0v0v-13v0v0c0 -3 -4 -6 -7 -6h-98v0c-3 0 -7 3 -7 6v0v0v13c0 3 4 6 7 6h98v0c3 0 7 -3 7 -6z" />
|
||||
<glyph glyph-name="uniF129" unicode="" horiz-adv-x="392"
|
||||
d="M391 228c2 -2 2 -7 0 -9l-116 -116v0v0l-59 -16v0c-2 -1 -5 0 -7 2s-2 4 -1 6v0l16 59v0v0l115 116c2 2 7 2 9 0zM227 107l35 9l-25 26zM309 73c3 0 6 -3 6 -6v0v0v-69v0v0c0 -15 -12 -27 -27 -27h-1h-259v0h-1c-15 0 -27 12 -27 27v0v0v362v0v0c0 15 12 27 27 27v0h75
|
||||
v17v1c0 5 3 8 8 8v0v0h95v0c4 0 8 -3 8 -8v0v0v-18h75v0c15 0 27 -12 27 -27v0v0v-68v0c0 -3 -3 -7 -6 -7v0h-39c-3 0 -6 4 -6 7v0v44h-25v-16v0c0 -5 -5 -9 -10 -9h-143v0v0c-5 0 -10 4 -10 9v1v15h-25v-314h213v45v0c0 3 3 6 6 6v0v0h39v0z" />
|
||||
<glyph glyph-name="uniF12A" unicode="" horiz-adv-x="315"
|
||||
d="M315 360v0v-362v0v0c0 -15 -12 -27 -27 -27h-1h-259v0h-1c-15 0 -27 12 -27 27v0v0v362v0v0c0 15 12 27 27 27v0h75v17v1c0 5 3 8 8 8v0v0h95v0c5 0 8 -3 8 -8v0v0v-18h75v0c15 0 27 -12 27 -27v0v0zM264 22v0v314h-25v-16v0c0 -5 -5 -9 -10 -9h-143v0v0
|
||||
c-5 0 -10 4 -10 9v1v15h-25v-314h213z" />
|
||||
<glyph glyph-name="uniF12B" unicode="" horiz-adv-x="384"
|
||||
d="M192 333c-78 0 -141 -63 -141 -141s63 -141 141 -141s141 63 141 141s-63 141 -141 141zM192 384v0c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM290 263c4 -4 4 -10 0 -14l-91 -91c-2 -2 -4 -3 -7 -3v0c-3 0 -5 1 -7 3l-62 62
|
||||
c-2 2 -3 4 -3 7s1 6 3 8l20 20c4 4 10 4 14 0l35 -36l64 64c4 4 10 4 14 0z" />
|
||||
<glyph glyph-name="uniF12C" unicode="" horiz-adv-x="342"
|
||||
d="M333 300c5 -1 9 -6 9 -11v-94v-6v-94c0 -5 -4 -10 -9 -11l-160 -32h-4l-160 32c-5 1 -9 6 -9 11v94v6v94c0 5 4 10 9 11l160 32h4zM149 150c1 2 1 5 0 7s-3 4 -5 5l-19 8c-2 1 -4 1 -6 0s-4 -3 -5 -5c-2 -4 -7 -14 -17 -14c-14 0 -24 17 -24 41s10 41 24 41
|
||||
c9 0 14 -7 17 -13c2 -4 8 -6 12 -4l18 8c2 1 4 3 5 5s0 4 -1 6c-11 23 -28 35 -51 35c-38 0 -64 -31 -64 -78s26 -78 64 -78c23 0 41 12 52 36zM308 150c1 2 1 5 0 7s-3 4 -5 5l-18 8c-2 1 -5 1 -7 0s-4 -3 -5 -5c-2 -4 -6 -14 -16 -14c-14 0 -25 17 -25 41s11 41 25 41
|
||||
c9 0 14 -7 17 -13c2 -4 7 -6 11 -4l18 8c2 1 4 3 5 5s0 4 -1 6c-11 23 -27 35 -50 35c-38 0 -65 -31 -65 -78s27 -78 65 -78c23 0 40 12 51 36z" />
|
||||
<glyph glyph-name="uniF12D" unicode="" horiz-adv-x="473"
|
||||
d="M387 206c46 6 86 -35 86 -84c0 -18 -6 -35 -16 -50c-3 -4 -7 -7 -12 -7h-414c-5 0 -10 3 -13 8c-12 20 -18 42 -18 66c0 66 49 121 110 121c8 0 15 -1 23 -3c27 39 70 62 115 62c65 0 121 -47 139 -113z" />
|
||||
<glyph glyph-name="uniF12E" unicode="" horiz-adv-x="470"
|
||||
d="M302 166h92v-93v0c-2 -8 -9 -14 -17 -14h-225l-44 -44c-3 -5 -9 -8 -15 -8c-10 0 -17 7 -17 17v35h-59v0c-8 0 -15 6 -17 14v0v288v0c1 9 8 16 17 16v0h360c9 0 16 -7 17 -16v0v-91h-92c-5 0 -9 -4 -9 -9v0v-86v0c0 -5 4 -9 9 -9zM461 245c5 0 9 -4 9 -9v-36
|
||||
c0 -5 -4 -8 -9 -8h-133c-5 0 -9 3 -9 8v36c0 5 4 9 9 9h133z" />
|
||||
<glyph glyph-name="uniF12F" unicode="" horiz-adv-x="394"
|
||||
d="M394 361v0v-288v0c-2 -8 -9 -14 -17 -14h-225l-44 -44c-3 -5 -9 -8 -15 -8c-10 0 -17 7 -17 17v35h-59v0c-8 0 -15 6 -17 14v0v288v0c1 9 8 16 17 16v0h360c9 0 16 -7 17 -16zM87 189c14 0 26 12 26 26s-10 24 -23 24c-3 0 -5 0 -6 -1c3 12 13 25 24 31v0h1v0
|
||||
c1 1 2 2 2 3s-1 2 -2 3v0l-14 9v0c-1 0 -1 1 -2 1s-1 -1 -2 -1v0c-21 -15 -34 -36 -34 -61c0 -22 14 -34 30 -34zM154 189c14 0 27 12 27 26s-10 24 -23 24c-3 0 -6 0 -7 -1c3 12 14 25 25 31v0v0v0c1 1 2 2 2 3s0 2 -1 3v0l-14 9v0c-1 0 -1 1 -2 1s-1 -1 -2 -1v0
|
||||
c-21 -15 -34 -36 -34 -61c0 -22 13 -34 29 -34zM235 152c21 15 35 37 35 62c0 22 -14 34 -30 34c-14 0 -26 -13 -26 -27s9 -24 22 -24c3 0 6 0 7 1c-3 -12 -14 -25 -25 -31v0v0v0c-1 -1 -2 -2 -2 -3s1 -2 2 -3h-1l14 -9v0c1 0 2 -1 3 -1s0 1 1 1v0zM303 152
|
||||
c21 15 34 37 34 62c0 22 -13 34 -29 34c-14 0 -27 -13 -27 -27s10 -24 23 -24c3 0 6 0 7 1c-3 -12 -14 -25 -25 -31v0v0v0c-1 -1 -2 -2 -2 -3s0 -2 1 -3v0l14 -9v0c1 0 1 -1 2 -1s1 1 2 1v0z" />
|
||||
<glyph glyph-name="uniF130" unicode="" horiz-adv-x="394"
|
||||
d="M394 361v0v-288v0c-2 -8 -9 -14 -17 -14h-225l-44 -44c-3 -5 -9 -8 -15 -8c-10 0 -17 7 -17 17v35h-59v0c-8 0 -15 6 -17 14v0v288v0c1 9 8 16 17 16v0h360c9 0 16 -7 17 -16zM305 155v124l-65 -30v28c0 7 -6 12 -13 12h-110c-7 0 -12 -5 -12 -12v-120c0 -7 5 -12 12 -12
|
||||
h110c7 0 13 5 13 12v29z" />
|
||||
<glyph glyph-name="uniF131" unicode="" horiz-adv-x="394"
|
||||
d="M394 361v0v-288v0c-2 -8 -9 -14 -17 -14h-225l-44 -44c-3 -5 -9 -8 -15 -8c-10 0 -17 7 -17 17v35h-59v0c-8 0 -15 6 -17 14v0v288v0c1 9 8 16 17 16v0h360c9 0 16 -7 17 -16z" />
|
||||
<glyph glyph-name="uniF132" unicode="" horiz-adv-x="461"
|
||||
d="M461 357v0v-217v0c-1 -6 -7 -11 -13 -11v0h-44v-26c0 -7 -6 -13 -13 -13c-4 0 -9 3 -11 6l-33 33h-57v150v0c-1 6 -5 10 -11 10h-115v68v0c1 7 6 11 13 11h271v0c7 0 12 -5 13 -11zM253 264c6 0 11 -5 12 -11v0v-193h-1c-1 -5 -5 -10 -11 -10h-151l-30 -29
|
||||
c-2 -3 -5 -5 -9 -5c-6 0 -12 5 -12 11v23h-40v0c-5 0 -10 5 -11 10v0v193v0c1 6 5 11 11 11v0h242z" />
|
||||
<glyph glyph-name="uniF133" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM192 51c78 0 141 63 141 141s-63 141 -141 141s-141 -63 -141 -141s63 -141 141 -141zM267 281c4 2 9 1 12 -2s4 -8 2 -12l-60 -100l-3 -3l-101 -61c-2 -1 -3 -1 -5 -1s-5 1 -7 3v0
|
||||
c-3 3 -4 8 -2 12l61 101l3 3z" />
|
||||
<glyph glyph-name="uniF134" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM193 333v-282c78 0 140 63 140 141s-62 141 -140 141z" />
|
||||
<glyph glyph-name="uniF135" unicode="" horiz-adv-x="378"
|
||||
d="M378 314v-1v0v-37h-378v36v2c0 8 6 13 14 13h1h349h1c8 0 13 -5 13 -13zM0 70v155h378v-155v0c0 -7 -6 -13 -13 -13v0h-350v0h-1c-8 0 -14 5 -14 13v0v0z" />
|
||||
<glyph glyph-name="uniF136" unicode="" horiz-adv-x="419"
|
||||
d="M405 84c8 0 14 -6 14 -14v-23c0 -8 -6 -14 -14 -14v0h-37v-36c0 -8 -6 -14 -14 -14h-23c-8 0 -14 6 -14 14v36h-241v0c-13 0 -25 12 -25 25v1v0v241h-37v0c-8 0 -14 6 -14 14v23c0 8 6 14 14 14v0h37v36v0c0 8 6 14 14 14h23c8 0 14 -6 14 -14v0v-36h241v0
|
||||
c13 0 25 -12 25 -25v-1v0v-241h37v0zM102 84v0h215v216h-215v-216z" />
|
||||
<glyph glyph-name="uniF137" unicode="" horiz-adv-x="348"
|
||||
d="M348 38v-19v0v0c0 -9 -6 -16 -15 -16h-317v0c-9 0 -16 7 -16 16v0v0v19v0c0 9 7 16 16 16v0h317c9 0 15 -7 15 -16v0zM44 251c1 -1 1 0 2 -1l34 -35l82 81v0c3 3 7 5 12 5s9 -2 12 -5v0l82 -81l35 35c0 1 0 2 1 2h1v0c3 2 6 4 10 4c9 0 17 -8 17 -17v-3v-145v0v0v-2v0
|
||||
c-1 -8 -8 -14 -17 -14v0v-1h-282v0v0c-9 0 -15 7 -16 15h-1v150h1c0 9 7 16 16 16c4 0 8 -1 11 -4v0v0zM292 208v0h-1h1zM2 309c0 21 10 31 31 31s30 -10 30 -31s-9 -31 -30 -31s-31 10 -31 31zM283 309c0 21 10 31 31 31s31 -10 31 -31s-10 -31 -31 -31s-31 10 -31 31z
|
||||
M145 350c0 21 10 31 31 31s31 -10 31 -31s-10 -31 -31 -31s-31 10 -31 31z" />
|
||||
<glyph glyph-name="uniF138" unicode="" horiz-adv-x="325"
|
||||
d="M0 375h325l-30 -330l-132 -36l-133 36zM260 268l4 40h-102h-103l5 -40h98h6l-6 -2l-94 -40l3 -39h91h49l-3 -52l-46 -13v0v0l-44 11l-3 32v0h-41v0l5 -62l83 -25v0h1l82 25l11 123h-94v0v0z" />
|
||||
<glyph glyph-name="uniF139" unicode="" horiz-adv-x="422"
|
||||
d="M124 323v-262v0v0c0 -12 -11 -22 -23 -22v0h-80v1c-12 0 -21 9 -21 21v0v262v0c0 12 9 22 21 22v0h80v0c12 0 22 -10 22 -22h1zM62 65c18 0 32 14 32 32s-14 32 -32 32s-32 -14 -32 -32s14 -32 32 -32zM98 180v139h-72v-139h72zM273 323v-262v0v0c0 -12 -10 -22 -22 -22
|
||||
v0h-81v1c-12 0 -21 9 -21 21v0v262v0c0 12 9 22 21 22v0h81v0c12 0 22 -10 22 -22v0zM211 65c18 0 32 14 32 32s-14 32 -32 32s-32 -14 -32 -32s14 -32 32 -32zM247 180v0v139h-72v-139h72zM422 323v-262v0v0c0 -12 -10 -22 -22 -22v0h-81v1c-12 0 -21 9 -21 21v0v262v0
|
||||
c0 12 9 22 21 22v0h81v0c12 0 22 -10 22 -22v0zM360 65c18 0 32 14 32 32s-14 32 -32 32s-32 -14 -32 -32s14 -32 32 -32zM396 180v139h-72v-139h72z" />
|
||||
<glyph glyph-name="uniF13A" unicode="" horiz-adv-x="348"
|
||||
d="M348 329v0v-274v0c0 -20 -17 -37 -37 -37v0h-274v0c-20 0 -37 17 -37 37v0v274v0c0 20 17 37 37 37v0h274v0c20 -1 37 -17 37 -37zM87 71c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM87 243c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36
|
||||
s16 -36 36 -36zM174 156c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM261 69c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM261 243c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36z" />
|
||||
<glyph glyph-name="uniF13B" unicode="" horiz-adv-x="348"
|
||||
d="M348 329v0v-274v0c0 -20 -17 -37 -37 -37v0h-274v0c-20 0 -37 17 -37 37v0v274v0c0 20 17 37 37 37v0h274v0c20 -1 37 -17 37 -37zM87 71c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM87 243c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36
|
||||
s16 -36 36 -36zM261 69c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM261 243c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36z" />
|
||||
<glyph glyph-name="uniF13C" unicode="" horiz-adv-x="348"
|
||||
d="M348 329v0v-274v0c0 -20 -17 -37 -37 -37v0h-274v0c-20 0 -37 17 -37 37v0v274v0c0 20 17 37 37 37v0h274v0c20 -1 37 -17 37 -37zM174 153c22 0 39 17 39 39s-17 39 -39 39s-39 -17 -39 -39s17 -39 39 -39z" />
|
||||
<glyph glyph-name="uniF13D" unicode="" horiz-adv-x="348"
|
||||
d="M348 329v0v-274v0c0 -20 -17 -37 -37 -37v0h-274v0c-20 0 -37 17 -37 37v0v274v0c0 20 17 37 37 37v0h274v0c20 -1 37 -17 37 -37zM87 71c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM87 156c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36
|
||||
s16 -36 36 -36zM87 243c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM261 69c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM261 156c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM261 243c20 0 36 16 36 36
|
||||
s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36z" />
|
||||
<glyph glyph-name="uniF13E" unicode="" horiz-adv-x="348"
|
||||
d="M348 329v0v-274v0c0 -20 -17 -37 -37 -37v0h-274v0c-20 0 -37 17 -37 37v0v274v0c0 20 17 37 37 37v0h274v0c20 -1 37 -17 37 -37zM87 243c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM174 156c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36
|
||||
s16 -36 36 -36zM261 69c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36z" />
|
||||
<glyph glyph-name="uniF13F" unicode="" horiz-adv-x="348"
|
||||
d="M348 329v0v-274v0c0 -20 -17 -37 -37 -37v0h-274v0c-20 0 -37 17 -37 37v0v274v0c0 20 17 37 37 37v0h274v0c20 -1 37 -17 37 -37zM87 243c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36s16 -36 36 -36zM261 69c20 0 36 16 36 36s-16 36 -36 36s-36 -16 -36 -36
|
||||
s16 -36 36 -36z" />
|
||||
<glyph glyph-name="uniF140" unicode="" horiz-adv-x="333"
|
||||
d="M333 295v0v-144v0c0 -7 -5 -13 -12 -13v0h-71v-58v-1v-17v0c0 -16 -13 -29 -29 -29c-11 0 -21 6 -26 15v0l-50 86v0v0v1l-3 3h-51h-1c-7 0 -13 6 -13 13v0v0v144v0v43v0v1v0v0c0 7 6 12 13 12h1h185c2 0 4 0 5 -1v0l50 -50v0c1 -1 2 -3 2 -5v0zM51 316v-138v-2
|
||||
c0 -7 -6 -13 -13 -13v0v0v0v0h-25v0c-7 0 -12 6 -13 12v0v141v0c0 7 6 12 13 12v0h24l1 1c7 0 13 -6 13 -13v0v0z" />
|
||||
<glyph glyph-name="uniF141" unicode="" horiz-adv-x="447"
|
||||
d="M447 294v-204v0v0c0 -11 -9 -20 -20 -20v0v0h-407c-11 0 -20 9 -20 20v0v0v204v0c0 11 9 20 20 20h407v0v0c11 0 20 -9 20 -20v0zM371 266v0v0c0 -2 2 -4 4 -4v0v0h21v-21v0c0 -2 2 -4 4 -4v0h17v0h1c2 0 4 2 4 4v0v43c0 2 -2 4 -4 4h-1v0h-42v0v0c-2 0 -4 -2 -4 -4v-18z
|
||||
M77 118v0v0c0 2 -2 4 -4 4h-1v0h-21v21v0c0 2 -2 4 -4 4v0h-17v0v0c-2 0 -4 -2 -4 -4v0v-43c0 -2 2 -4 4 -4v0v0h42v0h1c2 0 4 2 4 4v18zM77 284v0v0c0 2 -2 4 -4 4h-43c-2 0 -4 -2 -4 -4v0v0v-43v0v0c0 -2 2 -4 4 -4h17v0v0c2 0 4 2 4 4v0v0v21h22v0c2 0 4 3 4 5v0v17z
|
||||
M224 96c53 0 96 43 96 96s-43 96 -96 96s-96 -43 -96 -96s43 -96 96 -96zM422 143v0v0c0 2 -2 4 -4 4h-18v0c-2 0 -4 -2 -4 -4v0v0v-21h-21v0c-2 0 -4 -3 -4 -5v0v-17v0v0c0 -2 2 -4 4 -4h43c2 0 4 2 4 4v0v0v43zM224 263c39 0 70 -32 70 -71c0 -15 -4 -29 -12 -40v0
|
||||
c0 3 -2 7 -4 8l-28 13l-12 6c5 3 9 8 12 14c2 5 3 10 3 16c0 3 0 6 -1 9c-4 14 -14 25 -28 25c-13 0 -25 -10 -29 -24c-1 -3 -1 -6 -1 -10c0 -6 2 -12 4 -17c3 -6 7 -11 12 -14l-11 -5l-29 -13c-3 -1 -4 -5 -4 -8v0c-8 11 -13 25 -13 40c0 39 32 71 71 71z" />
|
||||
<glyph glyph-name="uniF142" unicode="" horiz-adv-x="209"
|
||||
d="M126 219c39 -10 83 -26 83 -77c0 -42 -28 -73 -83 -79v-28v0c0 -3 -3 -6 -6 -6h-23v0c-3 0 -6 3 -6 6v28c-39 3 -68 18 -89 39v0c-1 1 -2 3 -2 5c0 1 0 2 1 3v0l22 32v0c1 2 3 3 5 3c1 0 2 0 3 -1v0l1 -1v0c14 -14 34 -28 59 -32v58c-39 9 -81 25 -81 76
|
||||
c0 38 30 70 81 75v29c0 3 3 6 6 6h23c3 0 6 -3 6 -6v0v-30c30 -3 57 -14 77 -33c1 -1 2 -2 2 -4c0 -1 -1 -3 -2 -4v0l-21 -31h-1c-1 -1 -2 -2 -4 -2c-1 0 -3 0 -4 1v0c-14 12 -30 20 -47 24v-51zM91 227v46c-17 -2 -26 -11 -26 -24c0 -11 11 -17 26 -22zM126 111
|
||||
c19 4 29 14 29 26s-12 19 -29 24v-50z" />
|
||||
<glyph glyph-name="uniF143" unicode="" horiz-adv-x="404"
|
||||
d="M379 194c14 0 25 -11 25 -25v-144c0 -14 -11 -25 -25 -25h-354c-14 0 -25 11 -25 25v144c0 14 11 25 25 25h90c11 0 21 -7 24 -17c9 -28 34 -46 63 -46s54 18 63 46c3 10 13 17 24 17h90zM197 181l-70 98c-1 2 -2 5 -1 7s3 3 6 3h33v88c0 4 3 7 7 7h60c4 0 7 -3 7 -7v-88
|
||||
h33c3 0 5 -1 6 -3s0 -5 -1 -7l-69 -98c-1 -2 -4 -3 -6 -3v0c-2 0 -4 1 -5 3z" />
|
||||
<glyph glyph-name="uniF144" unicode="" horiz-adv-x="382"
|
||||
d="M13 141c-7 0 -13 6 -13 13c0 4 2 7 4 9l176 177c2 3 7 5 11 5s7 -2 9 -5v0l178 -177c3 -2 4 -5 4 -9c0 -7 -5 -13 -12 -13v0h-357v0zM382 52v0c0 -7 -6 -13 -13 -13v0h-357v0c-7 1 -12 6 -12 13v1v49v0c0 7 5 12 12 13v0l357 1v0c7 0 13 -6 13 -13v0v0v-51z" />
|
||||
<glyph glyph-name="uniF145" unicode="" horiz-adv-x="493"
|
||||
d="M389 393c7 0 13 -6 13 -13v-376c0 -7 -6 -13 -13 -13v0v0h-376v0v0v0v0c-7 0 -13 6 -13 13v376c0 7 6 13 13 13v0h376v0v0zM51 341v-298h299v298h-299zM490 120c1 0 2 0 3 -1s0 -2 -1 -3l-31 -44c-1 -1 -1 -2 -2 -2v0c-1 0 -2 1 -3 2l-31 44c-1 1 -2 2 -1 3s2 1 3 1h15
|
||||
v40c0 2 1 3 3 3h27c2 0 3 -1 3 -3v-40h15zM427 264c-1 0 -2 0 -3 1s0 2 1 3l31 44c1 1 2 2 3 2v0c1 0 1 -1 2 -2l32 -44c1 -1 1 -2 0 -3s-2 -1 -3 -1h-15v-40c0 -2 -1 -3 -3 -3h-27c-2 0 -3 1 -3 3v40h-15zM109 284c0 17 8 26 25 26s25 -9 25 -26s-8 -25 -25 -25
|
||||
s-25 8 -25 25zM182 252c6 -1 11 -6 11 -12v-72v0c0 -3 -1 -5 -3 -7c-4 -4 -12 -4 -16 0c-2 2 -3 4 -3 7v0v50v0c0 2 -1 4 -3 4s-4 -2 -4 -4v0v-32v-33v-66c0 -7 -5 -13 -12 -13s-13 6 -13 13v66v0c0 2 -1 4 -3 4s-4 -2 -4 -4v0v-66c0 -7 -5 -13 -12 -13s-13 6 -13 13v99v32
|
||||
v0v0c0 2 -1 4 -3 4s-3 -2 -3 -4v0v0v-50v0c0 -3 -2 -5 -4 -7c-4 -4 -11 -4 -15 0c-2 2 -3 4 -3 7v0v72v0c0 7 5 12 12 12v0h91v0zM238 284c0 17 9 26 26 26s25 -9 25 -26s-8 -25 -25 -25s-26 8 -26 25zM312 252c6 -1 10 -6 10 -12v-72v0c0 -3 -1 -5 -3 -7
|
||||
c-4 -4 -11 -4 -15 0c-2 2 -3 4 -3 7v0v50v0c0 2 -1 4 -3 4s-4 -2 -4 -4v0v-32v-33v-66c0 -7 -5 -13 -12 -13s-13 6 -13 13v66v0c0 2 -1 4 -3 4s-4 -2 -4 -4v0v-66c0 -7 -5 -13 -12 -13s-13 6 -13 13v99v32v0v0c0 2 -1 4 -3 4s-4 -2 -4 -4v0v0v-50v0c0 -3 -1 -5 -3 -7
|
||||
c-4 -4 -11 -4 -15 0c-2 2 -3 4 -3 7v0v72v0c0 7 5 12 12 12v0h91v0z" />
|
||||
<glyph glyph-name="uniF146" unicode="" horiz-adv-x="283"
|
||||
d="M280 125c3 -2 3 -5 2 -8v0v0v0v0c-19 -34 -55 -67 -115 -67c-68 0 -122 38 -140 98h-21v-1c-3 0 -6 3 -6 6v21c0 3 3 6 6 6v0h15v12v13h-15v0c-3 0 -6 3 -6 6v21c0 3 3 6 6 6v0h21c19 59 73 96 140 96c60 0 96 -33 115 -67v0v0v0v0c1 -3 1 -6 -2 -8h-1v0l-40 -19v0
|
||||
c-3 -1 -5 -1 -7 2v0c-12 22 -37 40 -65 40c-34 0 -61 -17 -75 -44h100v0c3 0 6 -3 6 -6v-21c0 -3 -3 -6 -6 -6v0v0v0v0h-110c-1 -5 -1 -8 -1 -13v-12h111v0c3 0 6 -3 6 -6v-21c0 -3 -3 -6 -6 -6v0h-101c14 -28 41 -45 76 -45c28 0 52 17 64 39l1 1c2 3 4 3 7 2v0l40 -19v0h1
|
||||
z" />
|
||||
<glyph glyph-name="uniF147" unicode="" horiz-adv-x="365"
|
||||
d="M359 204c4 -2 6 -6 6 -11c0 -4 -2 -8 -4 -10v0v0l-1 -1l-79 -79v0c-24 -27 -60 -44 -99 -44c-35 0 -66 14 -90 36v-1l-87 87c-3 3 -5 7 -5 11c0 3 1 7 3 9v0l80 79c24 27 60 45 99 45c35 0 67 -14 91 -36v1zM182 102c50 0 90 40 90 90s-40 90 -90 90s-90 -40 -90 -90
|
||||
s40 -90 90 -90zM139 192c0 29 14 43 43 43s44 -14 44 -43s-15 -44 -44 -44s-43 15 -43 44z" />
|
||||
<glyph glyph-name="uniF148" unicode="" horiz-adv-x="372"
|
||||
d="M372 192c0 -3 -1 -5 -4 -7v-1l-192 -111c-2 -2 -5 -2 -7 -2c-5 0 -8 3 -8 8v78l-146 -84c-2 -2 -4 -2 -6 -2c-5 0 -9 3 -9 8v226v0c0 5 4 8 9 8c2 0 4 0 6 -2l146 -84v78v0c0 5 3 8 8 8c2 0 5 0 7 -2l191 -111v0c3 -1 5 -4 5 -8z" />
|
||||
<glyph glyph-name="uniF149" unicode="" horiz-adv-x="268"
|
||||
d="M229 163c-20 -20 -44 -32 -70 -37v-41h28v0c2 0 4 0 6 -2s2 -4 2 -6v-35c0 -4 -4 -8 -8 -8v0h-28v-34v0c0 -4 -4 -8 -8 -8v0h-35v0v0h-2v0c-4 1 -6 4 -6 8v0v34h-27v0c-4 0 -8 4 -8 8v0v35v0c0 2 0 4 2 6s4 2 6 2v0h27v41c-25 5 -50 18 -69 37c-52 52 -52 138 0 190
|
||||
s138 52 190 0s52 -138 0 -190zM134 175c46 0 83 37 83 83s-37 83 -83 83s-83 -37 -83 -83s37 -83 83 -83z" />
|
||||
<glyph glyph-name="uniF14A" unicode="" horiz-adv-x="227"
|
||||
d="M68 345c0 28 14 42 42 42s42 -14 42 -42s-14 -42 -42 -42s-42 14 -42 42zM227 171v0v-3c0 -3 -1 -6 -3 -9s-3 -4 -6 -6s-6 -3 -9 -3c-8 0 -14 4 -17 12v0l-20 76v0c-1 3 -3 5 -6 5c-4 0 -5 -2 -5 -6v-2v0l32 -122c1 -1 1 -2 1 -3l1 -1h-1h1c0 -6 -4 -10 -10 -10h-24v-81
|
||||
c0 -4 -1 -7 -3 -10s-5 -6 -8 -8s-6 -3 -10 -3c-6 0 -11 3 -15 7s-6 8 -6 14v81h-12v-81c0 -6 -2 -10 -6 -14s-8 -7 -14 -7s-11 3 -15 7s-6 8 -6 14v81h-24c-6 0 -10 4 -10 10v0v0v1c0 1 0 2 1 3l33 123v1v0v0v1v0c-1 3 -2 5 -5 5s-5 -2 -6 -5v0v0l-20 -76h-1
|
||||
c-3 -8 -8 -12 -16 -12c-5 0 -10 3 -13 6s-5 7 -5 12v3v0l28 105v0l1 2v0c3 9 8 13 18 13v1h132v-1v0c10 0 16 -4 19 -13v0l1 -2v0z" />
|
||||
<glyph glyph-name="uniF14B" unicode="" horiz-adv-x="410"
|
||||
d="M410 334c0 -10 -4 -19 -11 -25v0l-156 -155v-118v0v-2c0 -10 -7 -17 -17 -17c-3 0 -6 1 -9 2v0v0l-1 1l-40 23v0c-5 3 -9 9 -9 15v0v0v96l-155 154c-8 6 -12 16 -12 26c0 18 15 33 33 33h2v0h343v0c18 -1 32 -15 32 -33zM114 301v0v0v0z" />
|
||||
<glyph glyph-name="uniF14C" unicode="" horiz-adv-x="411"
|
||||
d="M100 308v45v1c0 7 6 12 13 12v0v0h185v0c7 0 12 -5 12 -12v0v-47h-31v27h-148v-26h-31zM76 18v262h259v-262h-259zM128 167v-37c0 -1 0 -1 1 -2s2 -1 3 -1h52v-53c0 -1 0 -2 1 -3s2 -1 3 -1h37c1 0 1 0 2 1s2 2 2 3v53h52c1 0 2 0 3 1s1 1 1 2v37c0 2 -2 4 -4 4h-52v52
|
||||
c0 2 -2 4 -4 4h-37c-2 0 -4 -2 -4 -4v-52h-52c-2 0 -4 -2 -4 -4zM20 280v0h31v-262h-31v0c-11 0 -20 9 -20 20v1v220v0c0 11 9 21 20 21zM411 38v0c0 -11 -9 -20 -20 -20v0v0h-31v262h31c11 0 20 -9 20 -20v0v-222z" />
|
||||
<glyph glyph-name="uniF14D" unicode="" horiz-adv-x="372"
|
||||
d="M364 325c4 0 8 -4 8 -8v-169c0 -2 -1 -5 -3 -6c-21 -22 -51 -36 -84 -36c-31 0 -58 11 -79 31c-23 27 -57 45 -96 45c-20 0 -39 -4 -56 -13v-145c0 -15 -12 -27 -27 -27s-27 12 -27 27v322c0 15 12 27 27 27c11 0 20 -6 24 -15c20 18 47 29 77 29c31 0 59 -11 80 -31
|
||||
c23 -27 56 -45 95 -45c21 0 41 5 58 14v0h3z" />
|
||||
<glyph glyph-name="uniF14E" unicode="" horiz-adv-x="425"
|
||||
d="M397 325c15 0 28 -13 28 -28v-248c0 -15 -13 -28 -28 -28h-369c-15 0 -28 13 -28 28v248c0 15 13 28 28 28h30l26 32c3 4 8 6 13 6h74c6 0 12 -5 15 -10c3 -4 13 -16 23 -28h188zM374 124v31c0 2 -1 4 -3 4h-45v45c0 2 -2 3 -4 3h-31c-2 0 -4 -1 -4 -3v-45h-45
|
||||
c-2 0 -3 -2 -3 -4v-31c0 -1 0 -1 1 -2s1 -1 2 -1h45v-45c0 -1 0 -2 1 -3s2 -1 3 -1h31c1 0 2 0 3 1s1 2 1 3v45h45c1 0 1 0 2 1s1 1 1 2z" />
|
||||
<glyph glyph-name="uniF14F" unicode="" horiz-adv-x="425"
|
||||
d="M312 181c9 0 17 -8 17 -17v0v0v0v-15h-34v15c0 9 8 17 17 17zM397 325c15 0 28 -13 28 -28v-248c0 -15 -13 -28 -28 -28h-369c-15 0 -28 13 -28 28v248c0 15 13 28 28 28h30l26 32c3 4 8 6 13 6h74c6 0 12 -5 15 -10c3 -4 13 -16 23 -28h188zM374 77v0v67c0 3 -2 5 -5 5
|
||||
h-6h-9v15v0c0 23 -19 42 -42 42s-41 -19 -41 -42v-15h-8h-6c-3 0 -5 -2 -5 -5v-67c0 -3 2 -5 5 -5h112c3 0 5 2 5 5z" />
|
||||
<glyph glyph-name="uniF150" unicode="" horiz-adv-x="425"
|
||||
d="M397 325c15 0 28 -13 28 -28v-248c0 -15 -13 -28 -28 -28h-369c-15 0 -28 13 -28 28v248c0 15 13 28 28 28h30l26 32c3 4 8 6 13 6h74c6 0 12 -5 15 -10c3 -4 13 -16 23 -28h188z" />
|
||||
<glyph glyph-name="uniF151" unicode="" horiz-adv-x="264"
|
||||
d="M0 264c0 15 7 22 22 22s22 -7 22 -22s-7 -22 -22 -22s-22 7 -22 22zM168 368c0 30 15 45 45 45s45 -15 45 -45s-15 -44 -45 -44s-45 14 -45 44zM92 354c0 19 9 28 28 28s28 -9 28 -28s-9 -28 -28 -28s-28 9 -28 28zM249 69v0c9 -11 13 -24 13 -38c0 -17 -5 -31 -17 -43
|
||||
s-26 -17 -43 -17c-13 0 -25 3 -35 11s-18 18 -22 30l-76 131l1 1c-5 8 -9 16 -11 25c-3 11 -5 22 -5 33c0 29 10 53 31 74s45 31 74 31c19 0 37 -5 53 -14s29 -23 38 -39s14 -33 14 -52c0 -21 -5 -41 -17 -59v0c-6 -11 -9 -23 -9 -35c0 -14 3 -27 11 -39zM39 311
|
||||
c0 15 7 23 22 23s22 -8 22 -23s-7 -22 -22 -22s-22 7 -22 22z" />
|
||||
<glyph glyph-name="uniF152" unicode="" horiz-adv-x="356"
|
||||
d="M325 59c1 0 1 -1 1 -2v-1l-6 -12c0 -1 -1 -1 -2 -1h-1l-16 8c-1 0 -1 1 -1 2s1 0 1 1l6 12c1 1 2 2 3 1zM325 328c1 0 1 -1 1 -2v-1l-7 -11c0 -1 0 -2 -1 -2l-2 1l-15 9c-1 0 -1 0 -1 1v2l7 11c1 1 2 2 3 1zM295 75c1 -1 2 -2 1 -3l-7 -12c0 -1 -1 -1 -2 -1h-1l-4 3l-5 2
|
||||
l-62 -34v0v0c-1 -1 -4 -1 -5 -1s-3 0 -4 1v0v0l-63 34l-62 -34v0h-1c-1 -1 -3 -1 -4 -1s-3 0 -4 1h-1v0l-67 37c-3 1 -4 3 -4 6v274c0 3 2 6 5 7s6 0 9 -1l62 -34l62 34h1v1h2h2h2h2l1 -1v0l62 -34l62 34c2 2 6 3 9 1l7 -5l7 -3c1 0 1 -1 1 -2s-1 0 -1 -1l-6 -12
|
||||
c0 -1 -1 -1 -2 -1l-1 1l-9 5v-252zM76 46v256l-58 31v-256zM210 46v256l-67 36v-256zM354 160c1 0 2 -2 2 -3v-27c0 -1 -1 -2 -2 -2h-13c-1 0 -3 1 -3 2v27c0 1 2 3 3 3h13zM354 52c1 0 2 -1 2 -2v-10c0 -8 -5 -11 -10 -11c-2 0 -4 1 -6 2l-8 4c-1 0 -1 1 -1 2v1l6 12l1 1
|
||||
l2 1h1h13zM349 314c0 0 7 -4 7 -12v-10c0 -1 -1 -2 -2 -2h-13c-1 0 -3 1 -3 2v8l-6 4c-1 0 -1 0 -1 1v2l6 11c1 1 2 2 3 1zM354 267c1 0 2 -1 2 -2v-27c0 -1 -1 -2 -2 -2h-13c-1 0 -3 1 -3 2v27c0 1 2 2 3 2h13zM354 106c1 0 2 -1 2 -2v-27c0 -1 -1 -3 -2 -3h-13
|
||||
c-1 0 -3 2 -3 3v27c0 1 2 2 3 2h13zM354 213c1 0 2 -1 2 -2v-27c0 -1 -1 -2 -2 -2h-13c-1 0 -3 1 -3 2v27c0 1 2 2 3 2h13z" />
|
||||
<glyph glyph-name="uniF153" unicode="" horiz-adv-x="427"
|
||||
d="M193 140c4 0 8 -4 8 -8v-112c0 -4 -4 -8 -8 -8h-72c-4 0 -8 4 -8 8v112c0 4 4 8 8 8h72zM306 372c4 0 8 -4 8 -8v-344c0 -4 -4 -8 -8 -8h-72c-4 0 -8 4 -8 8v344c0 4 4 8 8 8h72zM420 267c4 0 7 -3 7 -7v-240c0 -4 -3 -8 -7 -8h-73c-4 0 -8 4 -8 8v240c0 4 4 7 8 7h73z
|
||||
M80 267c4 0 8 -3 8 -7v-240c0 -4 -4 -8 -8 -8h-72c-4 0 -8 4 -8 8v240c0 4 4 7 8 7h72z" />
|
||||
<glyph glyph-name="uniF154" unicode="" horiz-adv-x="387"
|
||||
d="M380 238c4 0 7 -4 7 -8v-77c0 -4 -3 -8 -7 -8h-372c-4 0 -8 4 -8 8v77c0 4 4 8 8 8h372zM226 357c3 -1 5 -4 5 -8v-77c0 -4 -2 -7 -5 -8v0h-218c-4 0 -8 4 -8 8v77c0 4 4 8 8 8h218v0zM275 120c4 0 8 -4 8 -8v-77c0 -4 -4 -8 -8 -8h-267c-4 0 -8 4 -8 8v77c0 4 4 8 8 8
|
||||
h267z" />
|
||||
<glyph glyph-name="uniF155" unicode="" horiz-adv-x="380"
|
||||
d="M212 382c93 -1 167 -76 168 -169c0 -4 -2 -6 -6 -6h-1v0h-161c-4 0 -7 2 -7 6v161v0v1c0 4 3 7 7 7zM341 174v0v-2c0 -94 -76 -170 -170 -170s-171 76 -171 170s77 171 171 171h1v0h1c4 0 7 -3 7 -7l-1 -1h1v-147c0 -4 2 -7 6 -7h148c4 0 7 -3 7 -7z" />
|
||||
<glyph glyph-name="uniF156" unicode="" horiz-adv-x="410"
|
||||
d="M384 37h-359c-14 0 -25 11 -25 25v260c0 14 11 25 25 25h359c14 0 26 -11 26 -25v-260c0 -14 -12 -25 -26 -25zM359 87v210h-309v-210h309zM162 111c-5 0 -10 2 -13 5l-25 25l-31 -25c-8 -6 -19 -6 -25 2s-6 19 2 25l44 36c7 6 17 6 24 -1l21 -21l46 72c4 5 9 8 16 8
|
||||
s12 -4 15 -10l27 -58l52 92c5 9 16 12 25 7s12 -15 7 -24l-70 -123c-3 -6 -9 -9 -16 -9s-13 5 -16 11l-28 59l-40 -63c-3 -5 -8 -7 -13 -8h-2z" />
|
||||
<glyph glyph-name="uniF157" unicode="" horiz-adv-x="399"
|
||||
d="M148 320c-3 2 -5 5 -5 9c0 6 4 10 10 10c3 0 6 -1 8 -3l60 -60c3 -2 5 -5 5 -9c0 -6 -5 -10 -11 -10c-2 0 -4 1 -6 2v0h-1v1zM215 111h-25c-14 0 -28 0 -41 1l24 -42c2 -3 4 -7 4 -11c0 -10 -8 -18 -18 -18c-6 0 -11 3 -14 7v0l-38 65l-37 -65h-1c-3 -4 -8 -7 -14 -7
|
||||
c-10 0 -18 8 -18 18c0 4 2 8 4 11l32 56c-8 6 -14 29 -14 56c0 13 2 24 4 34l-57 33v0c-4 2 -6 6 -6 11c0 7 6 13 13 13c2 0 4 0 6 -1v0l56 -33c1 0 2 1 3 1h137v-129zM306 125l31 -55c2 -3 4 -7 4 -11c0 -10 -8 -18 -18 -18c-6 0 -11 3 -14 7h-1l-37 65l-38 -65v0
|
||||
c-3 -4 -8 -7 -14 -7c-10 0 -18 8 -18 18c0 4 2 8 4 11l31 54v116l83 -83c-2 -17 -7 -29 -13 -32zM399 235c0 -4 -2 -8 -4 -10v0l-11 -11v0h-10h-40l-14 -14c1 -5 1 -11 1 -16l-72 72l42 42l26 45l13 -22c3 0 5 -2 7 -3v1l26 -15c7 -3 11 -10 11 -18v-21l20 -20
|
||||
c3 -2 5 -6 5 -10z" />
|
||||
<glyph glyph-name="uniF158" unicode="" horiz-adv-x="321"
|
||||
d="M148 302c58 0 104 -48 104 -106c0 -13 -2 -24 -6 -36c0 -1 -1 -2 -1 -3c0 0 -15 -26 -25 -38c-1 -2 -3 -3 -4 -5c-7 -8 -10 -11 -10 -24c1 -27 1 -37 -1 -45c-4 -26 -23 -39 -55 -40v0v0c-9 0 -17 7 -17 16c0 5 1 10 4 13s7 5 12 5c21 1 22 7 23 11v2c1 5 0 21 0 37
|
||||
c-1 26 10 38 18 48l4 4c6 7 17 24 21 32c2 7 3 15 3 23c0 39 -31 72 -70 72s-71 -33 -71 -72c0 -9 -8 -17 -17 -17s-17 8 -17 17c0 58 47 106 105 106zM172 194c0 13 -11 24 -24 24s-24 -11 -24 -24c0 -8 -7 -15 -15 -15s-14 7 -14 15c0 29 24 52 53 52s52 -23 52 -52
|
||||
c0 -8 -6 -15 -14 -15s-14 7 -14 15zM75 142c6 6 16 6 22 0c3 -3 5 -8 5 -12s-2 -8 -5 -11l-70 -71c-3 -3 -7 -4 -11 -4s-8 1 -11 4s-5 8 -5 12s2 8 5 11zM207 334c38 0 69 -30 69 -68v0v-7v0c0 -3 -3 -6 -6 -6v0h-12v0c-3 0 -6 3 -6 6v0v7v0c0 24 -21 44 -45 44v0v0h-7v0
|
||||
c-3 0 -5 3 -5 6v0v11v0c0 3 2 7 5 7v0h7v0v0zM321 266v0v-7v0c0 -3 -3 -6 -6 -6v0h-11v0c-3 0 -7 3 -7 6v0v7v0c0 49 -41 89 -90 89v0v0h-7v0c-3 0 -5 3 -5 6v0v12v0c0 3 2 6 5 6v0h7v0v0c63 0 114 -50 114 -113z" />
|
||||
<glyph glyph-name="uniF159" unicode="" horiz-adv-x="409"
|
||||
d="M295 379c63 0 114 -51 114 -114c0 -68 -33 -108 -59 -140l-6 -7c-31 -38 -128 -108 -132 -111c-2 -2 -5 -2 -8 -2s-5 0 -7 2c-4 3 -101 73 -132 111l-6 7c-26 32 -59 72 -59 140c0 63 51 114 114 114c36 0 69 -16 90 -44c21 28 55 44 91 44z" />
|
||||
<glyph glyph-name="uniF15A" unicode="" horiz-adv-x="353"
|
||||
d="M348 254c3 -2 5 -6 5 -10v-233c0 -7 -6 -13 -13 -13h-96c-7 0 -12 6 -12 13v143h-111v-143c0 -7 -5 -13 -12 -13h-96c-7 0 -13 6 -13 13v233c0 4 2 8 5 10l161 129c5 4 11 4 16 0z" />
|
||||
<glyph glyph-name="uniF15B" unicode="" horiz-adv-x="300"
|
||||
d="M0 362v0h300l-27 -306l-123 -34l-123 34zM243 289l1 11h-94v0h-94l1 -11l9 -103h84v0h46l-4 -49l-42 -11v0v0l-42 11l-3 30h-20h-17l5 -59l77 -21v0v0l77 21v7l9 98l1 11h-10h-77v0h-50l-3 38h53v0h90h1v8z" />
|
||||
<glyph glyph-name="uniF15C" unicode="" horiz-adv-x="355"
|
||||
d="M337 370c10 0 18 -7 18 -17v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM337 64c10 0 18 -7 18 -17v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM355 149v0v-16v0c0 -10 -8 -17 -18 -17v0h-198
|
||||
c-10 0 -18 7 -18 17v16c0 10 8 17 18 17h198v0v0c10 0 18 -7 18 -17zM355 250v0v-16v0c0 -10 -8 -17 -18 -17v0h-198c-10 0 -18 7 -18 17v16c0 10 8 18 18 18h198v0v0c10 0 18 -8 18 -18zM0 192c0 1 0 1 1 2v1l64 36c1 1 1 1 2 1c2 0 3 -1 3 -3v-74v0c0 -2 -1 -3 -3 -3
|
||||
c-1 0 -1 0 -2 1l-63 36v0c-1 0 -2 2 -2 3z" />
|
||||
<glyph glyph-name="uniF15D" unicode="" horiz-adv-x="355"
|
||||
d="M337 370c10 0 18 -7 18 -17v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM337 64c10 0 18 -7 18 -17v-16c0 -10 -8 -17 -18 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM355 149v0v-16v0c0 -10 -8 -17 -18 -17v0h-199
|
||||
c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h199v0c10 0 18 -7 18 -17zM355 250v0v-16v0c0 -10 -8 -17 -18 -17v0h-199c-10 0 -17 7 -17 17v16c0 10 7 18 17 18h199v0c10 0 18 -8 18 -18zM3 152c-2 0 -3 1 -3 3v74v0c0 2 1 3 3 3c1 0 1 0 2 -1l63 -36v0c1 0 2 -2 2 -3
|
||||
s0 -1 -1 -2v-1l-64 -36c-1 -1 -1 -1 -2 -1z" />
|
||||
<glyph glyph-name="uniF15E" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM212 85v124c0 4 -3 7 -7 7h-26c-4 0 -7 -3 -7 -7v-124c0 -4 3 -6 7 -6h26c4 0 7 2 7 6zM192 244c13 0 23 10 23 23s-10 23 -23 23s-23 -10 -23 -23s10 -23 23 -23z" />
|
||||
<glyph glyph-name="uniF15F" unicode="" horiz-adv-x="108"
|
||||
d="M108 324v-4v0l-58 -261v0c-1 -7 -6 -12 -13 -12h-25v0c-7 1 -12 6 -12 13v3v0v0v0l58 261v0c0 7 6 13 13 13h24c7 0 13 -6 13 -13z" />
|
||||
<glyph glyph-name="uniF160" unicode="" horiz-adv-x="445"
|
||||
d="M445 218v0v0v-32v0c0 -2 0 -5 -2 -7s-5 -2 -7 -2v0h-27h-1v-61v0v-39v0c0 -5 -4 -9 -9 -9h-1h-31v0c-5 0 -10 4 -10 9v0v21v0v79h-25v-26v0v-39v0c0 -5 -5 -9 -10 -9v0h-31v0h-1c-5 0 -9 4 -9 9v0v16v23v26h-45c-10 -56 -58 -99 -117 -99c-66 0 -119 53 -119 119
|
||||
s53 119 119 119c55 0 102 -38 115 -89h163h12h27c2 0 5 -1 7 -3s2 -4 2 -6zM119 128c38 0 68 30 68 68s-30 68 -68 68s-68 -30 -68 -68s30 -68 68 -68z" />
|
||||
<glyph glyph-name="uniF161" unicode="" horiz-adv-x="488"
|
||||
d="M60 101c-14 0 -26 12 -26 26v226c0 14 12 25 26 25h368c14 0 25 -11 25 -25v-226c0 -14 -11 -26 -25 -26h-368zM85 327v-175h318v175h-318zM476 76c6 0 12 -6 12 -12v-28c0 -3 -2 -7 -4 -9l-18 -18c-2 -2 -5 -3 -8 -3h-430c-3 0 -6 2 -8 4l-17 18c-2 2 -3 5 -3 8v28
|
||||
c0 6 6 12 12 12h464zM288 35v12c0 1 -1 2 -2 2h-84c-1 0 -3 -1 -3 -2v-12c0 -1 2 -3 3 -3h84c1 0 2 2 2 3z" />
|
||||
<glyph glyph-name="uniF162" unicode="" horiz-adv-x="381"
|
||||
d="M76 173v119h229v-119h-229zM82 92v56h57v-56h-57zM246 92v56h57v-56h-57zM164 92v56h57v-56h-57zM358 368c13 0 23 -10 23 -23v-307v0c-1 -12 -11 -22 -23 -22h-335v0v0c-12 0 -22 10 -23 22v0v307v0c0 13 10 23 23 23v0h335v0zM330 67v0v250h-279v-250h279z" />
|
||||
<glyph glyph-name="uniF163" unicode="" horiz-adv-x="426"
|
||||
d="M368 351v0l-45 -45c-3 -3 -7 -3 -10 0v0l-11 11v0c-3 3 -3 7 0 10l45 45v0c3 3 8 3 11 0v0v0l10 -10v0c3 -3 3 -8 0 -11v0v0v0zM206 349c-4 0 -8 3 -8 7v0v64c0 4 4 8 8 8v0h14v0c4 0 8 -4 8 -8v-64v0c0 -4 -4 -7 -8 -7v0h-14v0zM426 243v0v-15v0c0 -4 -3 -7 -7 -7v0h-64
|
||||
c-4 0 -7 3 -7 7v0v15v0c0 4 3 8 7 8h64v0c4 0 7 -4 7 -8zM213 324c60 0 109 -49 109 -109c0 -22 -7 -42 -18 -59c-18 -26 -28 -60 -29 -99c-1 -2 -4 -5 -7 -5h-1v0h-108v0h-1c-3 0 -5 2 -6 4c-1 37 -12 71 -28 96v0c-13 18 -20 39 -20 63c0 60 49 109 109 109zM268 -12
|
||||
c4 0 7 -3 7 -7v-17c0 -4 -3 -8 -7 -8h-1v0h-108v0h-1c-4 0 -8 4 -8 8v17c0 4 4 7 8 7h1v0h108v0h1zM268 37c4 0 7 -4 7 -8v-17c0 -4 -3 -8 -7 -8l-1 1v-1h-108v1l-1 -1c-4 0 -8 4 -8 8v17v0c0 4 4 8 8 8h1v0h108v0h1zM71 245c4 0 8 -4 8 -8v0v-14v0c0 -4 -4 -8 -8 -8v0h-64
|
||||
c-4 0 -7 4 -7 8v14c0 4 3 8 7 8h64v0zM60 354c-3 3 -3 7 0 10v0l11 11v0v0c3 3 8 3 11 0l45 -46v0c3 -3 3 -7 0 -10v0l-11 -10v0c-3 -3 -7 -4 -10 -1v0z" />
|
||||
<glyph glyph-name="uniF164" unicode="" horiz-adv-x="333"
|
||||
d="M333 67c0 -7 -6 -13 -13 -13v0v0v0v0h-26v1c-7 0 -11 5 -12 11v0v141v0c0 7 5 13 12 13v0h25h1c7 0 12 -6 12 -13v0v0v-138c0 -1 1 -1 1 -2zM256 46v0v-1v0v0c0 -7 -6 -12 -13 -12h-1h-185c-2 0 -4 0 -5 1v0l-50 50v0c-1 1 -2 3 -2 5v0v144v0c0 7 6 13 13 13v0h70v58v1
|
||||
v17v0v0c0 16 13 29 29 29c11 0 21 -6 26 -15v0l51 -87c1 -1 1 -2 2 -3h51h1c7 0 13 -6 13 -13v0v0v-144v0v-43zM15 89l1 -1l-1 1v0z" />
|
||||
<glyph glyph-name="uniF165" unicode="" horiz-adv-x="353"
|
||||
d="M323 339c39 -39 40 -101 4 -141v0l-52 -52c-9 -9 -20 -16 -31 -21c-4 -10 -10 -20 -18 -28v0l-51 -52c-40 -40 -105 -40 -145 0s-40 105 0 145l52 52v-1c8 8 17 14 27 18c5 11 13 23 22 32l52 51v0c40 36 101 36 140 -3zM140 82l37 38c-17 4 -33 13 -46 26s-23 30 -27 47
|
||||
l-37 -38v0c-20 -20 -20 -53 0 -73s53 -20 73 0v0zM168 183c9 -9 21 -14 33 -15c-1 12 -6 24 -15 33s-21 14 -33 15c1 -12 6 -24 15 -33zM290 232v0c17 20 16 51 -3 70s-50 20 -70 3v0l-3 -3v0v0l-38 -38c17 -4 33 -13 46 -26s23 -30 27 -47l38 38v0v0z" />
|
||||
<glyph glyph-name="uniF166" unicode="" horiz-adv-x="431"
|
||||
d="M414 345c10 0 17 -8 17 -18v-16c0 -10 -7 -17 -17 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 18 17 18h320zM414 217c10 0 17 -7 17 -17v-16c0 -10 -7 -17 -17 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 17 17 17h320zM414 90c10 0 17 -7 17 -17v-16c0 -10 -7 -18 -17 -18
|
||||
h-320c-10 0 -17 8 -17 18v16c0 10 7 17 17 17h320zM0 319c0 17 9 26 26 26s25 -9 25 -26s-8 -25 -25 -25s-26 8 -26 25zM0 197c0 17 9 26 26 26s25 -9 25 -26s-8 -25 -25 -25s-26 8 -26 25zM0 65c0 17 9 25 26 25s25 -8 25 -25s-8 -26 -25 -26s-26 9 -26 26z" />
|
||||
<glyph glyph-name="uniF167" unicode="" horiz-adv-x="423"
|
||||
d="M29 333l-9 -9l-6 7l17 17h10v-55h-12v40zM21 210c-6 0 -10 -2 -14 -6l-7 8c5 6 13 9 21 9c12 0 21 -7 21 -18c0 -9 -8 -18 -22 -28h22v-10h-41v9c22 17 29 22 29 29c0 5 -4 7 -9 7zM30 65c6 -1 13 -5 13 -13c0 -9 -8 -16 -21 -16c-10 0 -18 4 -22 9l6 8c4 -4 10 -7 15 -7
|
||||
c7 0 11 3 11 7s-3 7 -11 7h-7v10h7c6 0 10 2 10 6s-4 6 -10 6c-5 0 -10 -1 -14 -5l-6 7c4 5 11 9 21 9c13 0 20 -6 20 -15c0 -7 -6 -12 -12 -13zM406 346c10 0 17 -7 17 -17v-16c0 -10 -7 -18 -17 -18h-320c-10 0 -17 8 -17 18v16c0 10 7 17 17 17h320zM406 219
|
||||
c10 0 17 -8 17 -18v-16c0 -10 -7 -17 -17 -17h-320c-10 0 -17 7 -17 17v16c0 10 7 18 17 18h320zM406 91c10 0 17 -7 17 -17v-16c0 -10 -7 -18 -17 -18h-320c-10 0 -17 8 -17 18v16c0 10 7 17 17 17h320z" />
|
||||
<glyph glyph-name="uniF168" unicode="" horiz-adv-x="332"
|
||||
d="M321 103c6 0 11 -6 11 -12v-1v-53c0 -6 -5 -11 -11 -11v0h-182v0c-6 0 -11 5 -11 11v0v53v1c0 6 5 12 11 12v0v0h182v0zM65 103c6 0 12 -6 12 -12v0v0v-54v0c0 -6 -6 -11 -12 -11v0h-53v0v0c-6 0 -12 5 -12 11v0v54v0v0c0 6 6 12 12 12v0v0h53v0zM321 231
|
||||
c6 0 11 -6 11 -12v-1v-53c0 -6 -5 -11 -11 -11v0h-182v0c-6 0 -11 5 -11 11v0v53v1c0 6 5 12 11 12v0v0h182v0zM65 231c6 0 12 -6 12 -12v0v0v-54v0c0 -6 -6 -11 -12 -11v0h-53v0v0c-6 0 -12 5 -12 11v0v54v0v0c0 6 6 12 12 12v0v0h53v0zM128 293v0v53v1c0 6 5 11 11 11v0v0
|
||||
h182v0c6 0 11 -5 11 -11v-1v-53c0 -6 -5 -12 -11 -12v0h-182v0c-6 0 -11 6 -11 12v0zM65 358c6 0 12 -5 12 -11v0v0v-54v0c0 -6 -6 -12 -12 -12v0h-53v0v0c-6 0 -12 6 -12 12v0v54v0v0c0 6 6 11 12 11v0v0h53v0z" />
|
||||
<glyph glyph-name="uniF169" unicode="" horiz-adv-x="332"
|
||||
d="M0 293v53v1c0 6 6 11 12 11v0v0h309v0c6 0 11 -5 11 -11v0v-54v0c0 -6 -5 -12 -11 -12v0h-309v0c-6 0 -12 6 -12 12v0zM321 230c6 0 11 -5 11 -11v0v-54v0c0 -6 -5 -11 -11 -11v0h-309v0c-6 0 -12 5 -12 11v0v53v1c0 6 6 11 12 11v0v0h309v0zM321 103c6 0 11 -6 11 -12v0
|
||||
v-54v0c0 -6 -5 -11 -11 -11v0h-309v0c-6 0 -12 5 -12 11v0v53v1c0 6 6 12 12 12v0v0h309v0z" />
|
||||
<glyph glyph-name="uniF16A" unicode="" horiz-adv-x="356"
|
||||
d="M342 222c7 0 14 -7 14 -14v-197c0 -7 -7 -14 -14 -14h-329c-7 0 -13 7 -13 14v197c0 7 6 14 13 14h19h22v43c0 67 54 122 121 122s122 -55 122 -122v0v-43h26h19zM126 265v-43h99v43v1v0v0c0 27 -23 49 -50 49c-28 0 -49 -22 -49 -50z" />
|
||||
<glyph glyph-name="uniF16B" unicode="" horiz-adv-x="346"
|
||||
d="M346 327v-137c0 -2 -1 -5 -3 -6s-5 -2 -7 -1l-130 44c-3 1 -5 3 -5 6s1 6 3 8l30 21l5 4c-18 14 -41 23 -66 23c-40 0 -75 -22 -93 -55v0c-4 -6 -12 -9 -18 -5l-42 24v0c-6 3 -8 11 -5 17v0c31 56 90 93 158 93c50 0 95 -20 128 -53l2 2l31 22c2 2 5 1 8 0s4 -4 4 -7z
|
||||
M326 131c6 -3 9 -11 6 -17v0c-31 -56 -91 -93 -159 -93c-50 0 -94 20 -127 53l-3 -2l-30 -22c-2 -2 -5 -1 -8 0s-5 4 -5 7v137c0 2 1 5 3 6s5 2 7 1l130 -44c3 -1 6 -3 6 -6s-1 -6 -3 -8l-31 -21l-5 -4c18 -14 41 -23 66 -23c40 0 76 22 94 55v0c4 6 12 9 18 5l41 -24v0z
|
||||
" />
|
||||
<glyph glyph-name="uniF16C" unicode="" horiz-adv-x="397"
|
||||
d="M233 395c91 0 164 -74 164 -164s-73 -164 -164 -164c-26 0 -51 6 -73 17l-82 -82v1c-8 -8 -20 -14 -33 -14c-25 0 -45 20 -45 45c0 13 6 25 14 33h-1l80 79c-15 25 -24 54 -24 85c0 90 73 164 164 164zM234 132c57 0 103 45 103 102s-46 102 -103 102s-102 -45 -102 -102
|
||||
s45 -102 102 -102z" />
|
||||
<glyph glyph-name="uniF16D" unicode="" horiz-adv-x="394"
|
||||
d="M381 345c7 0 13 -6 13 -13v-24c0 -2 -1 -2 -2 -3s-4 -2 -5 -3l-188 -110c-1 0 -1 -1 -2 -1s0 1 -1 1l-194 110c-1 1 -2 2 -2 3v27c0 7 6 13 13 13h368zM393 265c1 -1 1 -1 1 -2v-177c0 -1 -1 -3 -2 -3l-1 -1c-1 0 -1 0 -2 1l-107 115c-1 1 0 2 0 3s0 2 1 2l106 62
|
||||
c1 1 3 1 4 0zM253 182l127 -137c1 -1 1 -3 0 -4s-2 -2 -3 -2h-364c-2 0 -4 1 -6 2c-1 1 -2 2 -2 3s0 2 1 3l142 131c1 1 3 2 4 1l38 -22c5 -3 11 -2 16 1l42 25c1 1 4 0 5 -1zM117 194l-112 -103c-1 -1 -1 -1 -2 -1h-1c-1 1 -2 2 -2 3v167c0 1 1 2 2 3s2 1 3 0l111 -64
|
||||
c1 -1 2 -1 2 -2s0 -2 -1 -3z" />
|
||||
<glyph glyph-name="uniF16E" unicode="" horiz-adv-x="466"
|
||||
d="M49 347c0 28 14 42 42 42s43 -14 43 -42s-15 -42 -43 -42s-42 14 -42 42zM172 293c5 -1 9 -2 12 -6s6 -8 6 -13v-120v0c0 -5 -3 -10 -6 -13s-7 -5 -12 -5s-10 2 -13 5s-5 8 -5 13v0v82v1c0 4 -2 6 -6 6s-6 -2 -6 -6v-1v-53v-55v-110c0 -6 -2 -11 -6 -15s-8 -6 -14 -6
|
||||
s-11 2 -15 6s-6 9 -6 15v110v0c0 4 -2 6 -6 6s-6 -2 -6 -6v0v-110c0 -6 -2 -11 -6 -15s-9 -6 -15 -6s-11 2 -15 6s-6 9 -6 15v165v53v1v0c0 4 -1 6 -5 6s-6 -2 -6 -6v0v-1v-82v0c0 -5 -2 -10 -5 -13s-8 -5 -13 -5s-10 2 -13 5s-5 8 -5 13v0v120v0c0 5 2 10 6 14s9 5 14 5v0
|
||||
h152v0zM305 347c0 28 14 42 42 42s43 -14 43 -42s-15 -43 -43 -43s-42 15 -42 43zM466 171v0v-3c0 -3 0 -6 -2 -9s-4 -5 -7 -7s-6 -2 -9 -2c-8 0 -14 3 -17 11v0l-21 78v0c-1 3 -2 4 -5 4c-4 0 -6 -1 -6 -5v-2v0l33 -124c1 -1 1 -3 1 -4h1h-1h1c0 -6 -4 -10 -10 -10h-25v-82
|
||||
c0 -6 -2 -11 -6 -15s-9 -6 -15 -6s-11 2 -15 6s-6 9 -6 15v82h-12v-82c0 -4 -1 -8 -3 -11s-4 -6 -7 -8s-7 -2 -11 -2c-6 0 -11 2 -15 6s-6 9 -6 15v82h-24c-6 0 -10 4 -10 10v0v0v0c0 1 0 3 1 4l33 124v1v1v0v1v0c-1 3 -2 4 -5 4s-5 -1 -6 -4v0v0l-21 -78v0
|
||||
c-3 -8 -9 -11 -17 -11c-5 0 -9 1 -13 5s-5 8 -5 13v3v0l29 106v0v2v0c3 9 9 14 19 14v0h134v0v0c10 0 16 -5 19 -14v0l1 -2v0z" />
|
||||
<glyph glyph-name="uniF16F" unicode="" horiz-adv-x="342"
|
||||
d="M342 354v0v-106v0c0 -4 -4 -8 -8 -8h-35c-4 0 -8 4 -8 8v28l-46 -46c14 -21 23 -46 23 -74c0 -74 -60 -134 -134 -134s-134 60 -134 134s60 134 134 134c28 0 54 -9 76 -24l44 45h-26v0c-4 0 -8 4 -8 8v0v0v35c0 4 4 8 8 8v0h106v0c4 0 8 -4 8 -8v0zM134 73
|
||||
c46 0 83 37 83 83s-37 83 -83 83s-83 -37 -83 -83s37 -83 83 -83z" />
|
||||
<glyph glyph-name="uniF170" unicode="" horiz-adv-x="190"
|
||||
d="M49 346c0 28 14 42 42 42s43 -14 43 -42s-15 -42 -43 -42s-42 14 -42 42zM172 292c5 -1 9 -2 12 -6s6 -9 6 -14v-120v0c0 -5 -3 -9 -6 -12s-7 -5 -12 -5s-10 2 -13 5s-5 7 -5 12v0v83v1c0 4 -2 6 -6 6s-6 -2 -6 -6v-1v-53v-55v-110c0 -6 -2 -11 -6 -15s-8 -6 -14 -6
|
||||
s-11 2 -15 6s-6 9 -6 15v110v0c0 4 -2 6 -6 6s-6 -2 -6 -6v0v-110c0 -6 -2 -11 -6 -15s-9 -6 -15 -6s-11 2 -15 6s-6 9 -6 15v165v53v1v0c0 4 -1 6 -5 6s-6 -2 -6 -6v0v-1v-83v0c0 -5 -2 -9 -5 -12s-8 -5 -13 -5s-10 2 -13 5s-5 7 -5 12v0v120v0c0 4 1 7 3 10s4 6 7 8
|
||||
s6 2 10 2v0h152v0z" />
|
||||
<glyph glyph-name="uniF171" unicode="" horiz-adv-x="430"
|
||||
d="M430 407v-430v0v-1v-2h-1c-1 -6 -6 -11 -13 -11v0h-402v0c-8 0 -14 6 -14 14v0v0v283v0l161 161v0v0v0v0h255v0v0v0v0c8 0 14 -6 14 -14v0zM305 370l-19 -33l93 -92v125h-74zM161 246h-110v-31l86 -85l139 240h-101v-110v0v0c0 -8 -6 -14 -14 -14v0zM51 14h19l53 93
|
||||
l-72 72v-165zM99 14h280v194l-106 106zM276 215c34 0 62 -28 62 -62c0 -14 -4 -27 -12 -37l-39 -67c0 -1 -1 -2 -2 -3v0v0c-2 -2 -5 -4 -8 -4c-4 0 -8 1 -10 4v0v1v1l-40 69c-7 10 -12 22 -12 36c0 34 27 62 61 62zM276 122c17 0 30 13 30 30s-13 31 -30 31s-30 -14 -30 -31
|
||||
s13 -30 30 -30z" />
|
||||
<glyph glyph-name="uniF172" unicode="" horiz-adv-x="289"
|
||||
d="M144 395c80 0 145 -65 145 -145c0 -33 -12 -63 -30 -87l-90 -157c-1 -2 -3 -4 -4 -6l-1 -2v1c-5 -6 -12 -10 -20 -10c-9 0 -16 4 -21 11v0v0c-1 1 -1 2 -2 3l-92 161c-18 24 -29 54 -29 86c0 80 64 145 144 145zM143 177c39 0 71 32 71 71s-32 71 -71 71s-71 -32 -71 -71
|
||||
s32 -71 71 -71z" />
|
||||
<glyph glyph-name="uniF173" unicode="" horiz-adv-x="385"
|
||||
d="M76 302c8 0 14 -6 14 -14v-127v0c-1 -7 -7 -12 -14 -12c-42 0 -76 35 -76 77s34 76 76 76zM385 364v0v0v-24v-236v-23v0v-2c0 -8 -6 -14 -14 -14c-4 0 -7 2 -9 4l-134 77h-98c-7 0 -13 6 -14 13v0v132c0 8 6 14 14 14h110l123 70c2 2 5 3 8 3c8 0 14 -6 14 -14zM224 41v0
|
||||
c3 -6 2 -13 -3 -17v-1l-26 -14v0c-1 -1 -1 -2 -2 -2c-6 -4 -14 -1 -18 5v0l-1 1v0v0l-49 85v1s-1 0 -1 1c-4 7 -2 15 5 19c2 1 4 2 6 2v0h37v0c4 0 7 -3 9 -6v0l43 -73v-1v0v0z" />
|
||||
<glyph glyph-name="uniF174" unicode="" horiz-adv-x="261"
|
||||
d="M261 196c0 -65 -48 -119 -111 -128v-28h68v0c11 0 19 -8 19 -19s-8 -20 -19 -20v0h-175v0c-11 0 -20 9 -20 20s9 19 20 19v0h68v28c-63 9 -111 63 -111 128v0v74c0 11 8 19 19 19s20 -8 20 -19v-73v0c0 -51 40 -92 91 -92s92 41 92 92v73c0 11 8 19 19 19s20 -8 20 -19
|
||||
v-73v0v0v-1v0zM65 197v2v0v119v0c1 36 29 65 65 65s65 -29 66 -65v0v-119v0v-2c0 -36 -30 -65 -66 -65s-65 29 -65 65z" />
|
||||
<glyph glyph-name="uniF175" unicode="" horiz-adv-x="384"
|
||||
d="M281 219c3 0 5 -2 5 -5v-44c0 -3 -2 -5 -5 -5h-178c-3 0 -5 2 -5 5v44c0 3 2 5 5 5h178zM192 333c-78 0 -141 -63 -141 -141s63 -141 141 -141s141 63 141 141s-63 141 -141 141zM192 384v0c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192z
|
||||
" />
|
||||
<glyph glyph-name="uniF176" unicode="" horiz-adv-x="414"
|
||||
d="M404 251c6 0 10 -5 10 -11v-96c0 -6 -4 -11 -10 -11h-394c-6 0 -10 5 -10 11v96c0 6 4 11 10 11h394z" />
|
||||
<glyph glyph-name="uniF177" unicode="" horiz-adv-x="224"
|
||||
d="M211 295c7 0 13 -6 13 -13v-307c0 -7 -6 -13 -13 -13h-199v0c-7 0 -12 6 -12 13v307c0 7 5 13 12 13v0h199zM112 -24c7 0 13 5 13 12s-6 13 -13 13s-12 -6 -12 -13s5 -12 12 -12zM173 13v0v231h-122v-231h122zM212 368v0v-1v0v0l-9 -9v0v0h-1v0c-3 -3 -7 -3 -10 -1v0v1v0
|
||||
v0l-5 5v0c-41 41 -109 40 -150 -1v0v0l-6 -5v0c-3 -2 -6 -2 -9 0v0l-10 9v0c-3 3 -3 7 -1 10v1h1v0v0l5 5v0v0c52 52 138 53 190 1v0v-1v0v0l5 -5v0c2 -3 2 -6 0 -9zM174 339c2 -3 2 -6 0 -9v0l-9 -10v0v0v0v0c-3 -3 -8 -3 -11 -1v0v1v0v0l-5 5v0c-21 20 -54 20 -74 0v-1v0
|
||||
l-6 -5v0c-3 -2 -6 -2 -9 0v0l-10 9v1c-3 3 -3 7 -1 10v0l6 5v0v1c31 31 82 31 114 0v0l5 -6v0z" />
|
||||
<glyph glyph-name="uniF178" unicode="" horiz-adv-x="224"
|
||||
d="M211 358c7 0 13 -5 13 -12v-308c0 -7 -6 -12 -13 -12h-199v0c-7 0 -12 5 -12 12v308c0 7 5 12 12 12v0h199zM112 39c7 0 13 5 13 12s-6 13 -13 13s-12 -6 -12 -13s5 -12 12 -12zM173 77v0v230h-122v-230h122z" />
|
||||
<glyph glyph-name="uniF179" unicode="" horiz-adv-x="471"
|
||||
d="M442 381c16 0 29 -12 29 -28v-252c0 -16 -13 -29 -29 -29h-156v-44h44c5 0 9 -3 9 -8v-9c0 -5 -4 -8 -9 -8h-194c-5 0 -8 3 -8 8v9c0 5 3 8 8 8h48v44h-155c-16 0 -29 13 -29 29v252c0 16 13 28 29 28h413zM51 123h368v208h-368v-208z" />
|
||||
<glyph glyph-name="uniF17A" unicode="" horiz-adv-x="467"
|
||||
d="M465 48c1 -2 2 -4 2 -6c0 -7 -5 -13 -12 -14v0h-256h-103h-87v1c-1 0 -1 -1 -2 -1c-4 0 -7 3 -7 7c0 2 0 4 1 5l8 13v1h1l81 140c2 4 6 7 11 7s9 -3 11 -7v0l27 -48l114 196c4 8 12 14 21 14s16 -5 20 -13v0l171 -295h-1z" />
|
||||
<glyph glyph-name="uniF17B" unicode="" horiz-adv-x="382"
|
||||
d="M366 378c9 0 16 -7 16 -16v-253c0 -31 -31 -56 -69 -56s-69 25 -69 56s31 56 69 56c6 0 12 -1 18 -2v135h-193v-236c0 -31 -31 -56 -69 -56s-69 25 -69 56s31 56 69 56c6 0 12 -1 18 -2v246c0 9 8 16 17 16v0h262v0z" />
|
||||
<glyph glyph-name="uniF17C" unicode="" horiz-adv-x="397"
|
||||
d="M397 52v0c0 -7 -6 -13 -13 -13v-1h-25v0c-7 0 -13 6 -13 13v1v0v120l-170 -99c-2 -2 -5 -2 -7 -2c-5 0 -8 3 -8 8v78l-146 -84c-2 -2 -4 -2 -6 -2c-5 0 -9 3 -9 8v226v0c0 5 4 8 9 8c2 0 4 0 6 -2l146 -84v78v0c0 5 3 8 8 8c2 0 5 0 7 -2l170 -99v119v2c0 7 6 13 13 13
|
||||
l1 -1h24v0c7 0 12 -4 13 -11v0v-2v0v0v-280z" />
|
||||
<glyph glyph-name="uniF17D" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM192 51c34 0 66 13 90 33l-24 24c-3 0 -4 1 -6 3v0l-4 7l-21 21l-16 -28h-1c-2 -2 -4 -4 -7 -4c-5 0 -10 5 -10 10c0 2 1 4 2 6l13 23h-21h-23l13 -23c1 -2 3 -4 3 -6
|
||||
c0 -5 -5 -10 -10 -10c-3 0 -6 2 -8 4v0l-21 36l-21 -36v0c-2 -2 -5 -4 -8 -4c-5 0 -10 5 -10 10c0 2 2 4 3 6l17 31c-4 4 -7 16 -7 31c0 7 1 14 2 19l-32 18v0c-2 1 -3 3 -3 6c0 4 3 7 7 7c1 0 3 0 4 -1v0l31 -18v1v0h25l-65 65c-20 -24 -33 -56 -33 -90
|
||||
c0 -78 63 -141 141 -141zM300 102c20 24 33 56 33 90c0 78 -63 141 -141 141c-34 0 -66 -13 -90 -33l83 -83h26l30 30h-1l17 30l10 -17h1v0l14 -8c4 -2 6 -6 6 -10v-11l11 -11c2 -1 3 -4 3 -6s-1 -4 -2 -5v0l-6 -6v0h-6h-22l-8 -8c0 -3 1 -6 1 -10c0 -16 -4 -29 -9 -32l3 -4
|
||||
z" />
|
||||
<glyph glyph-name="uniF17E" unicode="" horiz-adv-x="384"
|
||||
d="M272 213c3 0 5 -3 5 -6v-31v0c0 -3 -2 -5 -5 -5v0v0h-4v0c-3 0 -6 2 -6 5v0v1v0v0v30v0c0 3 3 6 6 6v0h4v0zM293 213c3 0 6 -3 6 -6v0v-30v-1v0c0 -3 -3 -5 -6 -5v0v0h-3v0c-3 0 -6 2 -6 5v0v31v0c0 3 3 6 6 6v0h3v0zM192 384c106 0 192 -86 192 -192s-86 -192 -192 -192
|
||||
s-192 86 -192 192s86 192 192 192zM192 51c34 0 66 13 90 33l-87 87h-104c-3 0 -6 3 -6 6v0v30v0v0c0 3 3 6 6 6v0h62l-69 69c-20 -24 -33 -56 -33 -90c0 -78 63 -141 141 -141zM300 102c20 24 33 56 33 90c0 78 -63 141 -141 141c-34 0 -66 -13 -90 -33l87 -87h60v0
|
||||
c3 0 6 -3 6 -6v0v-30v0c0 -3 -3 -6 -6 -6h-18z" />
|
||||
<glyph glyph-name="uniF17F" unicode="" horiz-adv-x="360"
|
||||
d="M301 46c5 0 9 -4 9 -9v0v-45v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0v-127v0v0v0v0c0 -5 -4 -9 -9 -9h-33c-5 0 -9 4 -9 9v0v105h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v15v0c0 5 4 9 9 9v0h33z
|
||||
M351 174c5 0 9 -4 9 -9v-36c0 -5 -4 -8 -9 -8h-40v-40c0 -5 -4 -9 -9 -9h-35c-5 0 -9 4 -9 9v40h-40c-5 0 -9 3 -9 8v36c0 5 4 9 9 9h40v40c0 5 4 8 9 8h35c5 0 9 -3 9 -8v-40h40z" />
|
||||
<glyph glyph-name="uniF180" unicode="" horiz-adv-x="361"
|
||||
d="M310 410v-392v0v-13c0 -5 -4 -9 -9 -9h-8h-276h-8c-5 0 -9 4 -9 9v13v271v5v12l133 133h9v0h147v0h12c5 0 9 -4 9 -9v-20v0zM51 47h208v341h-108v-91c0 -5 -4 -8 -9 -8h-91v-242zM354 321c4 0 7 -4 7 -8v-360c0 -4 -3 -8 -7 -8h-272v0v0c-4 0 -7 4 -7 8v17h253
|
||||
c4 0 8 4 8 8v343h18z" />
|
||||
<glyph glyph-name="uniF181" unicode="" horiz-adv-x="310"
|
||||
d="M106 119c-7 0 -12 -4 -12 -11s5 -12 12 -12c4 0 8 3 9 7l12 -6c-3 -6 -9 -14 -21 -14c-15 0 -26 10 -26 25s11 24 26 24c12 0 18 -7 21 -14l-12 -5c-1 4 -5 6 -9 6zM148 118c0 -4 26 0 26 -18c0 -10 -7 -17 -20 -17c-10 0 -17 3 -22 8l7 10c4 -3 9 -6 15 -6c3 0 6 1 6 3
|
||||
c0 5 -27 -1 -27 18c0 8 7 16 20 16c8 0 15 -3 20 -7l-8 -10c-4 3 -10 5 -14 5c-3 0 -3 -1 -3 -2zM204 99l10 32h16l-17 -47h-18l-18 47h16zM310 384v-392v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0zM51 22
|
||||
h208v340h-108v-90c0 -5 -4 -9 -9 -9h-91v-241z" />
|
||||
<glyph glyph-name="uniF182" unicode="" horiz-adv-x="361"
|
||||
d="M301 46c5 0 9 -4 9 -9v0v-45v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0v-127v0v0v0v0c0 -5 -4 -9 -9 -9h-33c-5 0 -9 4 -9 9v0v105h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v15v0c0 5 4 9 9 9v0h33z
|
||||
M330 147l28 -28c3 -3 3 -10 0 -13l-25 -25c-3 -3 -9 -3 -12 0l-29 29l-28 -29c-3 -3 -9 -3 -12 0l-25 25c-3 3 -3 10 0 13l28 28l-28 28c-3 3 -3 10 0 13l25 25c3 3 9 3 12 0l28 -28l28 28c3 3 10 3 13 0l25 -25c3 -3 3 -10 0 -13z" />
|
||||
<glyph glyph-name="uniF183" unicode="" horiz-adv-x="310"
|
||||
d="M310 384v-392v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0zM51 22h208v340h-108v-90c0 -5 -4 -9 -9 -9h-91v-241zM97 131c15 0 26 -8 26 -23s-11 -24 -26 -24h-21v47h21zM97 96c7 0 11 6 11 12
|
||||
s-3 11 -11 11h-7v-23h7zM155 132c15 0 26 -9 26 -24s-11 -25 -26 -25s-26 10 -26 25s11 24 26 24zM155 96c7 0 11 5 11 12s-4 11 -11 11s-12 -4 -12 -11s5 -12 12 -12zM213 119c-7 0 -12 -4 -12 -11s5 -12 12 -12c4 0 8 3 9 7l12 -6c-3 -6 -9 -14 -21 -14
|
||||
c-15 0 -26 10 -26 25s11 24 26 24c12 0 18 -7 21 -14l-12 -5c-1 4 -5 6 -9 6z" />
|
||||
<glyph glyph-name="uniF184" unicode="" horiz-adv-x="428"
|
||||
d="M301 46c5 0 9 -4 9 -9v0v-45v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0v-84v0v0v-1v0c0 -5 -4 -8 -9 -8h-33c-5 0 -9 3 -9 8v0v63h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v15v0c0 5 4 9 9 9v0h33z
|
||||
M425 248c3 -3 3 -9 0 -12l-163 -163v0v0l-82 -22v0c-3 -2 -7 -2 -10 1c-2 2 -3 6 -2 9v0l22 84l1 -1l-1 1l163 163c3 3 10 3 13 0zM195 78l49 13l-36 36z" />
|
||||
<glyph glyph-name="uniF185" unicode="" horiz-adv-x="458"
|
||||
d="M456 112c2 -1 2 -3 2 -5v0c0 -2 0 -4 -2 -5l-94 -68c-2 -1 -5 -2 -7 -1s-3 4 -3 6v32h-86c-4 0 -7 3 -7 7v58c0 4 3 6 7 6h85v32c0 2 2 5 4 6s4 1 6 0zM301 46c5 0 9 -4 9 -9v0v-45v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12
|
||||
c5 0 9 -3 9 -8v-21v0v-207v0v0v-1v0c0 -5 -4 -8 -9 -8h-33c-5 0 -9 3 -9 8v0v186h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v15v0c0 5 4 9 9 9v0h33zM107 119c-7 0 -11 -4 -11 -11s4 -12 11 -12c4 0 8 3 9 7l13 -6c-3 -6 -10 -14 -22 -14c-15 0 -26 10 -26 25s11 24 26 24
|
||||
c12 0 19 -7 22 -14l-13 -5c-1 4 -5 6 -9 6zM156 95c3 0 5 1 5 3c0 5 -26 -1 -26 18c0 8 6 16 19 16c8 0 15 -3 20 -7l-7 -10c-4 3 -10 5 -14 5c-3 0 -4 -1 -4 -2c0 -4 26 0 26 -18c0 -10 -7 -17 -20 -17c-10 0 -17 3 -22 8l8 10c4 -3 9 -6 15 -6zM195 131l10 -32l10 32h16
|
||||
l-17 -47h-18l-17 47h16z" />
|
||||
<glyph glyph-name="uniF186" unicode="" horiz-adv-x="458"
|
||||
d="M456 112c2 -1 2 -3 2 -5v0c0 -2 0 -4 -2 -5l-94 -68c-2 -1 -5 -2 -7 -1s-3 4 -3 6v32h-86c-4 0 -7 3 -7 7v58c0 4 3 6 7 6h85v32c0 2 2 5 4 6s4 1 6 0zM301 46c5 0 9 -4 9 -9v0v-45v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12
|
||||
c5 0 9 -3 9 -8v-21v0v-207v0v0v-1v0c0 -5 -4 -8 -9 -8h-33c-5 0 -9 3 -9 8v0v186h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v15v0c0 5 4 9 9 9v0h33zM77 131h22c15 0 26 -8 26 -23s-11 -24 -26 -24h-22v47zM110 108c0 6 -3 11 -11 11h-7v-23h7c7 0 11 6 11 12zM131 108
|
||||
c0 15 10 24 25 24s26 -9 26 -24s-11 -25 -26 -25s-25 10 -25 25zM168 108c0 7 -5 11 -12 11s-11 -4 -11 -11s4 -12 11 -12s12 5 12 12zM214 119c-7 0 -11 -4 -11 -11s4 -12 11 -12c4 0 8 3 9 7l13 -6c-3 -6 -10 -14 -22 -14c-15 0 -26 10 -26 25s11 24 26 24
|
||||
c12 0 19 -7 22 -14l-13 -5c-1 4 -5 6 -9 6z" />
|
||||
<glyph glyph-name="uniF187" unicode="" horiz-adv-x="458"
|
||||
d="M456 112c2 -1 2 -3 2 -5v0c0 -2 0 -4 -2 -5l-94 -68c-2 -1 -5 -2 -7 -1s-3 4 -3 6v32h-86c-4 0 -7 3 -7 7v58c0 4 3 6 7 6h85v32c0 2 2 5 4 6s4 1 6 0zM301 46c5 0 9 -4 9 -9v0v-45v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12
|
||||
c5 0 9 -3 9 -8v-21v0v-207v0v0v-1v0c0 -5 -4 -8 -9 -8h-33c-5 0 -9 3 -9 8v0v186h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v15v0c0 5 4 9 9 9v0h33zM83 131h26c11 0 17 -7 17 -16s-6 -16 -17 -16h-11v-15h-15v47zM111 115c0 3 -2 4 -4 4h-9v-8h9c2 0 4 2 4 4zM133 131h21
|
||||
c15 0 26 -8 26 -23s-11 -24 -26 -24h-21v47zM166 108c0 6 -4 11 -12 11h-7v-23h7c7 0 12 6 12 12zM225 119h-22v-5h21v-12h-21v-18h-14v47h36v-12z" />
|
||||
<glyph glyph-name="uniF188" unicode="" horiz-adv-x="458"
|
||||
d="M456 112c2 -1 2 -3 2 -5v0c0 -2 0 -4 -2 -5l-94 -68c-2 -1 -5 -2 -7 -1s-3 4 -3 6v32h-86c-4 0 -7 3 -7 7v58c0 4 3 6 7 6h85v32c0 2 2 5 4 6s4 1 6 0zM301 46c5 0 9 -4 9 -9v0v-45v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12
|
||||
c5 0 9 -3 9 -8v-21v0v-207v0v0v-1v0c0 -5 -4 -8 -9 -8h-33c-5 0 -9 3 -9 8v0v186h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v15v0c0 5 4 9 9 9v0h33z" />
|
||||
<glyph glyph-name="uniF189" unicode="" horiz-adv-x="310"
|
||||
d="M9 263c-5 0 -9 4 -9 9v9l133 132h9c5 0 9 -3 9 -8v-133c0 -5 -4 -9 -9 -9h-133zM301 413c5 0 9 -3 9 -8v-426c0 -5 -4 -8 -9 -8h-292c-5 0 -9 3 -9 8v224v0c1 4 5 8 9 8h184v0v0c5 0 9 4 9 9v0v0v187v0c1 4 4 6 8 6h91z" />
|
||||
<glyph glyph-name="uniF18A" unicode="" horiz-adv-x="389"
|
||||
d="M381 329c4 0 8 -4 8 -8v-360c0 -4 -4 -8 -8 -8h-271v0v0c-4 0 -8 4 -8 8v18h253c4 0 8 3 8 7v343h18zM338 12c0 -4 -4 -8 -8 -8h-271v0c-4 0 -8 4 -8 8v18h253c4 0 8 3 8 7v343h18c4 0 8 -4 8 -8v-360zM287 63c0 -4 -4 -8 -8 -8h-271c-4 0 -8 4 -8 8v360c0 4 4 8 8 8h271
|
||||
c4 0 8 -4 8 -8v-360z" />
|
||||
<glyph glyph-name="uniF18B" unicode="" horiz-adv-x="310"
|
||||
d="M110 131c11 0 17 -7 17 -16s-6 -16 -17 -16h-11v-15h-15v47h26zM108 111c2 0 4 2 4 4c0 3 -2 4 -4 4h-9v-8h9zM155 131c15 0 26 -8 26 -23s-11 -24 -26 -24h-21v47h21zM155 96c7 0 12 6 12 12s-4 11 -12 11h-7v-23h7zM190 84v47h36v-12h-22v-5h21v-12h-21v-18h-14z
|
||||
M310 384v-392v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0zM51 22h208v340h-108v-90c0 -5 -4 -9 -9 -9h-91v-241z" />
|
||||
<glyph glyph-name="uniF18C" unicode="" horiz-adv-x="360"
|
||||
d="M310 86v0v0v-94v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0v-176v0v0v0v0c0 -5 -4 -9 -9 -9h-33c-5 0 -9 4 -9 9v0v154h-108v-90c0 -5 -4 -9 -9 -9h-91v-241h208v64v0c0 5 4 9 9 9v0h33c5 0 9 -4 9 -9v0v0
|
||||
zM351 174c5 0 9 -4 9 -9v-36c0 -5 -4 -8 -9 -8h-133c-5 0 -9 3 -9 8v36c0 5 4 9 9 9h133z" />
|
||||
<glyph glyph-name="uniF18D" unicode="" horiz-adv-x="403"
|
||||
d="M403 384l-1 -392v0v-13c0 -5 -3 -8 -8 -8h-8h-277h-8c-5 0 -9 3 -9 8v13v45v0c0 5 4 9 9 9v0h34c5 0 8 -4 8 -9v0v-15h209v340h-109v-90c0 -5 -4 -9 -9 -9h-91v-9v0c0 -5 -3 -8 -8 -8v0h-34c-5 0 -8 3 -8 8h-1v9v5v13l133 132h9v0h147v0h13c5 0 8 -3 8 -8v-21h1zM199 146
|
||||
c0 -45 -37 -82 -82 -82c-13 0 -26 3 -37 9l-41 -41v0c-4 -4 -10 -7 -16 -7c-13 0 -23 9 -23 22c0 7 3 13 7 17v0l40 40c-7 12 -12 27 -12 42c0 45 37 82 82 82s82 -37 82 -82zM70 146c0 -26 21 -48 47 -48s48 22 48 48s-22 47 -48 47s-47 -21 -47 -47z" />
|
||||
<glyph glyph-name="uniF18E" unicode="" horiz-adv-x="310"
|
||||
d="M310 384v-392v0v-13c0 -5 -4 -8 -9 -8h-8h-276h-8c-5 0 -9 3 -9 8v13v271v5v13l133 132h9v0h147v0h12c5 0 9 -3 9 -8v-21v0zM51 22h208v340h-108v-90c0 -5 -4 -9 -9 -9h-91v-241z" />
|
||||
<glyph glyph-name="uniF18F" unicode="" horiz-adv-x="439"
|
||||
d="M389 203c5 -5 5 -13 0 -18l-203 -203v0c-5 -5 -13 -5 -18 0l-164 164c-5 5 -5 14 0 19v0l139 139l-57 58c-5 5 -8 11 -8 18c0 14 12 26 26 26c7 0 13 -3 18 -8l57 -58l27 27c5 5 14 5 19 0v0v0l164 -164v0zM288 156l38 38l-110 110l-149 -148h221zM430 70
|
||||
c6 -8 9 -17 9 -27c0 -26 -21 -48 -47 -48s-48 22 -48 48c0 11 4 20 10 28l30 52c0 1 1 0 1 1v1v0c2 2 4 3 7 3s5 -2 7 -4v0v0v-1z" />
|
||||
<glyph glyph-name="uniF190" unicode="" horiz-adv-x="356"
|
||||
d="M77 -5c-14 0 -32 5 -50 23c-24 24 -27 47 -25 62c2 18 11 34 27 50l150 151c42 42 70 26 83 13c16 -16 26 -42 -14 -82l-138 -139l-27 27l138 139c21 21 16 26 14 28s-8 8 -29 -13l-150 -151c-7 -7 -15 -17 -16 -28c-1 -10 3 -19 14 -30c13 -13 23 -11 26 -11
|
||||
c9 1 20 8 30 18l178 178c15 15 24 29 28 42c5 19 -1 38 -20 57c-20 20 -50 40 -98 -8l-165 -165c-7 -7 -20 -7 -27 0s-7 20 0 27l165 165c51 51 106 54 152 8c36 -36 36 -71 30 -94c-6 -20 -18 -39 -38 -59l-178 -178c-16 -16 -34 -27 -51 -29c-3 0 -6 -1 -9 -1z" />
|
||||
<glyph glyph-name="uniF191" unicode="" horiz-adv-x="228"
|
||||
d="M65 346c6 0 12 -5 12 -11v0v0v-286v0v0c0 -6 -6 -11 -12 -11v0h-54v0v0c-6 0 -11 5 -11 11v0v0v286v0v0c0 6 5 11 11 11v0h53v0h1zM228 49v0v0c0 -6 -5 -11 -11 -11v0h-55v0v0c-6 0 -11 5 -11 11v0v0v286v0v0c0 6 5 11 11 11v0h53v0h2c6 0 11 -5 11 -11v0v0v-286z" />
|
||||
<glyph glyph-name="uniF192" unicode="" horiz-adv-x="363"
|
||||
d="M104 240c0 -29 -23 -52 -52 -52s-52 23 -52 52s23 51 52 51s52 -22 52 -51zM311 281c29 0 52 -23 52 -52s-23 -51 -52 -51s-52 22 -52 51s23 52 52 52zM264 172l3 -4c16 -19 35 -44 35 -84c0 -37 -31 -68 -68 -68c-21 0 -40 10 -53 27c-13 -17 -33 -27 -54 -27
|
||||
c-37 0 -68 31 -68 68c0 40 19 65 35 84l3 4c5 6 13 13 22 21c16 17 37 27 62 27c28 0 52 -13 68 -33c6 -5 11 -11 15 -15zM128 310c0 38 19 58 57 58s57 -20 57 -58s-19 -57 -57 -57s-57 19 -57 57z" />
|
||||
<glyph glyph-name="uniF193" unicode="" horiz-adv-x="315"
|
||||
d="M73 88c-2 -9 -11 -16 -20 -16h-40c-9 0 -15 8 -13 16l58 248c2 8 10 16 19 16h85c18 0 34 -2 48 -4c14 -3 25 -7 35 -13s17 -14 22 -24s8 -22 8 -37c0 -33 -13 -60 -41 -80s-66 -31 -116 -31h-12c-9 0 -17 -7 -19 -15zM106 232c-2 -9 3 -16 12 -16h10c22 0 40 5 52 14
|
||||
s18 22 18 38c0 11 -4 19 -12 24s-19 8 -35 8h-13c-9 0 -18 -7 -20 -15zM307 271c5 -10 8 -21 8 -36c0 -33 -14 -61 -42 -81s-65 -30 -115 -30h-12c-9 0 -17 -8 -19 -16l-14 -60c-2 -9 -11 -16 -20 -16h-40c-9 0 -15 8 -13 16l2 8h28c9 0 17 6 19 15l14 61c2 8 10 15 19 15
|
||||
h12c50 0 88 11 116 31s41 47 41 80c0 15 -3 27 -8 37v1c1 0 1 -1 2 -1c10 -6 17 -14 22 -24z" />
|
||||
<glyph glyph-name="uniF194" unicode="" horiz-adv-x="405"
|
||||
d="M401 298c5 -5 5 -14 0 -19l-253 -254v0v0l-128 -34v0c-5 -2 -12 -2 -16 2s-5 9 -3 14v0l34 130h1v0l253 254c5 5 15 5 20 0zM43 33l77 20l-57 56z" />
|
||||
<glyph glyph-name="uniF195" unicode="" horiz-adv-x="443"
|
||||
d="M442 356h1v-330h-1c-1 -11 -10 -21 -22 -21c-1 0 -3 1 -4 1v-1h-393v0c-12 0 -22 10 -23 21v0v2v0v0v328v0c0 13 10 23 23 23v0h393v-1c1 0 3 1 4 1c13 0 22 -10 22 -23zM392 56v272h-341v-218l28 48c1 3 5 5 8 5s6 -2 7 -5v0l19 -33l77 134c2 5 9 10 15 10s11 -4 14 -9
|
||||
v0l116 -202v0c0 -1 1 -1 1 -2h56zM262 250c0 34 18 52 52 52s52 -18 52 -52s-18 -51 -52 -51s-52 17 -52 51z" />
|
||||
<glyph glyph-name="uniF196" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM192 51c78 0 141 63 141 141s-63 141 -141 141s-141 -63 -141 -141s63 -141 141 -141zM276 197c2 -1 2 -3 2 -5s0 -4 -2 -5v0l-120 -69v0h-3c-3 0 -5 1 -5 4v0v138v0v1c0 3 2 6 5 6
|
||||
c1 0 2 -1 3 -1v0l120 -69v0z" />
|
||||
<glyph glyph-name="uniF197" unicode="" horiz-adv-x="415"
|
||||
d="M389 35h-364c-14 0 -25 12 -25 26v262c0 14 11 26 25 26h364c14 0 26 -12 26 -26v-262c0 -14 -12 -26 -26 -26zM364 86v212h-313v-212h313zM283 191l-61 -36l-61 -35v71v70l61 -35z" />
|
||||
<glyph glyph-name="uniF198" unicode="" horiz-adv-x="276"
|
||||
d="M276 192c0 -4 -2 -8 -5 -10v0l-254 -146v0c-2 -1 -4 -2 -6 -2c-6 0 -10 4 -11 10v0v293v0v1c0 6 5 12 11 12c2 0 5 -1 7 -2l252 -146c4 -2 6 -6 6 -10z" />
|
||||
<glyph glyph-name="uniF199" unicode="" horiz-adv-x="371"
|
||||
d="M362 245c5 0 9 -5 9 -10v-86c0 -3 -1 -5 -3 -7s-3 -3 -6 -3h-124v-123c0 -2 -1 -5 -3 -7s-4 -3 -6 -3h-87c-2 0 -4 1 -6 3s-3 5 -3 7v123h-124c-2 0 -4 1 -6 3s-3 5 -3 7v86c0 5 4 10 9 10h124v123c0 5 4 10 9 10h87c5 0 9 -5 9 -10v-123h124z" />
|
||||
<glyph glyph-name="uniF19A" unicode="" horiz-adv-x="221"
|
||||
d="M221 75c0 -2 -1 -4 -2 -5v0c-12 -12 -31 -20 -61 -20c-36 0 -50 18 -75 18c-14 0 -32 -7 -48 -16v0h-2c-2 0 -4 1 -5 3v0h-1v0l-14 30v0v0v0v2c0 2 1 4 3 5v0v1v0c26 12 45 30 45 51c0 8 -2 15 -5 22h-50v0v0c-3 0 -6 2 -6 5v0v22v0c0 3 3 6 6 6v0v0h28
|
||||
c-10 14 -19 30 -19 50c0 52 51 85 100 85c46 0 79 -16 96 -50v0c1 -1 1 -2 1 -3c0 -2 -1 -4 -3 -5v0l-37 -22v0v0h-2c-2 0 -5 1 -6 3c-7 17 -23 29 -42 29c-24 0 -43 -15 -43 -38c0 -21 11 -34 20 -49h60v0c3 0 5 -2 5 -5v0v-22v0c0 -3 -2 -6 -5 -6v0h-47c0 -2 1 -6 1 -8
|
||||
c0 -19 -12 -37 -27 -46c6 2 14 4 20 4c24 0 34 -14 54 -14c18 0 30 8 35 15v0c1 1 3 2 5 2s4 -2 5 -4v0v0v0l15 -37v0c0 -1 1 -2 1 -3z" />
|
||||
<glyph glyph-name="uniF19B" unicode="" horiz-adv-x="384"
|
||||
d="M328 332c35 -35 56 -82 56 -135c0 -106 -86 -192 -192 -192s-192 86 -192 192c0 51 20 98 53 132c2 3 6 6 10 6c3 0 5 -1 7 -3v0h1v0l21 -19v-1v0v0c2 -2 4 -4 4 -7s-2 -7 -4 -9v0c-25 -26 -41 -60 -41 -99c0 -78 63 -141 141 -141s141 63 141 141c0 38 -15 73 -40 98
|
||||
c-3 2 -5 6 -5 10c0 3 1 5 3 7v0v0v1l21 19v0h1v0c2 2 4 3 7 3s6 -1 8 -3v0zM178 127c-6 0 -11 5 -11 11v0v0v230v0v0c0 6 5 11 11 11v0h28v0v0c6 0 11 -5 11 -11v0v0v-230v0v0c0 -6 -5 -11 -11 -11v0v0h-28v0v0v0v0z" />
|
||||
<glyph glyph-name="uniF19C" unicode="" horiz-adv-x="397"
|
||||
d="M388 313c5 0 9 -3 9 -8v-226v0c0 -5 -4 -8 -9 -8c-2 0 -4 0 -6 2l-146 84v-78v0c0 -5 -3 -8 -8 -8c-2 0 -4 0 -6 2l-171 99v-119v-2c0 -7 -6 -13 -13 -13l-1 1h-23h-1c-7 0 -12 4 -13 11v0v2v0v0v280v0c0 7 6 13 13 13v1h25v0c7 0 13 -6 13 -13v-1v0v-120l170 99
|
||||
c2 2 5 2 7 2c5 0 8 -3 8 -8v-78l146 84c2 2 4 2 6 2z" />
|
||||
<glyph glyph-name="uniF19D" unicode="" horiz-adv-x="325"
|
||||
d="M324 109c3 -5 1 -11 -4 -14v0l-166 -96v0c-5 -3 -12 -1 -15 4v1l-137 238v0h-1c-1 2 -1 4 -1 6v0v65v0c0 4 2 7 5 9l109 63v0v0l1 1v0c3 1 6 1 9 -1v0l57 -32v0c2 -1 3 -2 4 -4h1l137 -239v0zM93 298c9 5 12 18 7 27s-18 12 -27 7s-12 -18 -7 -27s18 -12 27 -7z" />
|
||||
<glyph glyph-name="uniF19E" unicode="" horiz-adv-x="395"
|
||||
d="M393 100c3 -5 2 -12 -3 -15v0l-167 -96v0c-5 -3 -11 -1 -14 4v1l-7 12l153 88v0c5 3 7 10 4 15v0v0l-104 179l-45 79l41 -24v0c2 -1 3 -2 4 -4v0l138 -238v0v-1zM320 104v0l-166 -96v0c-5 -3 -12 -1 -15 4v1l-137 238v0h-1c-1 2 -1 5 -1 7v0v65v0c0 4 2 7 5 9l109 62v0v1
|
||||
h1v0c3 1 6 1 9 -1v1l57 -33v0c2 -1 3 -2 4 -4h1l137 -239v0h1c3 -5 1 -12 -4 -15zM93 307c9 5 12 18 7 27s-18 12 -27 7s-12 -18 -7 -27s18 -12 27 -7z" />
|
||||
<glyph glyph-name="uniF19F" unicode="" horiz-adv-x="414"
|
||||
d="M414 268v0v-188c0 -5 -4 -10 -9 -10h-67v79v0h-26h-210h-25v0v-79h-68v0c-5 0 -9 5 -9 10v188v0c0 5 4 9 9 9v0h68v0v110c0 5 4 10 9 10h242c5 0 10 -5 10 -10v-110v0v0h67c5 0 9 -4 9 -9zM312 235v0v127c0 5 -4 9 -9 9h-192v0c-5 0 -9 -4 -9 -9v-127h210zM103 -3v126
|
||||
h209v-126c0 -5 -5 -10 -10 -10h-190c-5 0 -9 5 -9 10z" />
|
||||
<glyph glyph-name="uniF1A0" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM192 51c34 0 66 13 90 33l-198 198c-20 -24 -33 -56 -33 -90c0 -78 63 -141 141 -141zM300 102c20 24 33 56 33 90c0 78 -63 141 -141 141c-34 0 -66 -13 -90 -33z" />
|
||||
<glyph glyph-name="uniF1A1" unicode="" horiz-adv-x="391"
|
||||
d="M391 363v0v-32v0c0 -5 -4 -9 -9 -9h-373v0c-5 0 -9 4 -9 9v0v32v0c0 5 4 10 9 10v0h373c5 0 9 -5 9 -10v0zM359 296c5 0 9 -4 9 -9v0v-190c0 -11 -9 -21 -21 -21h-139v-27c5 -4 8 -10 8 -17c0 -12 -9 -21 -21 -21s-21 9 -21 21c0 7 4 13 9 17v27h-139
|
||||
c-12 0 -22 10 -22 21v190v0v0c0 5 4 9 9 9h328z" />
|
||||
<glyph glyph-name="uniF1A2" unicode="" horiz-adv-x="293"
|
||||
d="M293 198v0v0v-3h-1c-1 -5 -6 -8 -11 -8v0v-1c-4 1 -8 2 -13 2c-28 0 -50 -22 -50 -50s22 -50 50 -50c5 0 9 1 13 2v-1c6 0 11 -4 12 -10v0v-67v0c0 -11 -10 -20 -21 -20h-65v0c-6 0 -12 6 -12 12c0 2 0 3 1 4v0c1 4 2 9 2 14c0 28 -22 50 -50 50s-50 -22 -50 -50
|
||||
c0 -5 1 -10 2 -14v0v-1v0c0 -1 1 -2 1 -3c0 -6 -5 -12 -11 -12h-68h-2c-11 0 -20 9 -20 20v1v66v0v2c0 6 6 12 12 12c1 0 3 -1 4 -1v0c6 -3 12 -4 19 -4c28 0 50 22 50 50s-22 50 -50 50c-7 0 -14 -1 -20 -4c-1 0 -2 -1 -3 -1c-6 0 -12 6 -12 12v0v68v1c0 11 9 21 20 21v0
|
||||
h75v0c14 1 25 12 25 27c0 6 -1 12 -5 17v0c-4 6 -6 13 -6 21c0 23 20 42 43 42s42 -19 42 -42c0 -8 -2 -15 -6 -21h1c-4 -5 -6 -11 -6 -17c0 -15 11 -26 25 -27v0h1h1h1h62v-1c11 0 20 -9 20 -20v0v-66z" />
|
||||
<glyph glyph-name="uniF1A3" unicode="" horiz-adv-x="435"
|
||||
d="M52 224c20 0 35 -15 35 -37s-19 -41 -41 -41c-24 0 -46 19 -46 53c0 39 21 73 53 96v0c1 0 2 1 3 1s2 -1 3 -2v0l22 -13v0c2 -1 2 -3 2 -5s-1 -4 -3 -5v0v0v0c-16 -9 -34 -30 -39 -48c2 1 7 1 11 1zM157 224c20 0 35 -15 35 -37s-19 -41 -41 -41c-24 0 -46 19 -46 53
|
||||
c0 39 21 73 53 96h1c1 0 1 1 2 1s3 -1 4 -2v0l21 -13v0c2 -1 3 -3 3 -5s-2 -4 -4 -5v0v0v0c-16 -9 -33 -30 -38 -48c2 1 6 1 10 1zM284 238c24 0 46 -19 46 -53c0 -39 -21 -73 -53 -96v0c-1 0 -2 -1 -3 -1s-2 1 -3 2v0l-22 13v0c-2 1 -2 3 -2 5s1 4 3 5v0v0v0
|
||||
c16 9 34 30 39 48c-2 -1 -6 -1 -10 -1c-20 0 -36 15 -36 37s19 41 41 41zM389 238c24 0 46 -19 46 -53c0 -39 -21 -73 -53 -96v0c-1 0 -1 -1 -2 -1s-3 1 -4 2v0l-21 13v0c-2 1 -3 3 -3 5s1 4 3 5v0h1v0c16 9 33 30 38 48c-2 -1 -6 -1 -10 -1c-20 0 -35 15 -35 37
|
||||
s18 41 40 41z" />
|
||||
<glyph glyph-name="uniF1A4" unicode="" horiz-adv-x="380"
|
||||
d="M190 382c105 0 190 -85 190 -190s-85 -190 -190 -190s-190 85 -190 190s85 190 190 190zM190 130c34 0 62 28 62 62s-28 62 -62 62s-63 -28 -63 -62s29 -62 63 -62z" />
|
||||
<glyph glyph-name="uniF1A5" unicode="" horiz-adv-x="355"
|
||||
d="M354 337l1 -137c0 -2 -2 -5 -4 -6s-5 -2 -7 -1l-130 44c-3 1 -5 3 -5 6s1 6 3 8l31 22l5 3c-18 14 -41 23 -66 23c-59 0 -108 -48 -108 -107s49 -107 108 -107c36 0 69 17 89 47c3 4 9 6 13 3l45 -32c2 -1 4 -4 4 -6s0 -4 -1 -6c-34 -50 -91 -80 -151 -80
|
||||
c-100 0 -181 81 -181 181s81 181 181 181c50 0 95 -20 128 -53l2 2l31 22c2 2 5 1 8 0s4 -4 4 -7z" />
|
||||
<glyph glyph-name="uniF1A6" unicode="" horiz-adv-x="471"
|
||||
d="M123 210c2 -3 3 -7 5 -10l-53 -24c-14 -7 -23 -22 -23 -39v-56h-41c-6 0 -11 6 -11 13v54c0 5 3 10 7 12l69 32c-16 10 -28 29 -28 52c0 33 23 59 51 59c8 0 15 -2 21 -5c-2 -4 -3 -9 -4 -13c-3 -10 -4 -19 -4 -28c0 -16 4 -33 11 -47zM287 270c-1 4 -1 8 -2 13
|
||||
c-3 14 -10 27 -18 38h195v0v0c5 0 9 -4 9 -9v0v-33v0c0 -5 -4 -9 -9 -9v0v0h-175zM471 108v0v-33v0c0 -5 -4 -9 -9 -9v0v0h-114v4v11v36h114v0v0c5 0 9 -4 9 -9zM462 219c5 0 9 -4 9 -9v0v-33v0c0 -5 -4 -9 -9 -9v0v0h-126c-3 3 -7 6 -11 8l-53 25c2 4 5 7 7 11c1 2 1 5 2 7
|
||||
h181v0v0zM314 153c5 -3 9 -9 9 -16v-56v-11c0 -9 -6 -16 -14 -16h-218c-8 0 -14 7 -14 16v11v56c0 7 4 14 9 16l61 28l24 11c-11 7 -19 17 -25 29c-5 11 -9 23 -9 36c0 8 1 14 3 21c8 30 32 52 60 52c29 0 53 -22 60 -53c2 -6 3 -13 3 -20c0 -12 -3 -24 -8 -34
|
||||
c-6 -13 -14 -23 -25 -30l25 -12z" />
|
||||
<glyph glyph-name="uniF1A7" unicode="" horiz-adv-x="431"
|
||||
d="M431 336v-33v0c0 -5 -4 -9 -9 -9v0v0h-235v0c-5 0 -9 4 -9 9v0v33v0v0v0v0c0 5 4 9 9 9v0h235v0v0c5 0 9 -4 9 -9v0zM431 259v-32v0c0 -5 -4 -10 -9 -10v0v0h-235v0c-5 0 -9 5 -9 10v0v32v0v0v0v0c0 5 4 9 9 9v0h235v0v0c5 0 9 -4 9 -9v0zM127 336v-109v0v0
|
||||
c0 -5 -4 -10 -9 -10v0v0h-109v0c-5 0 -9 5 -9 10v0v0v109v0c0 5 4 9 9 9h1h108v0c5 0 9 -4 9 -9v0zM431 157v-32v0c0 -5 -4 -9 -9 -9v0v0h-235v0c-5 0 -9 4 -9 9v0v32v0v0v0v0c0 5 4 9 9 9v1h235v-1v0c5 0 9 -4 9 -9v0zM431 81v-33v0c0 -5 -4 -9 -9 -9v0v0h-235v0
|
||||
c-5 0 -9 4 -9 9v1v32v0v0v0v0c0 5 4 9 9 9v0h235v0v0c5 0 9 -4 9 -9v0zM127 157v-109v0v0c0 -5 -4 -9 -9 -9v0h-109v0c-5 0 -9 4 -9 9v0v0v109v0c0 5 4 10 9 10l1 -1h108v1c5 0 9 -5 9 -10v0z" />
|
||||
<glyph glyph-name="uniF1A8" unicode="" horiz-adv-x="406"
|
||||
d="M351 197c-2 -2 -5 -2 -7 -1l-131 42c-3 1 -5 3 -5 6s1 5 3 7l30 23l5 4c-19 14 -42 21 -67 21c-59 -1 -106 -50 -105 -109s51 -106 110 -105h10v0v0c7 0 14 -6 14 -13v0v0l1 -47v0c0 -3 -2 -7 -4 -9v0v0v-1l-2 -1v0c-1 0 -1 -1 -2 -1v0c-3 -1 -5 -2 -5 -2h-11
|
||||
c-100 -2 -183 78 -185 178s78 182 178 184c50 1 96 -18 129 -50l2 1l30 23c2 2 5 2 8 1s4 -4 4 -7l3 -138c0 -2 -1 -4 -3 -6zM291 142h18l2 -99h-21l-1 72l-16 -17l-13 12zM365 145c28 1 41 -25 41 -50s-11 -51 -39 -52s-42 25 -42 50s12 51 40 52zM366 61c14 0 19 15 19 33
|
||||
s-6 32 -20 32s-19 -15 -19 -33s6 -32 20 -32z" />
|
||||
<glyph glyph-name="uniF1A9" unicode="" horiz-adv-x="372"
|
||||
d="M363 313c5 0 9 -3 9 -8v-226v0c0 -5 -4 -8 -9 -8c-2 0 -4 0 -6 2l-145 84v-78v0c0 -5 -4 -8 -9 -8c-2 0 -4 0 -6 2l-192 111v0c-3 1 -5 4 -5 8c0 3 1 5 4 7v1l192 111c2 2 5 2 7 2c5 0 9 -3 9 -8v-78l145 84c2 2 4 2 6 2z" />
|
||||
<glyph glyph-name="uniF1AA" unicode="" horiz-adv-x="339"
|
||||
d="M48 115c25 0 45 -20 45 -45s-20 -44 -45 -44s-45 19 -45 44s20 45 45 45zM34 239c101 0 183 -81 184 -182v0v-18v0c-1 -8 -8 -14 -16 -15v-1h-31v0c-9 0 -17 7 -18 16v0v18v0c-1 65 -54 118 -119 118h-1v0h-17v0c-8 1 -15 7 -16 15v0v32v0c0 9 7 16 16 17v0h17v0h1z
|
||||
M339 57v0v0v-18v0c-1 -8 -7 -14 -15 -15v-1h-32v0c-9 0 -16 7 -17 16v0v18v0c-1 132 -109 239 -241 239h-1v0h-17v0c-8 1 -15 8 -16 16v0v31v0c0 9 7 16 16 17v1h17v0h1c168 0 304 -136 305 -304z" />
|
||||
<glyph glyph-name="uniF1AB" unicode="" horiz-adv-x="482"
|
||||
d="M482 28v-2c0 -7 -6 -12 -13 -12v0v0h-457c-7 0 -12 5 -12 12v1v0v25c0 7 5 12 12 12h39l169 293c4 8 12 13 21 13s17 -5 21 -13v0l169 -293h38v0c7 0 13 -5 13 -12v0v0v-24zM228 319l-29 -51h84l-29 51h-26zM169 217l-29 -51h202l-29 51h-144zM81 64h320l-30 51h-261z
|
||||
" />
|
||||
<glyph glyph-name="uniF1AC" unicode="" horiz-adv-x="379"
|
||||
d="M204 251v110h53v-110h-53zM375 350c2 -2 4 -5 4 -9v-1v-324v0v0c0 -7 -5 -12 -12 -12v0h-39v169v0c0 6 -6 11 -12 11v0v0v0v0h-253v0h-1h-1v0c-5 -1 -10 -5 -10 -11v0v-1v0v0v0v-156v0v-12h-39v0c-7 0 -12 5 -12 12v0v168v49v135v0c0 7 5 12 12 12v0v0h67v0c2 0 3 0 5 -2
|
||||
c1 -1 2 -2 2 -3v0v-130v0c0 -6 6 -12 12 -12v0h175h1c7 0 13 5 13 12v0v130v0c0 1 1 3 2 4s3 1 4 1v0v0v0h46v0c4 0 7 -2 9 -4l27 -26v0z" />
|
||||
<glyph glyph-name="uniF1AD" unicode="" horiz-adv-x="358"
|
||||
d="M298 252c33 0 60 -27 60 -60s-27 -60 -60 -60c-7 0 -14 2 -21 4l-157 -91v-3c0 -33 -27 -61 -60 -61s-60 28 -60 61s27 60 60 60c13 0 26 -5 36 -12l144 84c-2 6 -2 12 -2 18s0 12 2 18l-145 84c-10 -7 -22 -12 -35 -12c-33 0 -60 27 -60 60s27 61 60 61s60 -28 60 -61
|
||||
v-4l156 -90c7 3 14 4 22 4z" />
|
||||
<glyph glyph-name="uniF1AE" unicode="" horiz-adv-x="384"
|
||||
d="M356 130c15 0 28 -12 28 -27s-13 -28 -28 -28c-10 0 -19 6 -24 15h-80l-35 -61c2 -4 3 -7 3 -12c0 -15 -12 -28 -27 -28s-28 13 -28 28c0 5 1 9 3 13l-34 60h-78c-5 -7 -14 -12 -23 -12c-15 0 -27 12 -27 27s12 28 27 28c2 0 5 0 7 -1l35 60l-35 60c-4 -2 -8 -3 -12 -3
|
||||
c-15 0 -28 13 -28 28s13 27 28 27c10 0 19 -5 24 -14h79l37 63c-3 4 -5 9 -5 14c0 15 13 28 28 28s27 -13 27 -28c0 -6 -1 -11 -4 -15l36 -62h78c5 7 13 12 22 12c15 0 28 -13 28 -28s-13 -27 -28 -27c-2 0 -5 0 -7 1l-34 -60l35 -60c4 2 8 2 12 2z" />
|
||||
<glyph glyph-name="uniF1AF" unicode="" horiz-adv-x="344"
|
||||
d="M172 394l172 -99v0v-118c-1 -98 -76 -178 -172 -188c-96 10 -171 90 -172 188v0v118v0l172 100v-1zM172 41c67 10 120 66 121 136v0v6h-121v152l-121 -70v-82h121v-142z" />
|
||||
<glyph glyph-name="uniF1B0" unicode="" horiz-adv-x="441"
|
||||
d="M441 250v0v-216c0 -11 -9 -20 -20 -20v0v0h-401v0c-11 0 -20 9 -20 20v0v216v0c1 10 10 18 20 18h31c32 61 97 102 170 102s137 -41 169 -102h31v0c10 0 19 -8 20 -18zM112 268h217c-26 31 -64 51 -108 51s-83 -20 -109 -51z" />
|
||||
<glyph glyph-name="uniF1B1" unicode="" horiz-adv-x="425"
|
||||
d="M95 33c0 20 10 30 30 30s31 -10 31 -30s-11 -30 -31 -30s-30 10 -30 30zM307 33c0 20 10 30 30 30s30 -10 30 -30s-10 -30 -30 -30s-30 10 -30 30zM333 165v0h-200v-25h270v0c5 0 9 -3 12 -6s5 -7 5 -12v0v-15v0v-1v-2v0c-1 -9 -8 -14 -17 -15v0h-303v0v0
|
||||
c-5 0 -9 2 -12 5s-6 7 -6 12v0v224h-63v0v0h-1c-5 0 -10 2 -13 5s-5 8 -5 13v0v17v0c1 4 3 9 6 12s8 4 12 4v0h1v0h97v0v0c5 0 9 -2 12 -5s5 -8 5 -13v0v-33h54h146h75v0c5 0 9 -2 12 -5s5 -8 5 -13c0 -3 0 -5 -1 -7v0l-75 -130v0c-3 -7 -9 -10 -16 -10z" />
|
||||
<glyph glyph-name="uniF1B2" unicode="" horiz-adv-x="462"
|
||||
d="M74 129l44 45l55 -55l-54 -54c-4 -7 -12 -12 -21 -12v0h-87v0c-5 1 -9 5 -10 10h-1v56h1c1 6 6 11 12 11c1 0 2 -1 3 -1h58zM460 290c2 -1 2 -3 2 -5v0c0 -2 0 -4 -2 -5l-95 -69c-2 -1 -5 -1 -7 0s-4 4 -4 6v30h-53l-38 -37l-54 54l54 54v0c4 3 10 6 15 6v0h76v29
|
||||
c0 2 2 5 4 6s4 0 6 -1zM459 104c2 -1 3 -3 3 -5v0c0 -2 -1 -4 -3 -5l-95 -68c-2 -1 -5 -2 -7 -1s-3 4 -3 6v29h-77v0c-5 0 -10 3 -14 6v0l-189 189h-58c-1 0 -2 -1 -3 -1c-6 0 -12 5 -13 11v0v56v0c1 5 6 9 11 10v0h87v0c9 0 17 -5 21 -12l182 -182h53v30c0 2 1 5 3 6
|
||||
s5 1 7 0z" />
|
||||
<glyph glyph-name="uniF1B3" unicode="" horiz-adv-x="399"
|
||||
d="M390 78c12 -12 13 -31 1 -43c-7 -7 -18 -10 -27 -8c2 -9 -1 -19 -8 -26c-12 -12 -30 -11 -42 1s-12 29 -1 41l-45 45v-31c-19 -15 -42 -24 -68 -24s-50 9 -69 24v31l-45 -45c11 -12 11 -30 -1 -42s-30 -12 -42 0c-7 7 -10 17 -8 26c-9 -2 -19 2 -26 9c-12 12 -12 30 0 42
|
||||
c12 11 30 12 42 1l46 46c-13 19 -20 42 -20 67s7 48 20 67l-46 46c-12 -11 -30 -11 -42 1s-12 31 0 43c7 7 17 10 26 8c-2 9 1 19 8 26c12 12 31 11 43 -1s12 -29 1 -41l46 -46c19 13 42 20 67 20s48 -7 67 -20l46 46c-11 12 -10 30 1 42c12 12 30 12 42 0
|
||||
c7 -7 11 -17 9 -26c9 2 18 -2 25 -9c12 -12 12 -30 0 -42s-29 -12 -41 -1l-46 -46c13 -19 20 -42 20 -67s-7 -48 -20 -67l46 -46c12 11 29 10 41 -1zM166 146c13 0 24 10 24 23s-11 24 -24 24s-23 -11 -23 -24s10 -23 23 -23zM233 146c13 0 24 10 24 23s-11 24 -24 24
|
||||
s-23 -11 -23 -24s10 -23 23 -23z" />
|
||||
<glyph glyph-name="uniF1B4" unicode=""
|
||||
d="M262 227c10 0 17 -3 23 -10s9 -14 9 -23s-3 -18 -8 -24s-13 -9 -23 -9c-6 0 -10 1 -15 3s-10 4 -14 7s-9 6 -13 10s-8 8 -11 12l11 11s8 8 12 11s9 7 14 9s10 3 15 3zM151 204l11 -11c-3 -4 -6 -8 -10 -12s-9 -7 -13 -10s-9 -5 -14 -7s-10 -3 -15 -3c-10 0 -17 3 -23 9
|
||||
s-8 14 -8 23s3 17 8 24s13 10 22 10c5 0 10 -1 15 -3s9 -6 14 -9s9 -7 13 -11zM333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM326 166c3 9 5 19 5 29s-2 19 -5 28s-7 17 -13 23s-14 11 -22 15s-17 6 -28 6
|
||||
c-8 0 -17 -1 -24 -4s-14 -7 -20 -11s-12 -9 -17 -15s-11 -12 -16 -18c-5 6 -11 12 -16 18s-11 11 -17 15s-13 8 -20 11s-15 4 -24 4c-11 0 -21 -2 -29 -6s-15 -9 -21 -15s-10 -14 -13 -23s-5 -19 -5 -29s2 -20 5 -29s8 -17 14 -23s13 -11 21 -15s18 -6 29 -6c9 0 17 1 24 4
|
||||
s13 5 19 9s12 10 17 15l16 16c5 -6 10 -11 16 -16s12 -11 18 -15s13 -6 20 -9s15 -4 23 -4c11 0 19 2 28 6s16 8 22 15s10 15 13 24z" />
|
||||
<glyph glyph-name="uniF1B5" unicode=""
|
||||
d="M232 355h137v-326zM0 355h136l-136 -326v326zM125 94l59 141l87 -206h-57l-26 65h-63z" />
|
||||
<glyph glyph-name="uniF1B6" unicode="" horiz-adv-x="377"
|
||||
d="M199 264h7c1 0 1 1 3 1c-1 10 0 21 -2 31c-2 12 -11 20 -24 22c-20 3 -41 -8 -46 -29c-2 -7 -6 -9 -13 -8c-13 2 -26 3 -39 5c-9 1 -11 4 -9 13c6 27 22 47 47 58c38 17 75 18 113 1c25 -11 38 -31 39 -58c1 -35 2 -69 1 -104c0 -19 4 -36 17 -51c5 -6 5 -10 -1 -15
|
||||
c-11 -9 -22 -20 -33 -29c-6 -5 -12 -5 -17 1c-9 9 -16 18 -25 27c-5 -5 -11 -9 -17 -14c-21 -17 -45 -22 -70 -19c-39 4 -62 30 -63 70c-1 44 22 74 66 87c21 6 44 9 66 11zM198 163c13 20 10 41 10 64c-11 -1 -22 -2 -33 -4c-26 -5 -40 -23 -38 -50c1 -20 15 -32 34 -29
|
||||
c12 2 21 9 27 19zM318 85c6 2 11 0 14 -5s2 -10 -3 -14c-7 -5 -15 -10 -22 -14c-41 -24 -85 -37 -126 -38c-50 0 -88 14 -123 38c-19 13 -35 28 -53 42c-5 4 -6 9 -3 13s7 5 13 2c18 -9 36 -19 54 -28c28 -14 57 -25 89 -28c26 -2 52 0 77 5c27 5 53 14 78 25c2 1 3 1 5 2z
|
||||
M357 124c18 -2 21 -6 19 -24c-2 -19 -9 -35 -22 -49c-2 -2 -3 -4 -5 -4c-3 0 -7 -1 -9 1s-3 6 -2 9c2 8 5 15 7 23c1 4 2 9 3 13s0 6 -4 6h-10v0c-9 -1 -17 -2 -26 -3c-5 -1 -8 0 -10 4c-2 5 0 9 4 12c17 12 35 14 55 12z" />
|
||||
<glyph glyph-name="uniF1B7" unicode="" horiz-adv-x="338"
|
||||
d="M223 355c34 -17 57 -51 57 -89h-222c0 38 23 72 57 89l-18 32c-1 2 0 3 2 4s3 1 4 -1l18 -32c15 6 31 10 48 10s33 -4 48 -10l18 32c1 2 2 2 4 1s3 -2 2 -4zM118 307c5 0 10 4 10 9s-5 9 -10 9s-9 -4 -9 -9s4 -9 9 -9zM220 307c5 0 9 4 9 9s-4 9 -9 9s-10 -4 -10 -9
|
||||
s5 -9 10 -9zM338 238v-103c0 -14 -11 -25 -25 -25s-24 11 -24 25v103c0 14 10 25 24 25s25 -11 25 -25zM279 258v0v-160c0 -10 -5 -18 -13 -23c-4 -2 -8 -3 -13 -3h-18v-55c0 -14 -11 -25 -25 -25c-2 0 -3 1 -5 1c-11 2 -19 12 -19 24v55h-34v-55c0 -12 -8 -22 -19 -24
|
||||
c-2 0 -3 -1 -5 -1c-12 0 -22 9 -24 20c0 2 -1 3 -1 5v55h-18c-10 0 -19 5 -23 14c-2 4 -3 8 -3 12v160h220v0zM25 263c14 0 24 -11 24 -25v-103v-5c-2 -11 -12 -20 -24 -20c-5 0 -10 1 -14 4c-7 4 -11 12 -11 21v103c0 14 11 25 25 25z" />
|
||||
<glyph glyph-name="uniF1B8" unicode="" horiz-adv-x="352"
|
||||
d="M340 259c-69 -38 -58 -137 12 -163c-10 -21 -15 -30 -27 -49c-17 -26 -41 -60 -71 -60c-27 0 -35 18 -71 18s-43 -18 -70 -18c-30 0 -54 30 -71 56c-48 74 -53 160 -23 206c21 33 55 52 86 52c32 0 52 -18 78 -18s41 18 78 18c28 0 58 -16 79 -42zM233 330
|
||||
c-14 -18 -39 -33 -63 -32c-4 24 7 48 21 65c15 18 40 32 62 34c4 -25 -7 -50 -20 -67z" />
|
||||
<glyph glyph-name="uniF1B9" unicode="" horiz-adv-x="365"
|
||||
d="M255 160c6 0 11 -1 15 -5s6 -11 6 -14h-45c2 6 7 19 24 19zM140 136c1 0 14 0 14 -18c0 -15 -9 -18 -16 -18h-41v36h43v0zM331 376c16 0 29 -10 34 -24v-320c-4 -12 -14 -21 -26 -24h-312c-13 3 -23 13 -27 25v318c4 15 18 25 34 25h297zM220 220v-19c0 -1 1 -2 2 -2h64
|
||||
c1 0 3 1 3 2v19c0 1 -2 3 -3 3h-64c-1 0 -2 -2 -2 -3zM193 115c0 1 0 20 -11 32c-3 3 -6 6 -10 7c7 4 15 12 15 29c0 27 -17 43 -46 43h-80c-1 0 -3 -1 -3 -2v-155c0 -1 2 -2 3 -2h81c3 0 15 0 27 6c11 6 24 18 24 42zM312 117c0 1 4 33 -14 53c-10 11 -24 17 -42 17
|
||||
c-32 0 -48 -17 -55 -31c-8 -15 -7 -31 -7 -31c0 -1 -2 -26 15 -44c11 -12 26 -18 46 -18v0h3c9 0 53 2 53 44c0 1 -1 3 -2 3h-28c-1 0 -2 0 -2 -1c-1 -1 0 -1 0 -2c0 0 0 -4 -4 -8s-10 -6 -19 -6v0c-2 0 -9 1 -14 4c-6 4 -9 10 -10 18h77c1 0 3 1 3 2zM148 181
|
||||
c0 -15 -5 -16 -11 -16h-40v30h37h6s8 -1 8 -14z" />
|
||||
<glyph glyph-name="uniF1BA" unicode="" horiz-adv-x="388"
|
||||
d="M95 160c0 46 34 69 100 69s99 -23 99 -69s-33 -70 -99 -70s-100 24 -100 70zM333 252c42 -29 60 -64 54 -105c0 -16 -5 -31 -15 -45s-23 -26 -40 -36c-20 -12 -40 -20 -61 -25c-24 -6 -49 -9 -77 -9c-29 0 -55 3 -79 9c-37 14 -66 30 -86 50s-29 47 -29 82v179h58v-99
|
||||
c38 26 87 40 149 39c49 -3 91 -16 126 -40zM211 64c32 1 60 12 84 34s35 43 33 66c-1 14 -6 27 -13 38s-16 20 -26 27s-21 12 -33 17s-24 7 -34 9s-20 2 -28 2c-44 -2 -78 -13 -101 -33s-33 -42 -31 -66s12 -43 28 -58s34 -25 54 -30s43 -7 67 -6z" />
|
||||
<glyph glyph-name="uniF1BB" unicode=""
|
||||
d="M308 376c33 0 61 -28 61 -61v-246c0 -33 -28 -61 -61 -61h-247c-33 0 -61 28 -61 61v246c0 33 28 61 61 61h247zM296 151v58v3l-2 4l-3 2c-4 3 -25 0 -31 5c-4 4 -5 11 -6 20c-2 17 -3 18 -6 24c-10 21 -37 37 -55 39h-50c-39 0 -72 -33 -72 -72v-83c0 -39 33 -71 72 -71
|
||||
h82c39 0 71 32 71 71zM144 220c-8 0 -14 7 -14 14s6 13 14 13h39c8 0 14 -6 14 -13s-6 -14 -14 -14h-39zM224 166c7 0 14 -7 14 -14s-7 -13 -14 -13h-80c-8 0 -14 6 -14 13s6 14 14 14h80z" />
|
||||
<glyph glyph-name="uniF1BC" unicode="" horiz-adv-x="348"
|
||||
d="M174 192h174v-174h-174v174h-174v174h174v-174z" />
|
||||
<glyph glyph-name="uniF1BD" unicode=""
|
||||
d="M270 254h31v-57v-71h-28l-50 78l2 -78h-32v84v44h1h4h23l3 -5l47 -76zM333 376c20 0 36 -16 36 -36v-131l-63 50v0h-5h-31h-5v-5v-29l-39 31v1l-2 2h-3h-28h-5v-5v-40l-13 11c-1 3 -4 5 -6 8c-5 7 -11 12 -17 16c-12 7 -23 10 -43 10h-43h-3h-2h-5v-2v-10v-124l143 -115
|
||||
h-163c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM137 251v0c1 0 4 -1 5 -2v0v0c1 -1 3 -1 4 -2s3 -1 4 -2s3 -2 4 -3c1 0 0 -1 1 -1l2 -2l2 -2l1 -1l2 -2v0v0c1 -1 2 -3 3 -4c8 -11 12 -24 12 -40c0 -39 -24 -64 -60 -64h-51v123v5h20h5h18h3c11 0 18 -1 25 -3z
|
||||
M144 189c0 23 -11 36 -31 36h-14v-70h14c20 0 31 12 31 34z" />
|
||||
<glyph glyph-name="uniF1BE" unicode=""
|
||||
d="M166 219c9 -19 45 -93 45 -93l129 35v-118c0 -1 -1 -1 -2 -2s-2 -2 -3 -2h-303c-1 0 -2 1 -3 2s-1 1 -1 2v31l169 48l-48 101c82 18 123 11 165 -23c5 -5 3 -8 0 -9l-67 -16l-25 50c-19 3 -46 -2 -56 -6zM335 348c1 0 2 -1 3 -1c1 -1 2 -2 2 -3v-105l-1 1
|
||||
c-87 38 -186 21 -206 15c0 1 -15 33 -15 33h-56l20 -46c-23 -7 -48 -25 -54 -30v132c0 1 1 2 2 3c1 0 1 1 2 1h303zM68 131c-13 -3 -12 6 -12 6c-3 55 33 71 41 73l31 -63s-47 -13 -60 -16zM338 376c17 0 31 -14 31 -31v-306c0 -17 -14 -31 -31 -31h-307
|
||||
c-17 0 -31 14 -31 31v306c0 17 14 31 31 31h307zM350 43v301c0 8 -7 14 -15 14h-303c-8 0 -14 -6 -14 -14v-301c0 -8 6 -14 14 -14h303c8 0 15 6 15 14z" />
|
||||
<glyph glyph-name="uniF1BF" unicode="" horiz-adv-x="436"
|
||||
d="M436 253v-173c0 -4 -1 -6 -6 -6h-106c-4 0 -5 1 -5 5v23c0 4 1 5 5 5h62c4 0 5 1 5 5v16h-5h-62c-4 0 -5 2 -5 6v120c0 4 1 5 5 5h107h4c0 -2 1 -4 1 -6zM387 161c3 0 4 2 4 5v49v11h-25c-1 0 -2 -3 -2 -4v-57c0 -3 1 -4 4 -4h19zM300 259c4 0 5 -1 5 -5v-175v-5h-5h-106
|
||||
c-4 0 -5 1 -5 5v28h5h62c4 0 5 1 5 5v11s-1 6 -6 6c-20 0 -40 -1 -61 -1c-4 0 -5 2 -5 6v120c0 4 1 5 5 5h106zM261 164v59c0 1 -2 3 -3 3h-25c0 -21 1 -41 1 -62c0 -1 1 -3 2 -3h22c1 0 3 2 3 3zM72 305v5h44v-193h-4h-107c-4 0 -5 1 -5 5v132c0 4 1 5 5 5h61c7 0 6 -1 6 6
|
||||
v40zM67 150c4 0 5 1 5 5v66v5h-27v-74c0 -1 2 -2 3 -2h19zM171 259c3 0 4 0 4 -3v-137v-2h-26h-14c-4 0 -4 1 -4 5v94v38c0 4 0 5 4 5h36zM134 276c-1 0 -3 2 -3 3v31h44v-17v-13c0 -3 0 -4 -3 -4h-38z" />
|
||||
<glyph glyph-name="uniF1C0" unicode="" horiz-adv-x="371"
|
||||
d="M186 378c102 0 185 -84 185 -186s-83 -186 -185 -186s-186 84 -186 186s84 186 186 186zM308 292c-3 -5 -29 -39 -88 -63c4 -8 7 -14 10 -22c1 -3 3 -6 4 -9c53 7 105 -4 110 -5c0 37 -14 72 -36 99zM186 350c-13 0 -26 -1 -38 -4c4 -6 34 -45 60 -93c56 21 80 53 83 57
|
||||
c-28 25 -65 40 -105 40zM118 335c-44 -21 -78 -62 -88 -111c7 0 73 0 147 20c-26 47 -55 85 -59 91zM27 192c0 -41 16 -78 41 -106c4 6 47 78 129 104c2 1 4 1 6 2c-4 9 -8 18 -13 27c-79 -24 -156 -22 -163 -22v-5zM186 33c22 0 42 5 61 13c-2 14 -11 62 -33 120h-1
|
||||
c-89 -31 -122 -93 -125 -99c27 -21 61 -34 98 -34zM274 60c36 24 61 63 68 107c-5 2 -49 14 -99 6c21 -57 29 -103 31 -113z" />
|
||||
<glyph glyph-name="uniF1C1" unicode="" horiz-adv-x="384"
|
||||
d="M144 143h240l-67 -115h-240zM376 149l-132 1l-121 208l133 -1zM120 349l66 -115l-120 -208l-66 115z" />
|
||||
<glyph glyph-name="uniF1C2" unicode="" horiz-adv-x="396"
|
||||
d="M117 376l81 -68l-117 -72l-81 64zM0 171l81 65l117 -73l-81 -68zM198 163l118 73l80 -65l-116 -76zM396 300l-80 -64l-118 72l82 68zM198 148l82 -68l35 23v-25l-117 -70l-116 70v25l35 -23z" />
|
||||
<glyph glyph-name="uniF1C3" unicode="" horiz-adv-x="335"
|
||||
d="M33 315c-9 0 -17 -2 -23 -5l-4 -2l1 1l73 72v0l-1 -2c-3 -5 -5 -12 -5 -19v0c0 -9 1 -42 1 -42c0 -2 -2 -3 -4 -3h-38v0zM325 322c5 -27 12 -135 9 -171c-5 -57 -14 -90 -18 -101c-18 -56 -33 -58 -77 -58c-56 0 -73 8 -73 53c0 49 24 50 63 49c6 0 -1 -5 -1 -15
|
||||
s4 -13 -1 -13c-11 0 -27 2 -27 -14c0 -19 10 -19 34 -19c30 0 35 4 35 31c0 45 -13 51 -30 53c-19 2 -38 6 -47 9c-23 8 -22 38 -22 47c0 1 -2 1 -2 0c0 -12 -1 -29 -7 -48c-2 -5 -3 -9 -3 -9c-7 -15 -20 -10 -39 -8s-61 11 -79 19c-8 4 -11 7 -15 16c-11 22 -22 95 -23 105
|
||||
c-2 13 -2 20 -2 20c0 8 1 17 6 24c3 3 5 6 10 8s11 4 19 4h38c8 0 15 6 15 14c0 0 -1 9 -1 18v24c0 8 2 13 5 17c4 6 13 11 20 13c9 3 45 4 68 -5c9 -4 16 -13 18 -24c13 0 36 0 55 -2c24 -3 42 -7 51 -10s18 -11 21 -27zM249 203c10 0 19 -3 27 -6c0 11 -2 28 -20 29
|
||||
c-16 1 -21 -13 -22 -24c5 1 10 1 15 1z" />
|
||||
<glyph glyph-name="uniF1C4" unicode=""
|
||||
d="M348 376c11 0 21 -9 21 -20v-328c0 -11 -10 -20 -21 -20h-94v142h48l7 56h-55v36c0 16 5 27 28 27h29v49c-5 1 -23 3 -43 3c-42 0 -71 -26 -71 -74v-41h-48v-56h48v-142h-177c-11 0 -20 9 -20 20v328c0 11 9 20 20 20h328z" />
|
||||
<glyph glyph-name="uniF1C5" unicode=""
|
||||
d="M333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM110 133c33 0 60 27 60 60s-27 60 -60 60s-60 -27 -60 -60s27 -60 60 -60zM261 133c33 0 60 27 60 60s-27 60 -60 60s-59 -27 -59 -60s26 -60 59 -60z" />
|
||||
<glyph glyph-name="uniF1C6" unicode="" horiz-adv-x="350"
|
||||
d="M350 -18h-65v1h-92v66l73 36l-9 18l-64 -33v25l43 22l-10 18l-33 -18v43h-46v-67l-33 23l-11 -16l44 -31v-73v-13h-147l175 419z" />
|
||||
<glyph glyph-name="uniF1C7" unicode="" horiz-adv-x="415"
|
||||
d="M137 239l67 -67l155 154c3 3 6 4 10 4s6 -1 9 -4l33 -32c5 -5 5 -14 0 -19l-197 -197c-3 -3 -6 -4 -10 -4v0v0h-1c-3 0 -6 2 -8 4l-110 110c-5 5 -5 14 0 19l33 32c3 3 5 4 9 4s7 -1 10 -4zM399 227v-1c19 -19 19 -49 0 -68l-158 -159c-19 -19 -50 -19 -69 0l-158 159
|
||||
c-19 19 -19 49 0 68l158 159c19 19 50 19 69 0l72 -72l-108 -107l-50 50c-7 7 -17 12 -27 12s-20 -5 -27 -12l-32 -32c-7 -7 -11 -17 -11 -27s4 -20 11 -27l109 -109c5 -5 11 -8 18 -10l2 -1h7c10 0 20 4 27 11z" />
|
||||
<glyph glyph-name="uniF1C8" unicode=""
|
||||
d="M333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM215 350c-55 0 -99 -44 -99 -99c0 -36 20 -68 49 -85c14 28 43 47 77 47c20 0 39 -7 54 -19c11 16 18 36 18 57c0 55 -44 99 -99 99zM30 208
|
||||
c0 -41 22 -77 54 -98c9 22 31 36 56 36c6 0 11 0 17 -2c1 4 3 7 4 11c-33 19 -56 55 -56 96c0 28 11 53 28 72c-58 -7 -103 -55 -103 -115zM140 33c29 0 52 23 52 52s-23 52 -52 52s-52 -23 -52 -52s23 -52 52 -52zM242 50c42 0 77 34 77 76s-35 77 -77 77
|
||||
c-38 0 -69 -27 -75 -63c20 -10 34 -31 34 -55c0 -7 -2 -14 -4 -21c13 -9 28 -14 45 -14z" />
|
||||
<glyph glyph-name="uniF1C9" unicode="" horiz-adv-x="399"
|
||||
d="M200 387c110 0 199 -90 199 -200c0 -88 -57 -163 -136 -189c-10 -2 -14 4 -14 9v55c0 19 -6 31 -13 37c44 5 91 22 91 99c0 22 -7 39 -20 53c2 5 9 25 -2 53c0 0 -17 5 -55 -21c-16 4 -33 7 -50 7s-34 -3 -50 -7c-38 26 -55 21 -55 21c-11 -28 -4 -48 -2 -53
|
||||
c-13 -14 -21 -31 -21 -53c0 -77 47 -94 91 -99c-6 -5 -10 -14 -12 -27c-11 -5 -41 -14 -59 17c0 0 -10 19 -30 20c0 0 -20 0 -2 -12c0 0 14 -6 23 -29c0 0 11 -39 67 -27v-34s-3 -11 -13 -9c-79 26 -137 101 -137 189c0 110 90 200 200 200z" />
|
||||
<glyph glyph-name="uniF1CA" unicode=""
|
||||
d="M57 184c-9 7 -16 16 -22 26c-12 22 -17 42 -17 62c0 15 4 30 12 42c10 12 22 18 37 18c11 0 21 -4 30 -10c9 -7 16 -15 21 -26c11 -22 17 -45 17 -67c0 -5 0 -11 -1 -18s-4 -15 -9 -22c-10 -10 -22 -15 -37 -16c-12 0 -22 4 -31 11zM91 100c-13 0 -29 -2 -49 -5
|
||||
c-15 -3 -28 -7 -42 -13v99c15 -15 36 -23 62 -23c6 0 12 0 18 1l-3 -9s-2 -8 -2 -13c0 -8 1 -15 5 -21c3 -6 7 -11 11 -16zM106 84c21 -14 36 -27 46 -37c9 -10 14 -23 14 -37v-1h-140c-14 0 -26 12 -26 26v15c3 4 6 8 9 11c5 4 11 8 16 10s9 4 12 5c12 4 24 5 35 7
|
||||
c12 1 19 1 22 1h12zM343 375c14 0 26 -12 26 -26v-28v-286c0 -14 -12 -26 -26 -26h-145c2 7 3 15 3 23c0 19 -5 34 -13 46c-9 12 -18 22 -30 32l-19 15c-3 3 -6 6 -9 10s-5 8 -5 14s2 11 5 16c3 4 6 9 9 12c6 5 12 9 17 14s9 10 13 16c8 12 13 28 13 48c0 11 -2 21 -4 29
|
||||
c-3 8 -6 15 -10 21s-8 12 -12 16s-9 7 -12 9h34l35 20h-111c-15 0 -31 -2 -48 -5c-17 -4 -33 -12 -49 -25c-2 -2 -3 -4 -5 -6v7v28c0 14 12 26 26 26h65h93h93h66zM363 261v29h-58v58h-28v-58h-59v-29h59v-58h28v58h58z" />
|
||||
<glyph glyph-name="uniF1CB" unicode=""
|
||||
d="M333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM200 178l70 119h-31l-29 -57c-8 -16 -15 -30 -21 -43h-1c-6 14 -12 27 -20 43l-30 57h-30l65 -119v-88h27v88z" />
|
||||
<glyph glyph-name="uniF1CC" unicode="" horiz-adv-x="476"
|
||||
d="M459 222c13 -16 18 -36 16 -57c-2 -24 -12 -47 -30 -62c-17 -14 -41 -23 -71 -23c-22 0 -45 4 -64 15v-10h-310v76h15v66h-15v77h116v-55c19 7 45 7 62 -6v9h29v40h88v-31l8 43h160c-1 -27 -3 -55 -4 -82zM227 272v-32h48v32h-48zM195 105v0v36h-16v54c0 11 -5 39 -37 39
|
||||
c-22 0 -36 -12 -46 -28v78h-76v-37h15v-106h-15v-36h83v36h-7v23c0 34 24 42 24 21v-44h-8v-36h83zM290 105v36h-15v90h-76v-36h15v-54h-15v-36h91zM374 100c53 0 81 33 81 67c2 45 -26 62 -60 62c-13 0 -26 -4 -38 -9l2 16h81l2 48h-122l-17 -92l44 -6c6 7 13 9 18 9
|
||||
c13 0 19 -14 18 -28c-1 -15 -10 -29 -23 -29c-8 0 -18 4 -13 17c4 19 -7 28 -24 28c-26 0 -35 -31 -24 -51c10 -19 40 -32 75 -32z" />
|
||||
<glyph glyph-name="uniF1CD" unicode=""
|
||||
d="M276 216c0 14 -3 28 -9 40h102v-215c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v215h102c-6 -12 -10 -26 -10 -40c0 -51 41 -92 92 -92s92 41 92 92zM333 379c20 0 36 -16 36 -36v-72h-111c-17 22 -44 37 -74 37s-56 -15 -73 -37h-111v72c0 20 16 36 36 36h297z
|
||||
M29 288v0v72c-8 -3 -13 -11 -13 -20v-52h13zM51 288v74h-12v-74h12zM73 288v74h-12v-74h12zM95 340v22h-13v-74h13v22v30zM350 310v30c0 12 -10 22 -22 22h-35c-12 0 -22 -10 -22 -22v-30c0 -12 10 -22 22 -22h35c12 0 22 10 22 22zM184 135c-45 0 -81 36 -81 81
|
||||
s36 81 81 81s81 -36 81 -81s-36 -81 -81 -81zM184 280c-36 0 -64 -28 -64 -64s28 -65 64 -65s65 29 65 65s-29 64 -65 64z" />
|
||||
<glyph glyph-name="uniF1CE" unicode="" horiz-adv-x="357"
|
||||
d="M340 98c29 -24 22 -67 -13 -81c-2 -1 -3 -2 -5 -3h-24c-17 7 -31 18 -35 38c-31 -7 -59 -1 -81 22c12 12 23 22 33 32c9 -3 18 -7 27 -7c12 0 21 8 25 19c4 12 1 22 -8 31c-24 24 -46 47 -70 71c-2 2 -6 4 -8 6c12 13 23 25 36 39c12 -13 22 -25 33 -36
|
||||
c12 -12 22 -25 35 -36c25 -23 38 -49 32 -82c8 -4 17 -8 23 -13zM212 186c14 -12 25 -23 36 -33c-28 -28 -54 -56 -80 -82c-17 -17 -39 -24 -63 -21c-8 1 -11 -1 -14 -8c-5 -15 -17 -22 -31 -28h-22c-21 7 -33 21 -38 42v10c4 21 15 36 37 42c-5 30 1 56 23 77l35 -35
|
||||
c-1 -1 -3 -3 -4 -5c-9 -13 -8 -29 4 -39s27 -11 39 1c24 23 48 47 72 71c3 3 5 6 6 8zM107 228c29 29 56 59 85 87c17 16 40 20 63 15h6c4 15 11 29 27 35c10 3 21 5 31 4c20 -2 36 -21 38 -43c2 -23 -10 -39 -41 -52c6 -30 -1 -56 -23 -78l-34 34c3 5 7 11 9 17
|
||||
c3 12 -3 25 -14 31c-11 7 -24 6 -35 -4c-12 -11 -23 -23 -35 -35c-15 -14 -29 -29 -44 -44zM38 275c-28 10 -41 30 -35 60c4 19 22 33 41 35c25 2 39 -9 53 -40c29 5 53 -2 73 -22l-35 -35c0 0 -1 2 -3 3c-13 10 -29 8 -40 -4c-10 -12 -9 -27 2 -39c23 -24 47 -48 70 -72
|
||||
c3 -3 4 -5 7 -8c-12 -11 -22 -22 -35 -35c-11 12 -23 24 -34 36c-12 12 -23 24 -35 35c-25 23 -37 51 -29 86z" />
|
||||
<glyph glyph-name="uniF1CF" unicode="" horiz-adv-x="417"
|
||||
d="M354 210c41 -10 63 -29 63 -68c0 -48 -40 -66 -99 -66c-83 0 -112 38 -127 84l-15 48c-11 35 -25 62 -67 62c-29 0 -59 -21 -59 -80c0 -46 24 -75 57 -75c37 0 62 28 62 28l15 -42s-26 -25 -80 -25c-67 0 -104 39 -104 112c0 75 37 120 107 120c64 0 96 -23 116 -85
|
||||
l16 -48c11 -35 32 -60 80 -60c32 0 49 7 49 25c0 14 -8 23 -32 29l-33 8c-40 10 -55 31 -55 63c0 52 42 68 85 68c49 0 78 -18 82 -61l-48 -6c-2 21 -14 30 -37 30c-21 0 -34 -10 -34 -26c0 -14 6 -23 27 -28z" />
|
||||
<glyph glyph-name="uniF1D0" unicode=""
|
||||
d="M341 376c15 0 28 -11 28 -26v-316c0 -15 -13 -26 -28 -26h-314c-15 0 -27 11 -27 26v316c0 15 12 26 27 26h314zM109 62v176h-54v-176h54zM82 262c17 0 32 14 32 32c0 17 -15 32 -32 32c-18 0 -32 -15 -32 -32c0 -18 14 -32 32 -32zM314 62v0v97c0 47 -10 83 -65 83
|
||||
c-27 0 -45 -14 -52 -28h-1v24h-52v-176h54v87c0 23 5 45 33 45s28 -26 28 -46v-86h55z" />
|
||||
<glyph glyph-name="uniF1D1" unicode=""
|
||||
d="M346 376c13 -5 23 -18 23 -33v-299c0 -20 -16 -36 -36 -36h-299c-16 0 -29 11 -34 25v321c3 10 10 18 20 22h326zM276 241v22h-68l-22 -84h-1l-22 84h-68v-22h7c3 0 6 -4 6 -6v-89c0 -2 -3 -6 -6 -6h-7v-21h54v21h-13v94v0l32 -115h24l32 115v0v-94h-12v-21h64v21h-7
|
||||
c-3 0 -6 4 -6 6v89c0 2 3 6 6 6h7z" />
|
||||
<glyph glyph-name="uniF1D2" unicode="" horiz-adv-x="454"
|
||||
d="M375 187c-40 0 -73 32 -73 72s33 72 73 72s72 -32 72 -72s-32 -72 -72 -72zM147 238c0 43 22 65 65 65s65 -22 65 -65s-22 -65 -65 -65s-65 22 -65 65zM64 161c-32 0 -58 27 -58 59s26 58 58 58s59 -26 59 -58s-27 -59 -59 -59zM64 148c37 0 65 -32 65 -66v-23
|
||||
c0 -3 -3 -6 -6 -6h-2h-113h-2c-3 0 -6 3 -6 6v23c0 34 27 66 64 66zM212 159c41 0 71 -37 71 -74v-25c0 -4 -2 -7 -6 -7h-2h-126h-2c-4 0 -7 3 -7 7v25c0 37 31 74 72 74zM375 170c46 0 79 -40 79 -82v-27c0 -4 -3 -8 -7 -8h-3h-139h-2c-4 0 -8 4 -8 8v27c0 42 34 82 80 82z
|
||||
" />
|
||||
<glyph glyph-name="uniF1D3" unicode=""
|
||||
d="M185 272c45 0 82 -37 82 -82s-37 -82 -82 -82s-82 37 -82 82s37 82 82 82zM333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM185 66c69 0 124 55 124 124s-55 124 -124 124s-125 -55 -125 -124
|
||||
s56 -124 125 -124z" />
|
||||
<glyph glyph-name="uniF1D4" unicode="" horiz-adv-x="348"
|
||||
d="M348 235c0 -83 -67 -136 -171 -137v-23c0 -28 -16 -54 -41 -65c-6 -3 -17 -5 -28 -5c-10 0 -22 2 -35 8v65c21 -15 41 -7 41 11v181h63v-113c30 0 112 8 112 78c0 62 -60 85 -115 85c-57 0 -114 -27 -114 -85c0 -18 9 -45 18 -53l-41 -43c-24 22 -37 66 -37 96
|
||||
c0 85 72 144 174 144c104 0 174 -58 174 -144z" />
|
||||
<glyph glyph-name="uniF1D5" unicode="" horiz-adv-x="399"
|
||||
d="M282 373v0v-133c-7 6 -147 133 -152 138c23 8 46 13 70 13c29 0 56 -6 82 -18zM97 21v0c-37 23 -67 57 -83 97c2 2 76 69 83 75v-172zM0 192v0c0 75 42 143 109 177c2 -2 72 -65 75 -68c-3 -3 -172 -156 -177 -161c-5 17 -7 35 -7 52zM118 105v0h261
|
||||
c-31 -63 -92 -106 -161 -112h-37c-22 2 -43 8 -63 17v95zM303 362v0c59 -36 96 -101 96 -170c0 -22 -4 -45 -11 -66h-85v236z" />
|
||||
<glyph glyph-name="uniF1D6" unicode="" horiz-adv-x="399"
|
||||
d="M200 392c110 0 199 -90 199 -200s-89 -200 -199 -200c-20 0 -39 3 -57 8c8 12 16 28 20 43c2 9 14 55 14 55c7 -13 27 -25 49 -25c64 0 108 59 108 137c0 59 -51 115 -127 115c-95 0 -142 -68 -142 -125c0 -34 13 -66 41 -77c5 -2 9 0 10 5c1 4 3 13 4 17c1 5 1 7 -3 11
|
||||
c-8 9 -13 22 -13 39c0 50 37 95 98 95c54 0 83 -32 83 -76c0 -58 -25 -106 -63 -106c-21 0 -37 17 -32 38c6 25 18 53 18 71c0 16 -9 30 -27 30c-21 0 -39 -22 -39 -52c0 -19 7 -31 7 -31s-22 -93 -26 -109c-4 -15 -4 -32 -3 -46c-70 31 -120 101 -120 183
|
||||
c0 110 90 200 200 200z" />
|
||||
<glyph glyph-name="uniF1D7" unicode="" horiz-adv-x="435"
|
||||
d="M424 291c27 -2 4 -60 -52 -75c1 -7 1 -15 1 -23v-1c0 -99 -80 -179 -187 -179s-186 79 -186 178v1c0 99 80 179 187 179c19 0 36 -3 53 -7v-105c-3 2 -6 4 -10 5c-46 16 -96 -7 -119 -45l-1 -1c-24 -39 -12 -83 33 -98c46 -16 95 7 119 45l1 1c8 13 12 27 12 40v0v145
|
||||
c3 -1 6 -3 8 -4s4 -3 6 -4c24 -15 85 -53 135 -52z" />
|
||||
<glyph glyph-name="uniF1D8" unicode="" horiz-adv-x="458"
|
||||
d="M458 197c0 -18 -10 -35 -25 -45c1 -5 2 -10 2 -15c0 -76 -92 -138 -206 -138s-206 62 -206 138c0 5 0 11 1 16c-15 10 -24 26 -24 44c0 29 24 53 53 53c13 0 24 -5 34 -13c36 23 83 37 136 38l37 105l89 -22c6 16 22 27 40 27c24 0 43 -19 43 -43s-19 -43 -43 -43
|
||||
s-43 19 -43 43l-75 18l-31 -85c50 -2 97 -16 131 -38h1c10 8 21 13 34 13c29 0 52 -24 52 -53zM299 129c18 0 32 14 32 32s-14 33 -32 33s-33 -15 -33 -33s15 -32 33 -32zM305 73c3 3 3 9 0 12s-9 3 -12 0c0 0 -20 -20 -65 -20c-44 0 -62 19 -62 20c-3 3 -9 4 -12 1
|
||||
s-4 -9 -1 -12c1 -1 22 -25 75 -25s76 23 77 24zM131 161c0 -18 14 -32 32 -32s33 14 33 32s-15 33 -33 33s-32 -15 -32 -33z" />
|
||||
<glyph glyph-name="uniF1D9" unicode="" horiz-adv-x="390"
|
||||
d="M103 162c0 0 -20 3 -44 14l275 3c-25 -12 -48 -16 -48 -16h-74s-6 -1 -8 -4s-2 -9 -2 -9v-9c-11 8 -18 21 -24 21h-75zM25 166c-2 1 -4 2 -5 4c2 -1 3 -2 5 -4zM39 368v-182c-11 6 -17 11 -21 13v180c0 6 4 10 10 10h332c6 0 10 -4 10 -10v-180c-4 -3 -9 -7 -20 -13v182
|
||||
h-311zM388 202c3 -2 1 -6 0 -8s-7 -17 -33 -34s-51 -27 -51 -27s16 -51 1 -89s-48 -48 -64 -48s-39 12 -39 34v77c6 -1 13 -2 21 -2c18 0 31 8 42 19s19 32 10 35s-12 -3 -20 -14c-7 -9 -22 -17 -44 -9c-3 1 -6 3 -9 5v9s0 6 2 9s7 4 8 4h74s23 3 48 15c1 1 3 1 4 2
|
||||
c4 2 9 4 12 6c11 6 16 10 20 13l6 3c6 2 9 2 12 0zM265 124c-11 -11 -24 -19 -42 -19c-8 0 -15 1 -21 2c-8 2 -14 4 -14 4v-83s-24 -33 -40 -33s-48 9 -63 47s1 89 1 89s-25 11 -51 28c-4 2 -7 5 -10 7c-2 2 -3 3 -5 4c-14 12 -19 21 -19 23c-1 2 -3 6 0 8s7 2 13 0
|
||||
c1 -1 2 -1 4 -2c4 -2 10 -7 21 -13c4 -2 7 -5 12 -7c2 -1 6 -2 8 -3c24 -11 44 -14 44 -14h75c6 0 13 -13 24 -21c3 -2 6 -4 9 -5c22 -8 37 0 44 9c8 11 11 17 20 14s1 -24 -10 -35zM251 272c26 0 47 -20 47 -46s-21 -47 -47 -47s-46 21 -46 47s20 46 46 46zM141 272
|
||||
c26 0 47 -20 47 -46s-21 -47 -47 -47s-46 21 -46 47s20 46 46 46z" />
|
||||
<glyph glyph-name="uniF1DA" unicode="" horiz-adv-x="394"
|
||||
d="M383 150c7 -15 11 -32 11 -49c0 -60 -49 -109 -109 -109c-19 0 -36 5 -51 13c-11 -2 -23 -3 -35 -3c-104 0 -188 84 -188 188c0 13 2 26 4 38c-10 16 -15 35 -15 55c0 60 49 109 109 109c21 0 41 -6 58 -17c10 2 21 3 32 3c104 0 188 -84 188 -188c0 -14 -1 -27 -4 -40z
|
||||
M296 100c9 12 13 27 13 42c0 13 -3 24 -8 33s-12 16 -21 22s-19 11 -32 15c-12 4 -27 8 -42 11c-12 3 -21 5 -26 6s-10 4 -15 6s-8 6 -11 9s-4 7 -4 11c0 7 4 13 12 18s19 8 32 8c14 0 26 -2 32 -7c7 -5 12 -12 17 -21c4 -7 7 -12 11 -15s9 -5 16 -5c8 0 14 3 19 8
|
||||
s8 11 8 18s-2 14 -6 21s-10 15 -18 21s-19 11 -31 15s-27 6 -44 6c-21 0 -39 -2 -55 -8s-29 -15 -37 -26s-12 -24 -12 -38c0 -15 4 -28 12 -38s19 -17 32 -23s29 -11 49 -15c14 -3 26 -5 35 -8c8 -3 15 -7 20 -12s7 -10 7 -17c0 -9 -5 -18 -14 -24c-10 -7 -22 -10 -38 -10
|
||||
c-11 0 -20 2 -27 5s-12 7 -16 12s-8 11 -11 19c-3 7 -7 13 -11 17c-5 4 -10 5 -16 5c-8 0 -15 -2 -20 -7s-8 -11 -8 -18c0 -11 4 -22 12 -34c8 -11 18 -20 31 -27c18 -9 41 -14 68 -14c23 0 41 3 58 10s30 17 39 29z" />
|
||||
<glyph glyph-name="uniF1DB" unicode="" horiz-adv-x="390"
|
||||
d="M173 165c98 -37 75 -98 23 -98s-89 32 -89 32l-30 -70c21 -11 43 -19 56 -23l-33 -8c-15 -4 -31 6 -35 21l-64 268c-4 16 6 31 21 35l77 19c-7 -8 -12 -17 -16 -26c0 -1 -1 -1 -1 -2c-1 -2 -1 -4 -2 -6c0 -1 -1 -2 -1 -3c-1 -2 -2 -4 -2 -6v-4c0 -2 -1 -4 -1 -6
|
||||
c0 -1 -1 -2 -1 -3v-7v-3v-10s1 -6 1 -8v-1c0 -2 1 -6 2 -8v0c1 -2 1 -5 2 -7c0 -1 1 0 1 -1c1 -2 2 -5 3 -7v-1c1 -2 3 -4 4 -6c0 -1 1 0 1 -1c1 -2 3 -4 5 -6v-1c1 -2 3 -4 5 -6c0 -1 1 0 1 -1l6 -6v-1c2 -2 4 -2 6 -4c1 0 0 -2 1 -2c2 -2 5 -3 7 -5l1 -1
|
||||
c15 -11 33 -20 52 -27zM390 97c4 -15 -7 -31 -22 -35l-56 -14c9 14 17 33 18 56c1 29 -10 60 -39 85v0c-2 1 -3 3 -5 4l-1 1c-2 1 -3 3 -5 4l-1 1c-2 1 -4 3 -6 4h-1c-2 1 -4 3 -6 4c-1 0 -1 1 -2 1l-6 3h-1c-2 1 -5 3 -8 4h-2l-6 3c-1 0 -1 1 -2 1l-9 3
|
||||
c-100 33 -71 88 -20 85c54 -3 74 -25 74 -25l24 67c-2 1 -5 2 -7 3c-1 0 -1 1 -2 1c-2 1 -3 1 -5 2c-1 0 -1 1 -2 1c-2 1 -5 2 -7 3c-1 0 -1 1 -2 1c-1 1 -3 1 -4 2h-3c-1 1 -3 2 -4 2s-1 1 -2 1c-2 1 -3 0 -5 1c-1 0 -1 1 -2 1s-3 1 -4 1h-2c-2 0 -3 2 -5 2
|
||||
c-7 2 -13 2 -17 3l56 13c16 4 30 -5 34 -21z" />
|
||||
<glyph glyph-name="uniF1DC" unicode="" horiz-adv-x="379"
|
||||
d="M302 224c4 1 8 4 12 4c7 0 14 0 20 -3c10 -5 11 -16 2 -22c-7 -5 -16 -8 -24 -11c-18 -7 -21 -13 -12 -30c15 -29 37 -51 70 -59c3 -1 9 -4 9 -6c0 -5 -3 -11 -6 -13c-11 -5 -23 -9 -35 -12c-7 -2 -10 -4 -12 -11c-3 -15 -6 -17 -20 -14c-23 5 -44 0 -63 -14
|
||||
c-39 -28 -69 -27 -108 1c-19 14 -38 18 -61 13c-15 -3 -17 -1 -21 14c-1 6 -4 10 -11 11c-11 2 -24 6 -34 11c-4 2 -7 8 -8 13c0 2 7 7 11 8c36 10 57 34 72 66c3 8 0 13 -6 17c-6 3 -12 4 -18 7c-4 2 -8 4 -11 6c-7 4 -13 10 -9 19c3 8 14 12 23 9c5 -2 10 -4 15 -5
|
||||
c7 -1 10 1 10 9c-1 18 -1 36 0 54c3 38 26 65 60 78c50 19 109 4 136 -44c9 -17 10 -35 11 -54c-1 -12 -1 -23 -2 -35c-1 -8 4 -9 10 -7z" />
|
||||
<glyph glyph-name="uniF1DD" unicode="" horiz-adv-x="399"
|
||||
d="M200 392c110 0 199 -90 199 -200c0 -71 -37 -133 -92 -168c-8 17 -19 32 -33 45c-29 26 -66 41 -105 41c-25 0 -48 -6 -70 -17c-18 -9 -34 -21 -47 -36c-33 36 -52 83 -52 135c0 110 90 200 200 200zM294 114c4 6 3 15 -3 19c-36 25 -77 38 -121 38c-25 0 -51 -4 -74 -13
|
||||
c-7 -3 -11 -10 -8 -17s11 -11 18 -8c20 8 42 11 64 11c38 0 75 -11 106 -33c2 -2 4 -2 7 -2c4 0 8 1 11 5zM323 176c4 7 3 16 -4 20c-45 28 -96 43 -149 43c-29 0 -58 -4 -86 -13c-8 -3 -13 -11 -10 -19s11 -13 19 -10c25 8 51 12 77 12c47 0 93 -13 133 -38c2 -2 5 -2 8 -2
|
||||
c5 0 9 2 12 7zM338 229c6 0 11 3 14 8c5 8 2 18 -6 23c-53 31 -114 48 -176 48c-34 0 -67 -5 -99 -14c-9 -3 -15 -12 -12 -21s12 -14 21 -11c29 9 60 13 90 13c56 0 112 -16 160 -44c3 -2 5 -2 8 -2zM225 16c6 -6 11 -13 15 -20c-13 -3 -26 -4 -40 -4c-32 0 -63 7 -90 21
|
||||
c6 6 13 12 21 16c12 6 25 9 38 9c21 0 41 -8 56 -22z" />
|
||||
<glyph glyph-name="uniF1DE" unicode="" horiz-adv-x="425"
|
||||
d="M420 109c6 -21 7 -44 0 -65c-15 -44 -45 -72 -92 -78c-30 -4 -56 5 -77 28c-17 19 -26 41 -28 66c0 4 0 8 -1 12c-4 21 -18 30 -39 27c-24 -3 -47 -8 -71 -7c-13 0 -26 4 -38 10c-19 10 -28 27 -31 48s0 42 4 63c2 8 3 16 4 25c1 17 -8 29 -21 39c1 1 3 0 4 0
|
||||
c23 -3 39 -23 38 -46c-1 -22 -2 -43 3 -65c1 -4 2 -8 3 -11c4 -12 15 -14 26 -15c20 -1 40 3 60 6c10 2 18 3 29 5c-9 6 -17 11 -25 16c-40 24 -65 57 -73 104c-1 9 -4 17 -6 26c-5 26 -21 41 -47 45c-13 2 -25 1 -38 -1c-1 0 -3 -1 -4 0c-1 2 1 2 2 3c24 12 49 15 75 8
|
||||
c14 -4 22 -15 28 -27c6 -13 9 -27 13 -41c1 -4 3 -8 4 -12c6 -20 19 -35 38 -44s39 -15 60 -18c2 0 4 -2 4 2c-1 11 -1 22 -2 33c-2 21 -14 35 -32 45c-13 8 -26 17 -38 27c-30 27 -42 60 -35 100v2h3c-1 -24 5 -45 23 -63c10 -10 22 -15 34 -21c32 -14 58 -35 77 -64
|
||||
c9 -14 13 -29 16 -44c0 -1 1 -2 1 -4c7 13 18 23 26 34c14 20 12 37 -4 55c-12 13 -28 21 -44 28c-2 1 -5 2 -7 3c-21 8 -31 25 -29 48c0 3 0 5 2 8c1 -5 2 -9 3 -14c4 -17 13 -27 30 -31c25 -6 50 -16 69 -34c12 -11 20 -23 23 -39c2 -14 0 -26 -6 -38
|
||||
c-4 -9 -10 -18 -14 -27c-5 -10 -4 -20 0 -30c3 -6 7 -9 14 -9c41 -2 75 -28 86 -68z" />
|
||||
<glyph glyph-name="uniF1DF" unicode="" horiz-adv-x="337"
|
||||
d="M247 12v144h34v-179h-281v179h33l-2 -144h216zM53 35v36h169v-36h-169zM53 99l4 37l169 -17l-4 -36zM62 173l10 36l164 -46l-10 -36zM93 260l19 31l145 -87l-19 -32zM293 232l-30 -22l-98 138l29 22zM301 234l-28 167l36 6l28 -167z" />
|
||||
<glyph glyph-name="uniF1E0" unicode="" horiz-adv-x="497"
|
||||
d="M466 238c0 -20 -16 -36 -36 -36s-37 16 -37 36s17 36 37 36s36 -16 36 -36zM429 306c37 0 68 -31 68 -68s-31 -67 -68 -67l-64 -47c-2 -25 -24 -46 -50 -46c-24 0 -44 17 -49 40l-190 76c-8 -4 -17 -7 -26 -7c-28 0 -50 23 -50 51s22 50 50 50c24 0 44 -17 49 -40
|
||||
l190 -76c8 4 17 7 26 7h5l42 60c0 37 30 67 67 67zM429 283c-25 0 -45 -20 -45 -45s20 -45 45 -45s45 20 45 45s-20 45 -45 45zM50 275c-20 0 -37 -17 -37 -37s17 -37 37 -37c3 0 5 -1 8 0l-15 7v0c-14 6 -22 23 -16 38s23 21 38 16v0l18 -7c-6 12 -19 20 -33 20zM315 166
|
||||
c-3 0 -5 0 -8 -1l15 -6c15 -6 23 -24 17 -39s-24 -22 -39 -16c-6 2 -12 5 -18 7c6 -12 19 -19 33 -19c20 0 37 17 37 37s-17 37 -37 37z" />
|
||||
<glyph glyph-name="uniF1E1" unicode="" horiz-adv-x="399"
|
||||
d="M357 314c26 -34 42 -76 42 -122c0 -110 -89 -200 -199 -200c-78 0 -145 44 -178 109h87c41 0 70 22 70 58c0 76 -87 45 -87 72c0 13 10 18 29 18h32h29c17 0 36 -4 36 -22v-60c0 -44 35 -66 70 -66c36 0 69 22 69 66v147v0zM200 392c42 0 80 -13 112 -35v-191
|
||||
c0 -13 -11 -24 -24 -24v0c-13 0 -24 11 -24 24v57c0 46 -24 65 -54 65h-98c-36 0 -65 -23 -65 -59c0 -18 8 -45 45 -52c30 -5 42 -4 42 -20s-17 -15 -40 -15h-88c-4 16 -6 33 -6 50c0 110 90 200 200 200v0z" />
|
||||
<glyph glyph-name="uniF1E2" unicode="" horiz-adv-x="394"
|
||||
d="M193 244c-24 -18 -47 -38 -74 -52c-4 -2 -8 -4 -13 -5c-12 -3 -20 -14 -20 -27c0 -12 9 -24 21 -27s25 2 31 13c7 15 20 25 33 34c5 4 7 3 11 -2c4 9 9 18 13 27c1 -1 1 -1 1 -2c-7 -25 -14 -52 -22 -77c-2 -6 -5 -10 -10 -13c-14 -9 -18 -28 -9 -43c9 -14 27 -19 42 -11
|
||||
s21 26 14 41c-3 5 -3 11 -2 16c4 14 6 28 14 41c3 6 7 11 13 15c7 4 11 3 16 -3c6 -7 9 -15 8 -25c-1 -13 7 -25 19 -29s25 1 32 11c7 11 7 24 -2 34c-10 12 -21 23 -30 35c-16 21 -17 43 -4 66c7 12 15 25 22 38c6 11 15 21 26 27c10 5 19 6 29 0c6 -4 12 -7 19 -11
|
||||
c15 -9 23 -22 23 -39v-168c0 -17 -8 -30 -22 -38c-51 -29 -101 -58 -152 -86c-15 -8 -30 -8 -45 0c-51 29 -102 57 -153 86c-14 8 -22 21 -22 37v170c0 16 7 29 21 37c51 29 103 58 155 87c13 7 28 7 42 0c8 -4 15 -8 23 -12c6 -3 8 -9 9 -16c1 -12 -1 -23 -7 -33
|
||||
c-7 -12 -14 -25 -21 -37c-14 -22 -33 -36 -59 -40c-16 -3 -33 -1 -49 1c-12 1 -23 -5 -27 -16s0 -24 10 -30s23 -4 31 5c5 6 13 5 20 6c5 1 10 2 15 2c2 0 3 0 5 1c8 4 16 9 24 13z" />
|
||||
<glyph glyph-name="uniF1E3" unicode=""
|
||||
d="M333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM270 68v0v44c-14 -9 -28 -14 -42 -14c-8 0 -15 2 -21 6c-5 3 -8 6 -10 11s-2 15 -2 31v71h66v44h-66v70h-40c-2 -14 -4 -27 -9 -36s-11 -16 -19 -23
|
||||
s-17 -12 -28 -16v-39h30v-97c0 -13 1 -22 4 -29s8 -13 15 -19s15 -11 25 -14s20 -5 33 -5c11 0 22 1 32 3s20 6 32 12z" />
|
||||
<glyph glyph-name="uniF1E4" unicode="" horiz-adv-x="394"
|
||||
d="M394 314c-11 -16 -24 -31 -40 -42v-10c0 -107 -81 -230 -230 -230c-46 0 -88 13 -124 36c6 -1 12 -1 19 -1c38 0 73 13 101 35c-35 1 -66 24 -76 56c5 -1 10 -2 15 -2c7 0 15 1 22 3c-37 7 -65 40 -65 79v2c11 -6 23 -11 36 -11c-22 15 -35 40 -35 68c0 15 3 28 10 40
|
||||
c40 -49 100 -81 167 -84c-1 6 -2 12 -2 18c0 45 36 81 81 81c23 0 44 -9 59 -25c18 4 35 10 51 19c-6 -19 -18 -34 -35 -44c16 2 31 6 46 12z" />
|
||||
<glyph glyph-name="uniF1E5" unicode=""
|
||||
d="M333 376c20 0 36 -15 36 -35v-298c0 -20 -16 -35 -36 -35h-297c-20 0 -36 15 -36 35v298c0 20 16 35 36 35h297zM309 259c1 7 2 15 0 22c-1 3 -2 6 -4 8c-9 11 -29 11 -42 9c-11 -2 -48 -17 -60 -55h6c14 0 22 -2 25 -12c1 -4 2 -8 1 -14c-1 -10 -6 -20 -12 -31
|
||||
c-7 -12 -19 -37 -35 -20c-2 2 -4 6 -5 6v0c-10 15 -9 42 -12 59c-2 11 -4 26 -8 37c0 1 0 5 -1 5v0c-4 10 -11 17 -18 19c-9 3 -22 -2 -29 -6c-22 -13 -39 -31 -59 -46v-1c5 -1 4 -2 5 -4c2 -3 4 -5 8 -6c13 -2 25 11 34 -3c1 -2 2 -3 3 -5h-1l1 -1v1v-1c3 -7 4 -15 7 -22
|
||||
c5 -13 8 -26 12 -41c4 -16 8 -37 17 -52c5 -9 11 -16 19 -19c11 -5 28 2 36 7c23 13 40 32 55 52c35 46 54 99 57 114z" />
|
||||
<glyph glyph-name="uniF1E6" unicode="" horiz-adv-x="368"
|
||||
d="M368 203h-205v149l205 29v-178zM149 349v-146h-149v125zM0 183h149v-148l-149 22v126zM163 33v150h205v-180z" />
|
||||
<glyph glyph-name="uniF1E7" unicode="" horiz-adv-x="399"
|
||||
d="M147 277c-131 -136 -103 -210 -103 -210c-27 34 -44 78 -44 125c0 53 21 100 54 136c0 0 34 -15 93 -51zM200 315c-80 58 -130 29 -130 29c35 30 81 48 130 48s94 -18 129 -48c0 0 -49 29 -129 -29v0v0zM199 233c124 -92 145 -179 145 -179c-36 -38 -87 -62 -144 -62
|
||||
s-109 24 -145 62c0 0 33 97 144 179zM399 192c0 -47 -16 -91 -43 -125c0 0 27 74 -104 210c59 36 94 51 94 51c33 -36 53 -83 53 -136z" />
|
||||
<glyph glyph-name="uniF1E8" unicode="" horiz-adv-x="512"
|
||||
d="M460 50l-53 6l6 46l53 -6zM512 328l-50 -202l-39 6l-1 207zM332 292c35 -27 53 -61 53 -103c0 -43 -18 -78 -53 -105c-30 -22 -67 -35 -111 -39h-56c-44 4 -82 17 -111 39c-36 27 -54 62 -54 105c0 42 18 76 54 103c35 27 82 41 139 41c56 0 103 -14 139 -41zM305 224
|
||||
l7 10h-48h-50l2 -10l30 -5c-1 -6 -16 -20 -46 -43c-23 28 -39 51 -50 67l35 4l2 8c-16 1 -36 1 -60 0c-34 1 -52 1 -54 0v-10l33 -5c6 -5 18 -17 37 -40c18 -22 27 -36 28 -39l1 -15v-7c0 -12 -1 -20 -2 -21c-2 -2 -6 -2 -15 -3l-17 -1l-2 -10h52h59l1 11l-36 1l-3 22l1 9
|
||||
l1 14c2 5 13 16 33 32c19 16 30 24 34 25z" />
|
||||
<glyph glyph-name="uniF1E9" unicode="" horiz-adv-x="344"
|
||||
d="M212 133c7 7 17 4 17 4l101 -32s14 -2 14 -12c0 -7 -4 -15 -4 -15l-43 -61s-7 -6 -15 -6s-17 12 -17 12l-54 90s-6 13 1 20zM207 179c-5 8 0 17 0 17l57 89s6 12 16 11c9 -1 13 -8 13 -8l48 -57s4 -9 2 -16s-16 -13 -16 -13l-100 -29s-15 -3 -20 6zM159 214
|
||||
c-11 -3 -19 6 -19 6l-87 118s-12 14 -6 24c4 7 12 10 12 10l84 31c4 1 8 5 20 -3c8 -5 9 -23 9 -23l1 -143s-2 -17 -14 -20zM139 156c0 -15 -8 -15 -12 -18l-105 -23s-15 -6 -20 2c-3 6 -2 18 -2 18l6 73c0 5 5 9 10 12c6 4 20 -1 20 -1l90 -46s13 -6 13 -17zM165 117
|
||||
c8 -4 9 -15 9 -15l-2 -105s1 -14 -8 -17c-6 -2 -15 0 -15 0l-70 23c-5 2 -9 5 -11 12s7 19 7 19l70 78s11 10 20 5z" />
|
||||
<glyph glyph-name="uniF1EA" unicode=""
|
||||
d="M160 88v67h16v-88h-16v9c-6 -7 -13 -11 -19 -11c-5 0 -8 3 -10 7c-1 3 -1 7 -1 13v70h16v-65v-6c0 -2 2 -4 4 -4c3 0 6 3 10 8zM71 169v17h56v-17h-19v-102h-18v102h-19zM181 247c-5 0 -8 4 -8 12v38c0 8 3 12 8 12s7 -4 7 -12v-38c0 -8 -2 -12 -7 -12zM222 156
|
||||
c6 0 11 -3 13 -10c1 -4 2 -9 2 -17v-36c0 -8 -1 -14 -2 -18c-2 -7 -7 -10 -13 -10s-11 3 -16 10v-8h-16v119h16v-39c5 6 10 9 16 9zM221 92v38c0 8 -2 12 -7 12c-3 0 -5 -1 -8 -4v-54c3 -3 5 -4 8 -4c5 0 7 4 7 12zM274 156c8 0 15 -3 19 -9c3 -4 5 -11 5 -20v-19h-33v-16
|
||||
c0 -8 3 -12 9 -12c4 0 6 3 7 7v10h17v-2c0 -5 -1 -8 -1 -10c-1 -4 -2 -7 -4 -10c-4 -6 -11 -10 -19 -10s-14 4 -19 10c-3 4 -6 11 -6 20v31c0 9 2 17 5 21c5 6 12 9 20 9zM281 122v8c0 8 -3 12 -8 12s-8 -4 -8 -12v-8h16zM333 376c20 0 36 -16 36 -36v-296
|
||||
c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM218 323v-71c0 -6 0 -11 1 -14c2 -5 6 -6 11 -6c6 0 12 4 18 11v-10h17v90h-17v-69c-4 -5 -7 -7 -10 -7c-2 0 -4 0 -4 3v7v66h-16zM157 293v-31c0 -10 2 -17 5 -21c4 -6 11 -9 19 -9s14 3 19 9
|
||||
c3 4 5 11 5 21v31c0 10 -2 17 -5 22c-5 6 -11 9 -19 9s-15 -3 -19 -9c-3 -5 -5 -12 -5 -22zM113 353h-19l12 -33c6 -17 9 -29 11 -38v-49h18v49l21 71h-18l-12 -47zM313 61c5 20 4 42 4 63s1 43 -4 63c-3 14 -15 24 -29 26c-33 4 -67 4 -100 4s-66 0 -99 -4
|
||||
c-14 -2 -26 -12 -29 -26c-5 -20 -5 -42 -5 -63s0 -43 5 -63c3 -14 15 -24 29 -26c33 -4 66 -3 99 -3s67 -1 100 3c14 2 26 12 29 26z" />
|
||||
<glyph glyph-name="uniF1EB" unicode="" horiz-adv-x="281"
|
||||
d="M76 133c37 -3 66 -23 96 -41c21 -13 44 -20 69 -20c12 0 22 4 31 12c4 3 6 2 7 -3c9 -37 -21 -75 -59 -76c-19 -1 -35 6 -50 16c-17 11 -35 25 -52 36c-32 21 -67 28 -105 24c-11 -1 -12 -1 -13 11c-2 22 6 40 21 56c42 46 83 91 122 140c3 4 7 9 10 13c1 1 3 2 2 4
|
||||
s-2 1 -4 1c-18 -1 -37 -2 -56 -2c-10 0 -18 2 -27 6c-15 7 -20 17 -14 32c5 12 11 23 19 34c2 3 4 4 7 2c17 -9 36 -11 55 -13c40 -3 80 0 119 8c5 1 6 -1 6 -5c2 -18 -4 -33 -16 -46c-42 -48 -82 -98 -127 -143c-14 -14 -29 -28 -41 -46z" />
|
||||
<glyph glyph-name="uniF1EC" unicode=""
|
||||
d="M333 376c20 0 36 -16 36 -36v-296c0 -20 -16 -36 -36 -36h-297c-20 0 -36 16 -36 36v296c0 20 16 36 36 36h297zM289 155h-165l135 39c9 3 16 7 21 13s9 15 9 23c0 10 -5 19 -13 26s-18 11 -29 11h-155c-5 -1 -9 -6 -9 -11v-27h164l-135 -38c-8 -3 -16 -7 -21 -14
|
||||
s-8 -14 -8 -22c0 -10 4 -20 12 -27s18 -11 29 -11h154c5 1 10 6 11 11v27z" />
|
||||
<glyph glyph-name="uniF1ED" unicode="" horiz-adv-x="394"
|
||||
d="M175 214c12 12 32 12 44 0s12 -32 0 -44s-32 -12 -44 0s-12 32 0 44zM333 43v0v-1h-1v0l-8 -8v0c-4 -4 -11 -3 -15 0v0l-1 1v0v0l-7 7v0v0l-7 7v0v0v0v0c-4 4 -5 11 -1 16v1h1v1v0l7 7h1c64 65 64 171 -1 236v0v0l-8 7v0v0l-1 1v1c-4 4 -3 11 0 15v0l1 1v0v0l14 14v0v0v0
|
||||
v0c4 4 11 5 16 1v0l9 -9v0v0c82 -82 83 -216 1 -298zM274 102v0h-1v0v0l-8 -9v1c-4 -4 -11 -3 -15 0v-1l-2 1v0v0l-14 15v0c-4 4 -5 11 -1 16v0l1 1v0v0l8 8v0c32 32 31 84 -1 116v0v0l-8 8v0v0l-1 1v0c-4 4 -3 11 0 15v0l1 1v0v0l14 14l1 1v0v0v0c4 4 11 4 16 0v0l9 -8v0
|
||||
v-1c49 -49 50 -129 1 -179zM219 214c12 -12 12 -32 0 -44s-32 -12 -44 0s-12 32 0 44s32 12 44 0zM162 125c4 -4 3 -11 0 -15v0l-1 -1v0v0l-14 -14v0v-1v0v0c-4 -4 -12 -4 -17 0v0l-8 8v0l-1 1c-49 49 -49 129 0 179v0v0v0v0l7 8v0v0l1 1v-1c4 4 12 3 16 0v1l15 -16v0
|
||||
c4 -4 5 -11 1 -16v0l-9 -9v0c-32 -32 -31 -84 1 -116v0v0l8 -8v0v0l1 -1v0zM102 65c4 -4 3 -11 0 -15v0l-1 -1v0v0l-14 -14v0v0v0v0c-4 -4 -11 -5 -16 -1v0l-9 9v0v0c-82 82 -83 216 -1 298v0l9 9v0c4 4 11 3 15 0v0l8 -8v0v0l8 -7v0c4 -4 4 -11 0 -16v-1h-1v-1v0l-7 -7v0
|
||||
c-64 -65 -65 -171 0 -236h1v0l7 -7v0v0l1 -1v-1z" />
|
||||
<glyph glyph-name="uniF1EE" unicode="" horiz-adv-x="426"
|
||||
d="M426 244c1 -4 0 -8 -3 -11l-113 -90l50 -142c1 -4 0 -8 -3 -11c-2 -2 -4 -3 -6 -3s-4 1 -6 2l-132 79l-132 -79c-4 -2 -9 -2 -12 1s-4 7 -3 11l50 142l-112 90c-3 3 -4 7 -3 11s5 7 9 7h144l50 139c1 4 5 7 9 7s9 -3 10 -7l49 -139h144c4 0 9 -3 10 -7z" />
|
||||
<glyph glyph-name="uniF1EF" unicode="" horiz-adv-x="308"
|
||||
d="M308 335v0v-286v0c0 -6 -5 -11 -11 -11v0h-285v0h-1c-6 0 -11 5 -11 11v0v0v284v2c0 6 5 11 11 11h1h284v0h1c6 0 11 -5 11 -11z" />
|
||||
<glyph glyph-name="uniF1F0" unicode="" horiz-adv-x="312"
|
||||
d="M297 193c8 0 15 -7 15 -15s-7 -15 -15 -15h-1v0h-281v0c-8 0 -15 7 -15 15s7 15 15 15v0h281v0h1zM263 144c4 0 8 -4 8 -8v-1v-3c0 -48 -34 -90 -112 -90c-53 0 -91 19 -117 46c-2 1 -4 3 -4 6c0 2 1 4 2 5v0l18 27v0v0v0c1 2 4 4 7 4s5 -2 6 -4c20 -21 50 -39 90 -39
|
||||
c42 0 58 20 58 40c0 2 -1 5 -1 7v0v2c0 4 4 8 8 8l1 -1v1h36zM63 214v0c-8 11 -12 25 -12 43c0 48 41 85 105 85c45 0 82 -15 108 -41v0c2 -1 3 -4 3 -6s-1 -4 -2 -5v0l-17 -25h-1v-1v0c-1 -2 -3 -3 -6 -3c-2 0 -4 1 -5 2v0c-23 23 -55 34 -85 34s-48 -15 -48 -36
|
||||
c0 -28 45 -33 88 -46v0c1 0 2 -1 2 -2s-1 -3 -2 -3h-121c-3 0 -6 2 -7 4z" />
|
||||
<glyph glyph-name="uniF1F1" unicode="" horiz-adv-x="315"
|
||||
d="M313 54c1 -1 2 -3 2 -4v-11c0 -1 -1 -3 -2 -4s-2 -1 -3 -1h-67v0h-1c-1 0 -2 1 -2 2v13v0c0 1 1 2 2 3l1 1v0c37 26 41 30 41 36c0 4 -5 6 -10 6c-8 0 -15 -3 -21 -8v0v0v0v0c-2 -1 -5 -2 -7 0v0l-7 10v0c-1 2 -1 4 0 6v0v0l1 1v0c9 9 23 13 34 13c21 0 35 -12 35 -28
|
||||
c0 -11 -6 -21 -26 -34h27v0c1 0 2 0 3 -1zM210 277c5 -5 5 -14 0 -19v0l-67 -66l67 -67v0c5 -5 5 -14 0 -19l-17 -17c-5 -5 -14 -5 -19 0v0v0l-67 66l-67 -66v0c-5 -5 -14 -5 -19 0l-17 17c-5 5 -5 14 0 19v0v0l67 67l-67 66v1c-5 5 -5 13 0 18l17 18c5 5 14 5 19 0l67 -67
|
||||
l67 67v0c5 5 14 5 19 0z" />
|
||||
<glyph glyph-name="uniF1F2" unicode="" horiz-adv-x="315"
|
||||
d="M313 287c1 -1 2 -2 2 -3v-12c0 -1 -1 -2 -2 -3s-2 -2 -3 -2h-67v0h-1c-1 0 -2 1 -2 2v14v0c0 1 1 2 2 3h1v0c37 26 41 30 41 36c0 4 -5 6 -10 6c-8 0 -15 -3 -21 -8v0v0v0v0c-2 -1 -5 -2 -7 0v0l-7 11v0c-1 2 -1 3 0 5v0v1h1v1c9 9 23 12 34 12c21 0 35 -12 35 -28
|
||||
c0 -11 -6 -20 -26 -33h27v0c1 0 2 -1 3 -2zM210 277c5 -5 5 -14 0 -19v0l-67 -67l67 -67v0c5 -5 5 -14 0 -19l-17 -17c-5 -5 -14 -5 -19 0v0v0l-67 67l-67 -67v0c-5 -5 -14 -5 -19 0l-17 17c-5 5 -5 14 0 19v0v0l67 67l-67 67v0c-5 5 -5 14 0 19l17 17c5 5 14 5 19 0l67 -67
|
||||
l67 67v0c5 5 14 5 19 0z" />
|
||||
<glyph glyph-name="uniF1F3" unicode="" horiz-adv-x="436"
|
||||
d="M436 335v-1v0v-286v0c0 -8 -8 -14 -16 -14h-404c-8 0 -16 6 -16 14v0v286v0v1c0 9 7 15 16 15h404c9 0 16 -6 16 -15zM385 84v0v216h-334v-216h334zM410 179c7 0 13 5 13 12s-6 13 -13 13s-12 -6 -12 -13s5 -12 12 -12z" />
|
||||
<glyph glyph-name="uniF1F4" unicode="" horiz-adv-x="317"
|
||||
d="M301 410c9 0 16 -7 16 -16v-404c0 -9 -7 -16 -16 -16v0v0h-286v0c-8 0 -15 8 -15 16v404c0 8 7 16 15 16v0h286v0v0zM158 -13c7 0 13 6 13 13s-6 12 -13 12s-13 -5 -13 -12s6 -13 13 -13zM266 25v0v334h-215v-334h215z" />
|
||||
<glyph glyph-name="uniF1F5" unicode="" horiz-adv-x="486"
|
||||
d="M473 205c7 0 13 -6 13 -13s-6 -13 -13 -13h-38c-6 -96 -83 -173 -179 -179v-38c0 -7 -6 -13 -13 -13s-13 6 -13 13v38c-96 6 -172 83 -178 179h-39c-7 0 -13 6 -13 13s6 13 13 13h39c6 96 82 173 178 179v38c0 7 6 13 13 13s13 -6 13 -13v-38c96 -6 173 -83 179 -179h38z
|
||||
M230 51v26c-54 6 -96 48 -102 102h-25c6 -68 59 -122 127 -128zM230 307v0v26c-68 -6 -121 -60 -127 -128h25c6 54 48 96 102 102zM256 333v-26c54 -6 96 -48 102 -102h26c-6 68 -60 122 -128 128zM256 51c68 6 122 60 128 128h-26c-6 -54 -48 -96 -102 -102v-26z" />
|
||||
<glyph glyph-name="uniF1F6" unicode="" horiz-adv-x="384"
|
||||
d="M192 307c64 0 115 -51 115 -115s-51 -115 -115 -115s-116 51 -116 115s52 115 116 115zM192 127c36 0 64 29 64 65s-28 65 -64 65s-65 -29 -65 -65s29 -65 65 -65zM192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM192 51
|
||||
c78 0 141 63 141 141s-63 141 -141 141s-141 -63 -141 -141s63 -141 141 -141zM153 192c0 26 13 39 39 39s39 -13 39 -39s-13 -39 -39 -39s-39 13 -39 39z" />
|
||||
<glyph glyph-name="uniF1F7" unicode="" horiz-adv-x="333"
|
||||
d="M12 256c-3 3 -6 6 -6 11v56c0 5 3 12 6 16c0 0 1 3 3 5c11 11 54 45 153 45c117 0 154 -48 156 -50c3 -4 6 -11 6 -16v-56c0 -4 -1 -7 -4 -9c-4 -4 -9 -5 -14 -3l-65 20c-7 2 -12 9 -12 16v21c0 1 0 3 -1 4c0 0 -15 13 -66 13s-66 -13 -66 -13c0 -1 -1 -3 -1 -4v-24
|
||||
c0 -5 -3 -10 -6 -13c-2 -2 -4 -3 -6 -4h-1l-65 -17c-4 -1 -8 0 -11 2zM35 148c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13c0 -6 -5 -11 -11 -11h-35c-6 0 -11 5 -11 11v13zM161 135c0 -6 -5 -11 -11 -11h-35c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13z
|
||||
M230 135c0 -6 -5 -11 -11 -11h-35c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13zM299 135c0 -6 -5 -11 -11 -11h-35c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13zM11 189c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13
|
||||
c0 -6 -5 -11 -11 -11h-35zM115 189h-35c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13c0 -6 -5 -11 -11 -11zM184 189h-35c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13c0 -6 -5 -11 -11 -11zM253 224c6 0 11 -5 11 -11v-13
|
||||
c0 -6 -5 -11 -11 -11h-35c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35zM322 224c6 0 11 -5 11 -11v-13c0 -6 -5 -11 -11 -11h-35c-6 0 -11 5 -11 11v13c0 6 5 11 11 11h35zM57 70c0 -6 -5 -10 -11 -10h-35c-6 0 -11 4 -11 10v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13zM80 60
|
||||
c-6 0 -11 4 -11 10v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13c0 -6 -5 -10 -11 -10h-35zM149 60c-6 0 -11 4 -11 10v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13c0 -6 -5 -10 -11 -10h-35zM218 60c-6 0 -11 4 -11 10v13c0 6 5 11 11 11h35c6 0 11 -5 11 -11v-13
|
||||
c0 -6 -5 -10 -11 -10h-35zM322 94c6 0 11 -5 11 -11v-13c0 -6 -5 -10 -11 -10h-35c-6 0 -11 4 -11 10v13c0 6 5 11 11 11h35zM281 30c6 0 11 -5 11 -11v-13c0 -6 -5 -11 -11 -11h-233c-6 0 -10 5 -10 11v13c0 6 4 11 10 11h233z" />
|
||||
<glyph glyph-name="uniF1F8" unicode="" horiz-adv-x="358"
|
||||
d="M358 88c1 -5 -1 -11 -5 -15l-50 -50c-5 -5 -13 -9 -20 -10h-7v0c-19 0 -89 7 -179 97c-106 106 -97 183 -97 186c1 7 5 15 10 20l51 50c3 3 7 5 12 5c6 0 12 -4 15 -10l41 -76c4 -8 2 -19 -5 -26l-18 -19c-1 -1 -2 -4 -2 -5c0 0 1 -25 47 -71s72 -48 72 -48c1 0 3 2 4 3
|
||||
l22 21c4 4 10 7 16 7c4 0 7 -1 10 -3h1l73 -43c5 -3 8 -8 9 -13z" />
|
||||
<glyph glyph-name="uniF1F9" unicode="" horiz-adv-x="379"
|
||||
d="M364 45c8 0 15 -8 15 -16s-7 -15 -15 -15h-1v0h-348v0c-8 0 -15 7 -15 15s7 16 15 16v0h348v0h1zM63 80c-7 0 -13 6 -13 13c0 2 1 4 1 5v0l103 263v0c2 5 7 9 13 9h45c6 0 11 -4 13 -9v0l103 -262v-1v-1v0c0 -1 1 -3 1 -4c0 -7 -7 -13 -14 -13h-1v0h-29v0
|
||||
c-5 0 -9 4 -11 8v0l-18 48h-133l-18 -47c-2 -5 -7 -9 -13 -9h-29zM189 319l-52 -138h104z" />
|
||||
<glyph glyph-name="uniF1FA" unicode="" horiz-adv-x="332"
|
||||
d="M65 358c6 0 12 -5 12 -11v0v0v-54v0c0 -6 -6 -12 -12 -12v0h-53v0v0c-6 0 -12 6 -12 12v0v54v0v0c0 6 6 11 12 11v0v0h53v0zM193 358c6 0 12 -5 12 -11v0v0v-54v0c0 -6 -6 -12 -12 -12v0h-54v0v0c-6 0 -11 6 -11 12v0v54v0v0c0 6 5 11 11 11v0v0h54v0zM332 347v0v0v-54v0
|
||||
c0 -6 -5 -12 -11 -12v0h-54v0v0c-6 0 -12 6 -12 12v0v54v0c0 6 6 11 12 11v0v0h54v0c6 0 11 -5 11 -11zM65 230c6 0 12 -5 12 -11v0v0v-54v0c0 -6 -6 -11 -12 -11v0h-53v0v0c-6 0 -12 5 -12 11v0v54v0v0c0 6 6 11 12 11v0v0h53v0zM193 230c6 0 12 -5 12 -11v0v0v-54v0
|
||||
c0 -6 -6 -11 -12 -11v0h-54v0v0c-6 0 -11 5 -11 11v0v54v0v0c0 6 5 11 11 11v0v0h54v0zM321 230c6 0 11 -5 11 -11v0v0v-54v0c0 -6 -5 -11 -11 -11v0h-54v0v0c-6 0 -12 5 -12 11v0v54v0c0 6 6 11 12 11v0v0h54v0zM65 103c6 0 12 -6 12 -12v0v0v-54v0c0 -6 -6 -11 -12 -11v0
|
||||
h-53v0v0c-6 0 -12 5 -12 11v0v54v0v0c0 6 6 12 12 12v0v0h53v0zM193 103c6 0 12 -6 12 -12v0v0v-54v0c0 -6 -6 -11 -12 -11v0h-54v0v0c-6 0 -11 5 -11 11v0v54v0v0c0 6 5 12 11 12v0v0h54v0zM321 103c6 0 11 -6 11 -12v0v0v-54v0c0 -6 -5 -11 -11 -11v0h-54v0v0
|
||||
c-6 0 -12 5 -12 11v0v54v0c0 6 6 12 12 12v0v0h54v0z" />
|
||||
<glyph glyph-name="uniF1FB" unicode="" horiz-adv-x="512"
|
||||
d="M509 276c4 -4 4 -9 0 -13v0l-324 -324v0c-4 -4 -9 -4 -13 0v1l-63 62c8 12 7 28 -4 39s-27 12 -39 4l-63 63v0v0c-4 4 -4 9 0 13v0l324 324v0c4 4 9 4 13 0l63 -63c-8 -12 -7 -28 4 -39s27 -12 39 -4l63 -63v0zM438 263c4 4 4 9 0 13l-98 98v0c-4 4 -9 4 -13 0v0
|
||||
l-253 -253v0c-4 -4 -4 -9 0 -13v0l98 -98v0v0c4 -4 9 -4 13 0v0v0zM317 184c1 0 2 -1 2 -2s-1 -2 -1 -2l-1 -1l-39 -9l-9 -39c0 -1 -2 -2 -3 -2s-2 1 -2 2l-17 35l-36 -4c-1 0 -3 0 -3 1s-1 2 0 3l27 26l-17 34c0 1 0 2 1 3s2 1 3 1l34 -17l26 27c1 1 2 0 3 0s1 -2 1 -3
|
||||
l-4 -36z" />
|
||||
<glyph glyph-name="uniF1FC" unicode="" horiz-adv-x="332"
|
||||
d="M321 139c7 -3 11 -12 11 -21v-77v-14c0 -12 -9 -22 -19 -22h-123v70v2v0l-20 34v0v1v0c-1 1 -2 2 -4 2s-2 -1 -3 -2v0l-21 -35h1c0 -1 -1 -1 -1 -2v-70h-123c-10 0 -19 10 -19 22v14v77c0 9 5 18 12 21l83 38l32 15c-15 9 -27 22 -35 39c-7 14 -11 31 -11 49
|
||||
c0 10 2 20 4 29c11 40 43 70 81 70c39 0 72 -30 82 -72c2 -9 3 -18 3 -27c0 -17 -4 -32 -10 -46c-8 -17 -19 -32 -34 -41l33 -16zM190 162v2c0 2 -2 4 -4 4h-40c-2 0 -4 -2 -4 -4c0 -1 1 -1 1 -2v0l19 -35v0h1v0c1 -1 1 -2 3 -2s3 1 4 2v0l20 35v0z" />
|
||||
<glyph glyph-name="uniF1FD" unicode="" horiz-adv-x="332"
|
||||
d="M321 139c7 -3 11 -12 11 -21v-77v-14c0 -12 -9 -22 -19 -22h-294c-10 0 -19 10 -19 22v14v77c0 9 5 18 12 21l83 38l32 15c-1 1 -3 2 -4 3h-44c-7 0 -13 5 -13 12v33v0c1 46 18 87 42 112c13 14 29 23 47 26h2s2 1 3 1h6v0v0v0h6c1 0 2 -1 3 -1h3c18 -3 34 -13 47 -27
|
||||
c24 -25 40 -65 41 -111v0v-33c0 -7 -5 -12 -12 -12h-45l-2 -2l33 -16z" />
|
||||
<glyph glyph-name="uniF1FE" unicode="" horiz-adv-x="332"
|
||||
d="M321 139c7 -3 11 -12 11 -21v-77v-14c0 -12 -9 -22 -19 -22h-294c-10 0 -19 10 -19 22v14v77c0 9 5 18 12 21l83 38l32 15c-15 9 -27 22 -35 39c-7 14 -11 31 -11 49c0 10 2 20 4 29c11 40 43 70 81 70c39 0 72 -30 82 -72c2 -9 3 -18 3 -27c0 -17 -4 -32 -10 -46
|
||||
c-8 -17 -19 -32 -34 -41l33 -16z" />
|
||||
<glyph glyph-name="uniF1FF" unicode="" horiz-adv-x="488"
|
||||
d="M382 145c4 -2 7 -5 9 -9c2 -3 3 -7 3 -11v-69v-13c0 -11 -8 -20 -17 -20h-266c-9 0 -17 9 -17 20v13v69v3c1 7 5 14 11 17l74 34l29 14c-1 1 -2 0 -3 1h-40c-6 0 -11 5 -11 11v30v0c1 37 12 69 30 92c14 21 36 34 60 34c22 0 42 -11 56 -29c20 -23 33 -58 34 -97v0v-30
|
||||
c0 -6 -5 -11 -11 -11h-40c-1 0 -1 -1 -2 -1l29 -14zM119 179l-25 -11c-15 -7 -25 -24 -25 -42v-69h-55c-8 0 -14 7 -14 16v66c0 7 4 13 9 15l83 39c-20 12 -33 36 -33 64c0 40 28 71 62 71c10 0 20 -3 28 -8c-17 -30 -29 -68 -30 -110v0v-31zM479 153c5 -2 9 -8 9 -15v-66
|
||||
c0 -9 -6 -16 -14 -16h-55v69c0 18 -11 35 -26 42l-24 11v31h-1c-1 42 -12 80 -29 110c8 5 18 9 28 9c34 0 62 -32 62 -72c0 -28 -14 -52 -34 -64z" />
|
||||
<glyph glyph-name="uniF200" unicode="" horiz-adv-x="489"
|
||||
d="M481 153c5 -3 8 -8 8 -15v-66c0 -9 -5 -16 -13 -16h-58v70c0 18 -9 34 -24 41l-54 25v0c-5 3 -10 7 -14 12c12 19 19 43 19 67c0 16 -2 32 -8 46c9 7 20 11 32 11c34 0 61 -32 61 -72c0 -27 -13 -51 -32 -63zM150 193l-56 -26c-15 -7 -25 -23 -25 -41v-70h-55
|
||||
c-8 0 -14 7 -14 16v66c0 7 4 13 9 15l83 39c-20 12 -33 36 -33 64c0 40 28 72 62 72c11 0 21 -4 30 -10c-6 -14 -9 -30 -9 -47c0 -25 7 -49 20 -68c-4 -4 -8 -7 -12 -10zM383 144c6 -3 10 -10 10 -18v-70v-12c0 -11 -8 -20 -17 -20h-265c-9 0 -17 9 -17 20v12v70
|
||||
c0 8 5 15 11 18l75 35l29 13c-13 8 -25 20 -32 35c-6 13 -10 28 -10 44c0 9 2 18 4 26c10 36 39 63 73 63c35 0 64 -28 73 -65c2 -8 3 -16 3 -24c0 -15 -3 -29 -9 -41c-7 -16 -18 -29 -31 -37l31 -15z" />
|
||||
<glyph glyph-name="uniF201" unicode="" horiz-adv-x="442"
|
||||
d="M140 207v0v-24l-39 -18c-16 -8 -27 -25 -27 -44v-74h-59c-8 0 -15 8 -15 17v71c0 7 3 13 9 16l90 41c-21 13 -36 38 -36 68c0 42 30 77 66 77c12 0 22 -4 32 -10c-1 -4 -3 -7 -4 -11c-10 -22 -16 -48 -17 -76v0v-28v-5zM430 139c7 -3 12 -12 12 -21v-77v-14
|
||||
c0 -12 -9 -22 -19 -22h-295c-10 0 -19 10 -19 22v14v77c0 9 5 18 12 21l83 38l33 15c-1 1 -3 2 -4 3h-45c-7 0 -12 5 -12 12v33v0c1 46 18 87 42 112c13 14 28 23 46 26h3s2 1 3 1h6h6c1 0 2 -1 3 -1h2c18 -3 34 -13 47 -27c24 -25 40 -65 41 -111h1v-33
|
||||
c0 -7 -6 -12 -13 -12h-44c-1 -1 -2 -1 -3 -2l34 -16z" />
|
||||
<glyph glyph-name="uniF202" unicode="" horiz-adv-x="420"
|
||||
d="M419 124c0 -1 1 -2 1 -3v-74v-13c0 -12 -9 -21 -19 -21h-282c-10 0 -18 9 -18 21v13v74c0 2 0 4 1 6c2 6 5 12 10 14l80 37l31 14c-14 8 -26 22 -34 38c-6 12 -9 25 -10 40v6v8c1 7 2 13 4 20c1 4 2 8 3 11c13 33 41 56 74 56v0v0c37 0 68 -29 78 -69c2 -8 4 -17 4 -26
|
||||
c0 -16 -4 -31 -10 -44c-8 -17 -19 -30 -33 -39l32 -16l77 -36c6 -3 10 -10 11 -17zM155 190l-54 -25c-16 -8 -27 -25 -27 -44v-74h-59c-8 0 -15 8 -15 17v71c0 7 3 13 9 16l90 41l-2 1h-36c-5 0 -10 5 -10 10v26v0c2 62 37 111 80 111c9 0 17 -2 25 -6
|
||||
c-8 -17 -13 -37 -13 -58c0 -30 10 -56 26 -78z" />
|
||||
<glyph glyph-name="uniF203" unicode="" horiz-adv-x="420"
|
||||
d="M160 193l-59 -28c-16 -8 -27 -25 -27 -44v-74h-59c-8 0 -15 8 -15 17v71c0 7 3 13 9 16l90 41c-21 13 -36 38 -36 68c0 42 30 77 66 77c12 0 22 -4 32 -10c-6 -15 -9 -33 -9 -51c0 -27 7 -51 21 -72c-4 -4 -8 -8 -13 -11zM408 141c7 -3 12 -11 12 -20v-74v-13
|
||||
c0 -12 -9 -21 -19 -21h-282c-10 0 -18 9 -18 21v13v74c0 9 4 17 11 20l80 37l31 14c-14 8 -26 22 -34 38c-7 14 -10 29 -10 46c0 10 2 19 4 28c10 39 40 67 77 67s68 -29 78 -69c2 -8 4 -17 4 -26c0 -16 -4 -31 -10 -44c-8 -17 -19 -30 -33 -39l32 -16z" />
|
||||
<glyph glyph-name="uniF204" unicode="" horiz-adv-x="321"
|
||||
d="M293 277c7 0 12 -5 12 -12v-256c0 -7 -5 -12 -12 -12h-265c-7 0 -12 5 -12 12v256c0 7 5 12 12 12h265zM309 359c7 0 12 -5 12 -12v-35c0 -7 -5 -13 -12 -13h-297c-7 0 -12 6 -12 13v35c0 7 5 12 12 12h101v16c0 7 5 12 12 12h72c7 0 12 -5 12 -12v-16h100z" />
|
||||
<glyph glyph-name="uniF205" unicode="" horiz-adv-x="431"
|
||||
d="M431 33c1 -1 0 -2 0 -3c0 -4 -2 -7 -6 -7v0h-72v-20c0 -3 -3 -5 -6 -5h-26c-3 0 -6 2 -6 5v20h-72v0c-4 0 -6 3 -6 7c0 1 0 2 1 3v0l8 16h-82v-44c0 -4 -4 -7 -8 -7h-38c-4 0 -7 3 -7 7v44h-101v0c-5 0 -10 4 -10 9c0 2 1 4 2 6l52 90h-21v0c-4 0 -8 4 -8 8c0 2 1 4 2 5
|
||||
l48 83h-12v0c-3 0 -6 2 -6 5c0 1 1 2 1 3v0l71 123c2 3 4 5 8 5s7 -2 9 -5v0l71 -123v0c0 -1 1 -2 1 -3c0 -3 -3 -5 -6 -5v0h-12l49 -84v-1c1 -1 1 -2 1 -3c0 -4 -4 -8 -8 -8v0h-21l43 -75l11 19h-15v0c-3 0 -5 3 -5 6v2v0l35 60h-9v0c-2 0 -4 2 -4 4c0 1 1 1 1 2v0l50 87v0
|
||||
c1 2 3 4 6 4s5 -2 6 -4v0l50 -87v0c0 -1 1 -1 1 -2c0 -2 -2 -4 -4 -4v0h-8l34 -60v0c0 -1 1 -1 1 -2c0 -3 -3 -6 -6 -6v0h-15l38 -65v0z" />
|
||||
<glyph glyph-name="uniF206" unicode="" horiz-adv-x="331"
|
||||
d="M331 325v0v-133v0c0 -5 -5 -10 -10 -10v0h-43c-7 -47 -42 -84 -87 -94v-65h51v0c7 0 13 -6 13 -13s-6 -13 -13 -13v0v0h-153v0c-7 0 -13 6 -13 13s6 13 13 13h51v65c-45 10 -81 47 -88 94h-42v0c-5 0 -10 5 -10 10v0v0v0v0v133v0v1v0v0c0 5 5 9 10 9h41v36v1
|
||||
c0 8 6 14 14 15v0h199v0h1c8 0 15 -7 15 -15v-1v-36h41v0c5 0 10 -5 10 -10zM51 208v102h-25v-102h25zM305 208v102h-25v-102h25z" />
|
||||
<glyph glyph-name="uniF207" unicode="" horiz-adv-x="312"
|
||||
d="M297 61c8 0 15 -7 15 -15s-7 -16 -15 -16l-1 1v-1h-281v1c-8 0 -15 7 -15 15s7 15 15 15v0h281v0h1zM156 79c-81 0 -121 46 -121 115v145c0 8 7 15 15 15h20c8 0 15 -7 15 -15v-1v-143c0 -44 25 -73 71 -73s71 29 71 73v143v1c0 8 7 15 15 15h20c8 0 15 -7 15 -15v-145
|
||||
c0 -69 -40 -115 -121 -115z" />
|
||||
<glyph glyph-name="uniF208" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM192 51c78 0 141 63 141 141s-63 141 -141 141s-141 -63 -141 -141s63 -141 141 -141zM166 281c0 16 8 25 24 25s24 -9 24 -25s-8 -24 -24 -24s-24 8 -24 24zM273 201v0
|
||||
c2 -2 3 -4 3 -7c0 -6 -4 -10 -10 -10c-3 0 -5 1 -7 3v0l-34 34v0h-1v0c-1 0 -1 1 -2 1c-2 0 -3 -1 -3 -3v-1v0v-30v-32v-63c0 -7 -5 -12 -12 -12s-12 5 -12 12v63v0c0 2 -1 4 -3 4s-3 -2 -3 -4v0v-63c0 -7 -5 -12 -12 -12s-12 5 -12 12v63v32v31v0c0 2 -1 3 -3 3
|
||||
c-1 0 -2 0 -3 -1v0l-34 -34v0c-2 -2 -4 -3 -7 -3c-6 0 -10 4 -10 10c0 3 1 5 3 7v0l46 46v1v0v0c2 2 5 3 8 3v0h53h1c3 0 6 -1 8 -3v0v0v-1z" />
|
||||
<glyph glyph-name="uniF209" unicode="" horiz-adv-x="463"
|
||||
d="M59 344v0c-6 6 -6 14 0 20s14 6 20 0v0v0v0h1l49 -49c6 -6 6 -15 0 -21s-15 -6 -21 0zM144 363v0c-2 8 2 16 10 18s16 -2 18 -10v0v0v0v0l19 -68c2 -8 -3 -16 -11 -18s-16 2 -18 10zM58 246c-8 2 -12 10 -10 18s9 12 17 10v0h1v0v0l67 -18c8 -2 13 -10 11 -18
|
||||
s-10 -12 -18 -10l-68 18v0v0v0v0zM405 40v0c6 -6 5 -14 -1 -20s-14 -6 -20 0v0v0v0v0l-50 49c-6 6 -6 15 0 21s15 6 21 0zM319 21v0c2 -8 -2 -16 -10 -18s-16 2 -18 10v0v0v0v0l-18 68c-2 8 2 16 10 18s16 -2 18 -10zM405 138c8 -2 13 -10 11 -18s-10 -12 -18 -10v0v0v0v0
|
||||
l-68 18c-8 2 -13 10 -11 18s10 12 18 10l68 -18v0v0v0v0zM222 183c39 -39 40 -101 4 -141v0l-51 -52c-40 -40 -105 -40 -145 0s-40 105 0 145l52 52v0c40 36 101 35 140 -4zM189 77v0c17 20 16 50 -3 69s-50 20 -70 3v0l-3 -3v0v0l-46 -46v0c-20 -20 -20 -53 0 -73
|
||||
s53 -20 73 0v0l46 46v0v0zM433 394c39 -39 40 -101 4 -141v0l-52 -52c-40 -40 -104 -40 -144 0s-40 105 0 145l51 51h1c40 36 101 36 140 -3zM400 287v0c17 20 16 51 -3 70s-50 20 -70 3v0l-3 -3v0v0l-46 -46h-1c-20 -20 -20 -53 0 -73s53 -20 73 0v0l47 46v0v0z" />
|
||||
<glyph glyph-name="uniF20A" unicode="" horiz-adv-x="356"
|
||||
d="M342 215c7 0 14 -6 14 -13v-192c0 -7 -7 -13 -14 -13h-329c-7 0 -13 6 -13 13v192c0 7 6 13 13 13h19h29v54c0 65 55 118 122 118c66 0 120 -52 121 -116v-2c0 -5 -4 -10 -10 -10v0h-51c-6 0 -10 5 -10 10c0 27 -22 48 -50 48s-50 -21 -50 -48v-54h123h67h19z" />
|
||||
<glyph glyph-name="uniF20B" unicode="" horiz-adv-x="473"
|
||||
d="M387 259c46 6 86 -34 86 -83c0 -18 -6 -35 -16 -50c-3 -4 -7 -7 -12 -7h-138c-1 47 -39 86 -87 86s-86 -39 -87 -86h-102c-5 0 -10 3 -13 8c-12 20 -18 42 -18 66c0 66 49 121 110 121c8 0 15 -1 23 -3c27 39 70 61 115 61c65 0 121 -47 139 -113zM274 88c1 -2 1 -3 0 -5
|
||||
s-2 -3 -4 -3h-24v-64c0 -3 -2 -4 -5 -4h-44c-3 0 -4 1 -4 4v64h-24c-2 0 -4 1 -5 3s0 4 1 5l50 71c1 1 2 2 4 2v0c2 0 3 -1 4 -2z" />
|
||||
<glyph glyph-name="uniF20C" unicode="" horiz-adv-x="420"
|
||||
d="M394 194c14 0 26 -11 26 -25v-144c0 -14 -12 -25 -26 -25h-368c-14 0 -26 11 -26 25v144c0 14 12 25 26 25h93c11 0 22 -7 25 -17c9 -28 36 -46 66 -46s57 18 66 46c3 10 13 17 24 17h94zM137 273c-3 0 -5 1 -6 3s-1 5 1 7l72 98c1 2 4 3 6 3v0c2 0 4 -1 5 -3l73 -98
|
||||
c2 -2 2 -5 1 -7s-3 -3 -6 -3h-35v-88c0 -4 -3 -7 -7 -7h-63c-4 0 -7 3 -7 7v88h-34z" />
|
||||
<glyph glyph-name="uniF20D" unicode="" horiz-adv-x="150"
|
||||
d="M150 266v0v-233v0c0 -8 -7 -15 -15 -15v0h-121v0c-8 0 -14 7 -14 15v0v233v0c0 8 6 14 14 14h1h11v77v0c0 4 3 7 7 8v1h83c5 0 8 -4 8 -9v0v0v-77h11v0c8 0 15 -6 15 -14zM99 280v60h-48v-60h48zM62 294v11h26v-11h-26z" />
|
||||
<glyph glyph-name="uniF20E" unicode="" horiz-adv-x="410"
|
||||
d="M288 134v-54c0 -13 -10 -23 -23 -23h-242c-13 0 -23 10 -23 23v224c0 13 10 23 23 23h242c13 0 23 -10 23 -23v-53v0l122 57v-232z" />
|
||||
<glyph glyph-name="uniF20F" unicode="" horiz-adv-x="243"
|
||||
d="M243 328v-270h-1l1 -1c0 -6 -5 -11 -11 -11c-2 0 -4 1 -6 2l-85 49v0l-59 34h-74v0v0c-4 0 -8 4 -8 8v106c0 4 4 8 8 8v0v0h74l40 23v0l105 60v0c2 1 3 2 5 2c5 0 9 -5 10 -10h1z" />
|
||||
<glyph glyph-name="uniF210" unicode="" horiz-adv-x="436"
|
||||
d="M271 268c-4 5 -4 12 0 17v0l1 1v0v0l16 16v0v0v0v0c5 5 14 5 19 1v0l10 -10v0v0c43 -43 53 -106 31 -159l-41 41c5 29 -4 60 -26 82v1v0l-10 10v0zM366 361c77 -77 90 -194 40 -285l-37 37c31 70 18 155 -39 212v0h-1l-8 9v0v0l-1 1v0c-4 5 -4 12 0 17v0l17 18v0
|
||||
c5 5 13 5 18 1v0l10 -10v0h1zM243 316v0v-77l-60 60l44 25v0c2 1 3 2 5 2c5 0 9 -5 10 -10h1zM82 241l9 5l152 -151v-49h-1l1 -1c0 -6 -5 -10 -11 -10c-2 0 -4 0 -6 1l-85 49v0l-59 34h-74v0v0c-4 0 -8 4 -8 8v106c0 4 4 8 8 8v0v0h74zM401 45v0c5 -5 5 -13 0 -18l-1 -1
|
||||
l-17 -18v0c-5 -5 -13 -5 -18 0v0l-331 331c-5 5 -5 13 0 18v0l18 18v1c5 5 13 5 18 0v0v0z" />
|
||||
<glyph glyph-name="uniF211" unicode="" horiz-adv-x="436"
|
||||
d="M366 361c93 -93 93 -245 0 -338v0l-8 -9v0v0l-1 -1h-1c-5 -4 -12 -4 -17 0v0l-17 17v0c-5 5 -5 14 -1 19v0l1 1v0v0l9 9v-1c73 74 73 193 -1 267v1l-1 -1l-8 9v0v0l-1 1v0c-4 5 -4 13 0 18v0l17 17v0c5 5 13 5 18 1v0l10 -10v0h1zM317 293c56 -56 56 -147 1 -203v0
|
||||
l-10 -10v0c-5 -4 -12 -4 -17 0v0l-18 18v0c-5 5 -5 13 -1 18v0l1 1v0v0l9 9v0c36 37 35 96 -1 132v0v0l-10 10v0c-4 5 -4 12 0 17v0l1 1v0v0l16 16v1v0v0v0c5 5 14 5 19 1v0l10 -10v0v-1zM232 326c5 0 9 -4 10 -9h1v-270h-1l1 -1c0 -6 -5 -11 -11 -11c-2 0 -4 1 -6 2l-85 49
|
||||
v0l-59 34h-74v0v0c-4 0 -8 4 -8 8v105c0 4 4 8 8 8v0v0h74l40 23v0l105 61v0c2 1 3 1 5 1z" />
|
||||
<glyph glyph-name="uniF212" unicode="" horiz-adv-x="384"
|
||||
d="M192 384c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192zM53 167c8 -44 37 -81 75 -101c-24 28 -40 63 -45 101h-30zM83 217c5 38 21 73 45 101c-38 -20 -67 -57 -75 -101h30zM167 100v67h-33c5 -25 17 -48 33 -67zM167 217v67
|
||||
c-16 -19 -28 -42 -33 -67h33zM331 217c-8 44 -37 81 -75 101c24 -28 40 -63 45 -101h30zM217 100c16 19 28 42 33 67h-33v-67zM217 217h33c-5 25 -17 48 -33 67v-67zM256 66c38 20 67 57 75 101h-30c-5 -38 -21 -73 -45 -101z" />
|
||||
<glyph glyph-name="uniF213" unicode="" horiz-adv-x="285"
|
||||
d="M152 62l29 -29c-19 -15 -44 -25 -70 -25c-61 0 -111 50 -111 111c0 26 10 51 25 70l27 -28c-8 -12 -13 -25 -13 -40c0 -39 33 -72 72 -72c15 0 29 5 41 13zM202 340c0 24 13 36 37 36s37 -12 37 -36s-13 -37 -37 -37s-37 13 -37 37zM262 215c13 0 23 -10 23 -23v-117
|
||||
c0 -13 -10 -23 -23 -23s-23 10 -23 23v94h-29c8 -15 12 -32 12 -50c0 -26 -8 -49 -23 -68l-29 29c8 12 12 26 12 41c0 39 -32 71 -71 71c-15 0 -29 -5 -41 -13l-27 27c10 8 22 15 35 19l67 66l-28 17l-33 -33h-1v-1v0c-4 -4 -10 -6 -16 -6c-13 0 -23 10 -23 23
|
||||
c0 6 3 12 7 16v0l45 45l1 1v1v-1c4 4 10 6 16 6c3 0 6 -1 9 -2v1l103 -60v0c7 -4 11 -11 11 -19c0 -5 -1 -10 -4 -14v0l-1 -1c-1 -1 -3 -2 -4 -3l-46 -46h81z" />
|
||||
<glyph glyph-name="uniF214" unicode="" horiz-adv-x="415"
|
||||
d="M405 226c6 -1 10 -5 10 -11v-46c0 -6 -4 -10 -10 -11l-53 -5c-3 -13 -8 -25 -15 -36l34 -41c4 -4 3 -11 -1 -15l-32 -32c-2 -2 -5 -3 -8 -3c-2 0 -5 1 -7 3l-41 33c-11 -7 -23 -12 -36 -15l-5 -52c-1 -6 -5 -10 -11 -10h-45c-6 0 -10 4 -11 10l-5 52c-13 3 -26 8 -37 15
|
||||
l-40 -33c-2 -2 -5 -3 -7 -3c-3 0 -6 1 -8 3l-32 32c-4 4 -5 11 -1 15l34 41c-7 11 -12 23 -15 36l-53 5c-6 1 -10 5 -10 11v46c0 6 4 10 10 11l53 5c3 13 8 25 15 36l-34 41c-4 4 -3 11 1 15l32 32c2 2 5 3 8 3c2 0 5 -1 7 -3l40 -33c11 7 24 12 37 15l5 52c1 6 5 10 11 10
|
||||
h45c6 0 10 -4 11 -10l5 -52c13 -3 25 -8 36 -15l41 33c2 2 5 3 7 3c3 0 6 -1 8 -3l32 -32c4 -4 5 -11 1 -15l-34 -41c7 -11 12 -23 15 -36zM207 131c34 0 62 27 62 61s-28 61 -62 61s-61 -27 -61 -61s27 -61 61 -61z" />
|
||||
<glyph glyph-name="uniF215" unicode="" horiz-adv-x="401"
|
||||
d="M386 328c1 1 4 1 5 0v0v0l1 -1v0c6 -14 9 -29 9 -45c0 -61 -49 -111 -110 -111c-12 0 -23 3 -34 6l-115 -116c-1 -39 -32 -70 -71 -70s-71 32 -71 71s31 70 70 71l115 116c-3 11 -5 21 -5 33c0 61 50 111 111 111c16 0 30 -4 44 -10h1v0v0l1 -1c1 -1 1 -3 0 -4l-20 -20
|
||||
l-19 -19l-24 -25c-1 -13 3 -26 13 -36s23 -14 36 -13l24 24l20 19zM100 62c0 16 -13 30 -29 30s-29 -14 -29 -30s13 -29 29 -29s29 13 29 29z" />
|
||||
<glyph glyph-name="uniF216" unicode="" horiz-adv-x="384"
|
||||
d="M284 142c2 -2 2 -5 0 -7l-35 -35c-1 -1 -2 -2 -3 -2s-3 1 -4 2l-50 50l-50 -50c-1 -1 -3 -2 -4 -2s-2 1 -3 2l-35 35c-1 1 -2 2 -2 3s1 3 2 4l50 50l-50 50c-1 1 -2 3 -2 4s1 2 2 3l35 35c2 2 5 2 7 0l50 -50l50 50c2 2 5 2 7 0l35 -35c2 -2 2 -5 0 -7l-50 -50zM192 333
|
||||
c-78 0 -141 -63 -141 -141s63 -141 141 -141s141 63 141 141s-63 141 -141 141zM192 384v0c106 0 192 -86 192 -192s-86 -192 -192 -192s-192 86 -192 192s86 192 192 192z" />
|
||||
<glyph glyph-name="uniF217" unicode="" horiz-adv-x="361"
|
||||
d="M358 96c4 -4 4 -10 0 -14l-67 -68c-2 -2 -4 -3 -7 -3s-5 1 -7 3l-96 96l-96 -96c-2 -2 -5 -3 -8 -3s-5 1 -7 3l-67 68c-2 2 -3 4 -3 7s1 5 3 7l96 96l-96 96c-2 2 -3 4 -3 7s1 5 3 7l67 68c4 4 11 4 15 0l96 -96l96 96c4 4 10 4 14 0l67 -68c4 -4 4 -10 0 -14l-96 -96z
|
||||
" />
|
||||
<glyph glyph-name="uniF218" unicode="" horiz-adv-x="270"
|
||||
d="M269 331h1l-85 -130h74v0v0c3 0 6 -2 6 -5v0v-21c0 -3 -3 -6 -6 -6v0v0h-95v-33h95v0v0c3 0 6 -3 6 -6v-20c0 -3 -3 -6 -6 -6v0v0h-95v-48v0c0 -3 -2 -6 -5 -6v0h-47v0v0c-3 0 -6 3 -6 6v1v47h-94v0v0c-3 0 -6 3 -6 6v20c0 3 3 6 6 6v0v0h94v33h-94v0v0c-3 0 -6 2 -6 5v0
|
||||
v1v0v0v20v0c0 3 3 6 6 6v0v0h72l-84 130v0v1c0 1 0 2 1 2h3h2h57c2 0 3 -2 4 -3v0l68 -110l68 110v0c1 1 2 3 4 3h57h2h3v-1c1 0 1 0 1 -1z" />
|
||||
<glyph glyph-name="uniF219" unicode="" horiz-adv-x="397"
|
||||
d="M233 395c91 0 164 -74 164 -164s-73 -164 -164 -164c-26 0 -51 6 -73 17l-82 -82v1c-8 -8 -20 -14 -33 -14c-25 0 -45 20 -45 45c0 13 6 25 14 33h-1l80 79c-15 25 -24 54 -24 85c0 90 73 164 164 164zM234 132c57 0 103 45 103 102s-46 102 -103 102s-102 -45 -102 -102
|
||||
s45 -102 102 -102zM295 258c4 0 7 -4 7 -8v-32c0 -4 -4 -8 -8 -8v0v0h-36v-36v0c0 -4 -4 -8 -8 -8v0h-32v0v0c-4 0 -8 4 -8 8v0v0v36h-35h-1c-4 0 -8 4 -8 8v32c0 4 4 8 8 8h36v35v1c0 4 4 8 8 8h1h31v0c4 0 8 -4 8 -8v-36h37v0z" />
|
||||
<glyph glyph-name="uniF21A" unicode="" horiz-adv-x="397"
|
||||
d="M233 395c91 0 164 -74 164 -164s-73 -164 -164 -164c-26 0 -51 6 -73 17l-82 -82v1c-8 -8 -20 -14 -33 -14c-25 0 -45 20 -45 45c0 13 6 25 14 33h-1l80 79c-15 25 -24 54 -24 85c0 90 73 164 164 164zM234 132c57 0 103 45 103 102s-46 102 -103 102s-102 -45 -102 -102
|
||||
s45 -102 102 -102zM294 258c4 0 9 -4 9 -8v0v-32c0 -4 -5 -8 -9 -8h-120c-4 0 -8 4 -8 8v32v0c0 4 4 8 8 8h120z" />
|
||||
</font>
|
||||
</defs></svg>
|
Before Width: | Height: | Size: 147 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
font/ubuntu.woff
BIN
font/ubuntu.woff
Binary file not shown.
18
img/logo.svg
18
img/logo.svg
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 11 KiB |
157
index.html
157
index.html
|
@ -1,157 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en" ng-app="copayApp" ng-csp>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<title>Copay - Multisignature Wallet</title>
|
||||
<style type="text/css">
|
||||
.loadingpage {
|
||||
height:100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: none;
|
||||
background: #2C3E50;
|
||||
padding-top: 20%;
|
||||
text-align: center;
|
||||
}
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak,
|
||||
.ng-hide:not(.ng-hide-animate) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link rel="shortcut icon" href="img/favicon.ico">
|
||||
</head>
|
||||
<body
|
||||
ng-controller="IndexController"
|
||||
ng-swipe-disable-mouse
|
||||
ng-swipe-left="closeMenu()"
|
||||
ng-swipe-right="openMenu()">
|
||||
|
||||
<div id="loading" class="loadingpage">
|
||||
<img src="img/ajax-loader.gif" alt="Loading...">
|
||||
</div>
|
||||
<div ng-cloak class="page ng-cloak">
|
||||
|
||||
<div ng-show="signingOut">
|
||||
<div ng-include="'views/includes/loading.html'" ng-init="title = 'Signing out'"></div>
|
||||
</div>
|
||||
|
||||
<div ng-show="sessionExpired" class="session-expired">
|
||||
<i class="fi-clock size-72 text-gray"></i>
|
||||
<p class="text-gray size-18" translate>Your session is about to expire due to inactivity in {{countdown}} seconds</p>
|
||||
</div>
|
||||
|
||||
<div class="off-canvas-wrap" id="off-canvas-wrap" ng-show="!signingOut">
|
||||
<div class="inner-wrap">
|
||||
|
||||
<div class="status" ng-if="$root.needsEmailConfirmation && !$root.pleaseConfirmEmailAck">
|
||||
<a class="close-notification text-white" ng-click="$root.pleaseConfirmEmailAck=true">×</a>
|
||||
<div class="row">
|
||||
<div class="large-10 medium-9 small-12 columns m5b">
|
||||
<i class="fi-alert size-36 left m20r"></i>
|
||||
<strong class="size-16" translate>Email not confirmed</strong>.<br>
|
||||
<span translate>
|
||||
Please confirm your email address using the confirmation link at the message we sent you
|
||||
</span>
|
||||
</div>
|
||||
<div class="large-2 medium-3 small-12 columns text-center">
|
||||
<button class="text-white warning tiny m0"
|
||||
ng-click="resendVerificationEmail()"
|
||||
ng-disabled="loading"
|
||||
ng-show="!hideReSendButton">
|
||||
{{ loading ? 'Resending...' : 'Resend' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status" ng-if="$root.pleaseConfirmEmail">
|
||||
<a class="close-notification text-white" ng-click="$root.pleaseConfirmEmail=null">×</a>
|
||||
<i class="fi-alert size-36 left m20r"></i>
|
||||
<strong class="size-16" translate>Confirm your email address</strong>.<br>
|
||||
<span translate>An email was sent to</span> {{$root.iden.getName()}}.
|
||||
<span translate>Please follow the link on it to confirm it. Unconfirmed profiles could be deleted from server.</span>
|
||||
|
||||
</div>
|
||||
<div class="status" ng-if="$root.reconnecting">
|
||||
<i class="fi-loop icon-rotate"></i>
|
||||
<strong class="size-16" translate>Network Error</strong>.<br>
|
||||
<span translate>Attempting to reconnect...</span>
|
||||
</div>
|
||||
|
||||
<div ng-if="$root.iden">
|
||||
<div ng-controller="SidebarController" ng-show="!$root.hideNavigation && !$root.wpInputFocused">
|
||||
<nav class="tab-bar">
|
||||
<section class="left-small">
|
||||
<a class="p10"
|
||||
ng-click="openMenu()"><i class="fi-list size-24"></i></a>
|
||||
</section>
|
||||
|
||||
<section class="right-small"
|
||||
ng-show="$root.wallet.isComplete() && !$root.hideWalletNavigation && isCordova">
|
||||
<a class="p10"
|
||||
ng-click="openScanner()"><i class="fi-camera size-24"></i></a>
|
||||
</section>
|
||||
|
||||
<section class="middle tab-bar-section">
|
||||
<h1 class="title ellipsis" ng-show="$root.wallet && !$root.hideWalletNavigation">
|
||||
{{$root.wallet.getName()}}
|
||||
</h1>
|
||||
</section>
|
||||
</nav>
|
||||
|
||||
<nav class="left-off-canvas-menu">
|
||||
<div ng-include="'views/includes/sidebar-mobile.html'"></div>
|
||||
</nav>
|
||||
|
||||
<div ng-include="'views/includes/sidebar.html'"
|
||||
role='navigation'
|
||||
class="sidebar"></div>
|
||||
<div class="bottom-bar" ng-if="$root.wallet &&
|
||||
$root.wallet.isComplete() && !$root.wallet.isLocked && !$root.hideWalletNavigation">
|
||||
<div ng-include="'views/includes/bottombar-mobile.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div notifications="right top"></div>
|
||||
|
||||
<div
|
||||
ng-controller="HeadController"
|
||||
class="head show-for-large-up"
|
||||
ng-include="'views/includes/head.html'"
|
||||
ng-if="$root.iden"
|
||||
ng-class="{'dni':$root.hideNavigation || $root.wpInputFocused}"
|
||||
></div>
|
||||
|
||||
<section
|
||||
ng-class="{'main':$root.iden && !$root.starting && !$root.hideNavigation && !$root.wpInputFocused}"
|
||||
ng-view></section>
|
||||
|
||||
<a class="close-menu" ng-click="closeMenu()"></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="css/vendors.min.css">
|
||||
<link rel="stylesheet" href="css/copay.min.css">
|
||||
|
||||
<script src="lib/vendors.js"></script>
|
||||
<script src="lib/angularjs-all.js"></script>
|
||||
|
||||
<!-- DO NOT DELETE THIS COMMET -->
|
||||
<!-- PLACEHOLDER: CORDOVA SRIPT -->
|
||||
|
||||
<script src="init.js"></script>
|
||||
<script src="config.js"></script>
|
||||
<script src="js/copayBundle.js"></script>
|
||||
<script src="js/copayMain.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
29
init.js
29
init.js
|
@ -1,29 +0,0 @@
|
|||
var isChromeApp = window.chrome && chrome.runtime && chrome.runtime.id;
|
||||
var ld;
|
||||
if (!isChromeApp) {
|
||||
ld = (document.all);
|
||||
}
|
||||
|
||||
var ns4 = document.layers;
|
||||
var ns6 = !isChromeApp && document.getElementById && !document.all;
|
||||
var ie4 = !isChromeApp && document.all;
|
||||
if (ns4) {
|
||||
ld = document.loading;
|
||||
} else if (ns6) {
|
||||
ld = document.getElementById("loading").style;
|
||||
} else if (ie4) {
|
||||
ld = document.all.loading.style;
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (ns4) {
|
||||
ld.visibility = "hidden";
|
||||
} else if (ns6 || ie4) {
|
||||
ld.display = "none";
|
||||
} else {
|
||||
ld = document.getElementById("loading").style;
|
||||
ld.visibility = "hidden";
|
||||
ld.display = "none";
|
||||
}
|
||||
}
|
||||
init();
|
|
@ -1,8 +0,0 @@
|
|||
chrome.app.runtime.onLaunched.addListener(function() {
|
||||
chrome.app.window.create('index.html', {
|
||||
'bounds': {
|
||||
'width': 1200,
|
||||
'height': 800
|
||||
}
|
||||
});
|
||||
});
|
69
js/app.js
69
js/app.js
|
@ -1,69 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var copay = require('copay');
|
||||
var _ = require('lodash');
|
||||
var LS = require('../js/plugins/LocalStorage');
|
||||
var ls = new LS();
|
||||
|
||||
|
||||
// TODO move this to configService !
|
||||
var config = copay.defaultConfig;
|
||||
|
||||
ls.getItem('config', function(err, data) {
|
||||
var localConfig;
|
||||
try {
|
||||
localConfig = JSON.parse(data);
|
||||
} catch(e) {};
|
||||
if (localConfig) {
|
||||
var cmv = copay.version.split('.')[1];
|
||||
var lmv = localConfig.version ? localConfig.version.split('.')[1] : '-1';
|
||||
if (cmv === lmv) {
|
||||
_.each(localConfig, function(value, key) {
|
||||
config[key] = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var modules = [
|
||||
'ngRoute',
|
||||
'angularMoment',
|
||||
'mm.foundation',
|
||||
'monospaced.qrcode',
|
||||
'ngIdle',
|
||||
'gettext',
|
||||
'ui.gravatar',
|
||||
'ngTouch',
|
||||
'copayApp.filters',
|
||||
'copayApp.services',
|
||||
'copayApp.controllers',
|
||||
'copayApp.directives',
|
||||
];
|
||||
|
||||
var copayApp = window.copayApp = angular.module('copayApp', modules);
|
||||
|
||||
var defaults = JSON.parse(JSON.stringify(copay.defaultConfig));
|
||||
copayApp.value('defaults', defaults);
|
||||
|
||||
copayApp.config(function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhitelist([
|
||||
'self',
|
||||
'mailto:**'
|
||||
]);
|
||||
});
|
||||
|
||||
angular.module('ui.gravatar').config([
|
||||
'gravatarServiceProvider',
|
||||
function(gravatarServiceProvider) {
|
||||
gravatarServiceProvider.defaults = {
|
||||
size: 35
|
||||
};
|
||||
// Use https endpoint
|
||||
gravatarServiceProvider.secure = true;
|
||||
}
|
||||
]);
|
||||
|
||||
angular.module('copayApp.filters', []);
|
||||
angular.module('copayApp.services', []);
|
||||
angular.module('copayApp.controllers', []);
|
||||
angular.module('copayApp.directives', []);
|
|
@ -1,72 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('CopayersController',
|
||||
function($scope, $rootScope, $timeout, go, identityService, notification, isCordova) {
|
||||
var w = $rootScope.wallet;
|
||||
|
||||
|
||||
$scope.init = function() {
|
||||
$rootScope.title = 'Share this secret with your copayers';
|
||||
$scope.loading = false;
|
||||
$scope.secret = $rootScope.wallet.getSecret();
|
||||
$scope.isCordova = isCordova;
|
||||
|
||||
w.on('publicKeyRingUpdated', $scope.updateList);
|
||||
w.on('ready', $scope.updateList);
|
||||
|
||||
$scope.updateList();
|
||||
};
|
||||
|
||||
$scope.updateList = function() {
|
||||
var w = $rootScope.wallet;
|
||||
|
||||
$scope.copayers = $rootScope.wallet.getRegisteredPeerIds();
|
||||
if (w.isComplete()) {
|
||||
|
||||
w.removeListener('publicKeyRingUpdated', $scope.updateList);
|
||||
w.removeListener('ready', $scope.updateList);
|
||||
go.walletHome();
|
||||
}
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.deleteWallet = function() {
|
||||
$rootScope.starting = true;
|
||||
$timeout(function() {
|
||||
identityService.deleteWallet(w, function(err) {
|
||||
$rootScope.starting = false;
|
||||
if (err) {
|
||||
$scope.error = err.message || err;
|
||||
copay.logger.warn(err);
|
||||
$timeout(function () { $scope.$digest(); });
|
||||
} else {
|
||||
if ($rootScope.wallet) {
|
||||
go.walletHome();
|
||||
}
|
||||
$timeout(function() {
|
||||
notification.success('Success', 'The wallet "' + (w.name || w.id) + '" was deleted');
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.copySecret = function(secret) {
|
||||
if (isCordova) {
|
||||
window.cordova.plugins.clipboard.copy(secret);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.shareSecret = function(secret) {
|
||||
if (isCordova) {
|
||||
if (isMobile.Android() || isMobile.Windows()) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.socialsharing.share(secret, null, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
|
@ -1,78 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('CreateController',
|
||||
function($scope, $rootScope, $location, $timeout, identityService, backupService, notification, defaults, isMobile, isCordova) {
|
||||
|
||||
$rootScope.fromSetup = true;
|
||||
$scope.loading = false;
|
||||
$scope.walletPassword = $rootScope.walletPassword;
|
||||
$scope.isMobile = isMobile.any();
|
||||
$scope.hideAdv = true;
|
||||
$scope.networkName = config.networkName;
|
||||
$rootScope.title = 'Create new wallet';
|
||||
$rootScope.hideWalletNavigation = true;
|
||||
$scope.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
||||
|
||||
// ng-repeat defined number of times instead of repeating over array?
|
||||
$scope.getNumber = function(num) {
|
||||
return new Array(num);
|
||||
}
|
||||
|
||||
$scope.totalCopayers = config.wallet.totalCopayers;
|
||||
$scope.TCValues = _.range(1, config.limits.totalCopayers + 1);
|
||||
|
||||
var updateRCSelect = function(n) {
|
||||
var maxReq = copay.Wallet.getMaxRequiredCopayers(n);
|
||||
$scope.RCValues = _.range(1, maxReq + 1);
|
||||
$scope.requiredCopayers = Math.min(parseInt(n / 2 + 1), maxReq);
|
||||
};
|
||||
|
||||
updateRCSelect($scope.totalCopayers);
|
||||
|
||||
$scope.$watch('totalCopayers', function(tc) {
|
||||
updateRCSelect(tc);
|
||||
});
|
||||
|
||||
$scope.$watch('networkName', function(tc) {
|
||||
$scope.networkUrl = config.network[$scope.networkName].url;
|
||||
});
|
||||
|
||||
$scope.showNetwork = function() {
|
||||
return $scope.networkUrl != defaults.network.livenet.url && $scope.networkUrl != defaults.network.testnet.url;
|
||||
};
|
||||
|
||||
|
||||
$scope.create = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
$scope.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
var opts = {
|
||||
requiredCopayers: $scope.requiredCopayers,
|
||||
totalCopayers: $scope.totalCopayers,
|
||||
name: $scope.walletName,
|
||||
privateKeyHex: $scope.private,
|
||||
networkName: $scope.networkName,
|
||||
};
|
||||
$rootScope.starting = true;
|
||||
identityService.createWallet(opts, function(err, wallet){
|
||||
$rootScope.starting = false;
|
||||
if (err || !wallet) {
|
||||
copay.logger.debug(err);
|
||||
if (err.match('OVERQUOTA')){
|
||||
$scope.error = 'Could not create wallet: storage limits on remove server exceeded';
|
||||
} else {
|
||||
$scope.error = 'Could not create wallet: ' + err;
|
||||
}
|
||||
}
|
||||
|
||||
$timeout(function(){
|
||||
$rootScope.$digest();
|
||||
},1);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
$rootScope.hideWalletNavigation = false;
|
||||
});
|
||||
});
|
|
@ -1,202 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('CreateProfileController', function($scope, $rootScope, $location, $timeout, $window, notification, pluginManager, identityService, pinService, isMobile, isCordova, configService, go) {
|
||||
|
||||
var _credentials;
|
||||
|
||||
$scope.init = function() {
|
||||
|
||||
if ($rootScope.wallet)
|
||||
go.walletHome();
|
||||
|
||||
$scope.isMobile = isMobile.any();
|
||||
$scope.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
||||
$scope.hideForWP = 0;
|
||||
$scope.digits = [];
|
||||
$scope.defined = [];
|
||||
$scope.askForPin = 0;
|
||||
|
||||
$scope.createStep = 'storage';
|
||||
$scope.useLocalstorage = false;
|
||||
$scope.minPasswordStrength = _.isUndefined(config.minPasswordStrength) ?
|
||||
4 : config.minPasswordStrength;
|
||||
|
||||
};
|
||||
|
||||
$scope.clear = function() {
|
||||
pinService.clearPin($scope);
|
||||
};
|
||||
|
||||
$scope.press = function(digit) {
|
||||
pinService.pressPin($scope, digit, true);
|
||||
};
|
||||
|
||||
$scope.skip = function () {
|
||||
pinService.skipPin($scope, true);
|
||||
};
|
||||
|
||||
$scope.formFocus = function() {
|
||||
if (!$scope.isWindowsPhoneApp) return
|
||||
$scope.hideForWP = true;
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.createPin = function(pin) {
|
||||
preconditions.checkArgument(pin);
|
||||
preconditions.checkState($rootScope.iden);
|
||||
preconditions.checkState(_credentials && _credentials.email);
|
||||
$rootScope.starting = true;
|
||||
|
||||
$timeout(function() {
|
||||
pinService.save(pin, _credentials.email, _credentials.password, function(err) {
|
||||
_credentials.password = '';
|
||||
_credentials = null;
|
||||
$scope.askForPin = 0;
|
||||
$rootScope.hasPin = true;
|
||||
$scope.createDefaultWallet();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
$scope.setStep = function(step) {
|
||||
$scope.error = null;
|
||||
$scope.createStep = step;
|
||||
$scope.hideForWP = false;
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.selectStorage = function(storage) {
|
||||
$scope.useLocalstorage = storage == 'local';
|
||||
$scope.hideForWP = false;
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.goToEmail = function() {
|
||||
$scope.createStep = 'email';
|
||||
$scope.useEmail = !$scope.useLocalstorage;
|
||||
};
|
||||
|
||||
$scope.setEmailOrUsername = function(form) {
|
||||
$scope.userOrEmail = $scope.useLocalstorage ? form.username.$modelValue : form.email.$modelValue;
|
||||
preconditions.checkState($scope.userOrEmail);
|
||||
|
||||
$scope.error = null;
|
||||
$scope.hideForWP = false;
|
||||
$scope.createStep = 'pass';
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
/* Last step. Will emit after creation so the UX gets updated */
|
||||
$scope.createDefaultWallet = function() {
|
||||
$rootScope.hideNavigation = false;
|
||||
$rootScope.starting = true;
|
||||
identityService.createDefaultWallet(function(err) {
|
||||
$scope.askForPin = 0;
|
||||
$rootScope.starting = null;
|
||||
|
||||
if (err) {
|
||||
var msg = err.toString();
|
||||
$scope.error = msg;
|
||||
} else {
|
||||
if (!$scope.useLocalstorage) {
|
||||
$rootScope.pleaseConfirmEmail = true;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
$scope._doCreateProfile = function(emailOrUsername, password, cb) {
|
||||
preconditions.checkArgument(_.isString(emailOrUsername));
|
||||
preconditions.checkArgument(_.isString(password));
|
||||
|
||||
$rootScope.hideNavigation = false;
|
||||
|
||||
identityService.create(emailOrUsername, password, function(err) {
|
||||
if (err) {
|
||||
var msg = err.toString();
|
||||
$scope.createStep = 'email';
|
||||
if (msg.indexOf('EEXIST') >= 0 || msg.indexOf('BADC') >= 0) {
|
||||
msg = 'This profile already exists'
|
||||
}
|
||||
if (msg.indexOf('EMAILERROR') >= 0) {
|
||||
msg = 'Could not send verification email. Please check your email address.';
|
||||
}
|
||||
return cb(msg);
|
||||
} else {
|
||||
// mobile
|
||||
if ($scope.isMobile) {
|
||||
$rootScope.starting = null;
|
||||
_credentials = {
|
||||
email: emailOrUsername,
|
||||
password: password,
|
||||
};
|
||||
$scope.askForPin = 1;
|
||||
$scope.hideForWP = 0;
|
||||
|
||||
$rootScope.hideNavigation = true;
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
return;
|
||||
} else {
|
||||
$scope.createDefaultWallet();
|
||||
}
|
||||
}
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.saveSettings = function(cb) {
|
||||
var plugins = config.plugins;
|
||||
|
||||
plugins.EncryptedLocalStorage = false;
|
||||
plugins.EncryptedInsightStorage = false;
|
||||
|
||||
var pluginName = $scope.useLocalstorage ? 'EncryptedLocalStorage' : 'EncryptedInsightStorage';
|
||||
plugins[pluginName] = true;
|
||||
|
||||
configService.set({
|
||||
plugins: plugins
|
||||
}, cb);
|
||||
};
|
||||
|
||||
|
||||
$scope.createProfile = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
$scope.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.saveSettings(function(err) {
|
||||
preconditions.checkState(!err, err);
|
||||
$rootScope.starting = true;
|
||||
|
||||
$scope._doCreateProfile($scope.userOrEmail, form.password.$modelValue, function(err) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
$scope.passwordStrength = null;
|
||||
$rootScope.starting = false;
|
||||
}
|
||||
form.password.$setViewValue('');
|
||||
form.password.$render();
|
||||
form.repeatpassword.$setViewValue('');
|
||||
form.repeatpassword.$render();
|
||||
form.$setPristine();
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
|
@ -1,204 +0,0 @@
|
|||
'use strict';
|
||||
var bitcore = require('bitcore');
|
||||
|
||||
angular.module('copayApp.controllers').controller('HistoryController',
|
||||
function($scope, $rootScope, $filter, $timeout, $modal, rateService, notification, go) {
|
||||
var w = $rootScope.wallet;
|
||||
|
||||
$rootScope.title = 'History';
|
||||
$scope.loading = false;
|
||||
$scope.generating = false;
|
||||
$scope.lastShowed = false;
|
||||
|
||||
$scope.currentPage = 1;
|
||||
$scope.itemsPerPage = 10;
|
||||
$scope.nbPages = 0;
|
||||
$scope.totalItems = 0;
|
||||
$scope.blockchain_txs = [];
|
||||
$scope.alternativeCurrency = [];
|
||||
|
||||
$scope.selectPage = function(page) {
|
||||
$scope.paging = true;
|
||||
$scope.currentPage = page;
|
||||
$scope.update();
|
||||
};
|
||||
|
||||
$scope.downloadHistory = function() {
|
||||
var w = $rootScope.wallet;
|
||||
if (!w) return;
|
||||
|
||||
var filename = "copay_history.csv";
|
||||
var descriptor = {
|
||||
columns: [{
|
||||
label: 'Date',
|
||||
property: 'ts',
|
||||
type: 'date'
|
||||
}, {
|
||||
label: 'Amount (' + w.settings.unitName + ')',
|
||||
property: 'amount',
|
||||
type: 'number'
|
||||
}, {
|
||||
label: 'Amount (' + w.settings.alternativeIsoCode + ')',
|
||||
property: 'alternativeAmount'
|
||||
}, {
|
||||
label: 'Action',
|
||||
property: 'action'
|
||||
}, {
|
||||
label: 'AddressTo',
|
||||
property: 'addressTo'
|
||||
}, {
|
||||
label: 'Comment',
|
||||
property: 'comment'
|
||||
}, ],
|
||||
};
|
||||
if (w.isShared()) {
|
||||
descriptor.columns.push({
|
||||
label: 'Signers',
|
||||
property: function(obj) {
|
||||
if (!obj.actionList) return '';
|
||||
return _.map(obj.actionList, function(action) {
|
||||
return w.publicKeyRing.nicknameForCopayer(action.cId);
|
||||
}).join('|');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.generating = true;
|
||||
|
||||
$scope._getTransactions(w, null, function(err, res) {
|
||||
if (err) {
|
||||
$scope.generating = false;
|
||||
logger.error(err);
|
||||
notification.error('Could not get transaction history');
|
||||
return;
|
||||
}
|
||||
$scope._addRates(w, res.items, function(err) {
|
||||
copay.csv.toCsv(res.items, descriptor, function(err, res) {
|
||||
if (err) {
|
||||
$scope.generating = false;
|
||||
logger.error(err);
|
||||
notification.error('Could not generate csv file');
|
||||
return;
|
||||
}
|
||||
var csvContent = "data:text/csv;charset=utf-8," + res;
|
||||
var encodedUri = encodeURI(csvContent);
|
||||
var link = document.createElement("a");
|
||||
link.setAttribute("href", encodedUri);
|
||||
link.setAttribute("download", filename);
|
||||
link.click();
|
||||
$scope.generating = false;
|
||||
$scope.$digest();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.update = function() {
|
||||
$scope.getTransactions();
|
||||
};
|
||||
|
||||
$scope.show = function() {
|
||||
$scope.loading = true;
|
||||
setTimeout(function() {
|
||||
$scope.update();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope._getTransactions = function(w, opts, cb) {
|
||||
w.getTransactionHistory(opts, function(err, res) {
|
||||
if (err) return cb(err);
|
||||
if (!res) return cb();
|
||||
|
||||
var now = new Date();
|
||||
var items = res.items;
|
||||
_.each(items, function(tx) {
|
||||
tx.ts = tx.minedTs || tx.sentTs;
|
||||
tx.rateTs = Math.floor((tx.ts || now) / 1000);
|
||||
tx.amount = $filter('noFractionNumber')(tx.amount);
|
||||
});
|
||||
return cb(null, res);
|
||||
});
|
||||
};
|
||||
|
||||
$scope._addRates = function(w, txs, cb) {
|
||||
if (!txs || txs.length == 0) return cb();
|
||||
var index = _.groupBy(txs, 'rateTs');
|
||||
rateService.getHistoricRates(w.settings.alternativeIsoCode, _.keys(index), function(err, res) {
|
||||
if (err || !res) return cb(err);
|
||||
_.each(res, function(r) {
|
||||
_.each(index[r.ts], function (tx) {
|
||||
var alternativeAmount = (r.rate != null ? tx.amountSat * rateService.SAT_TO_BTC * r.rate : null);
|
||||
tx.alternativeAmount = alternativeAmount ? $filter('noFractionNumber')(alternativeAmount, 2) : null;
|
||||
});
|
||||
});
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.openTxModal = function(btx) {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.btx = btx;
|
||||
|
||||
$scope.getShortNetworkName = function() {
|
||||
var w = $rootScope.wallet;
|
||||
return w.getNetworkName().substring(0, 4);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/tx-details.html',
|
||||
windowClass: 'medium',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getTransactions = function() {
|
||||
var w = $rootScope.wallet;
|
||||
if (!w) return;
|
||||
|
||||
$scope.blockchain_txs = w.cached_txs || [];
|
||||
$scope.loading = true;
|
||||
|
||||
$scope._getTransactions(w, {
|
||||
currentPage: $scope.currentPage,
|
||||
itemsPerPage: $scope.itemsPerPage,
|
||||
}, function(err, res) {
|
||||
if (err) throw err;
|
||||
|
||||
if (!res) {
|
||||
$scope.loading = false;
|
||||
$scope.lastShowed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var items = res.items;
|
||||
$scope._addRates(w, items, function(err) {
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
})
|
||||
|
||||
$scope.blockchain_txs = w.cached_txs = items;
|
||||
$scope.nbPages = res.nbPages;
|
||||
$scope.totalItems = res.nbItems;
|
||||
|
||||
$scope.loading = false;
|
||||
$scope.paging = false;
|
||||
setTimeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.hasAction = function(actions, action) {
|
||||
return actions.hasOwnProperty('create');
|
||||
};
|
||||
|
||||
});
|
|
@ -1,216 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $timeout, $window, go, notification, identityService, Compatibility, pinService, applicationService, isMobile, isCordova, localstorageService) {
|
||||
|
||||
var KEY = 'CopayDisclaimer';
|
||||
var ls = localstorageService;
|
||||
var _credentials;
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.isMobile = isMobile.any();
|
||||
$scope.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
||||
$scope.hideForWP = 0;
|
||||
$scope.attempt = 0;
|
||||
$scope.digits = [];
|
||||
$scope.defined = [];
|
||||
$scope.askForPin = 0;
|
||||
|
||||
// This is only for backwards compat, insight api should link to #!/confirmed directly
|
||||
if (getParam('confirmed')) {
|
||||
var hashIndex = window.location.href.indexOf('/?');
|
||||
window.location = window.location.href.substr(0, hashIndex) + '#!/confirmed';
|
||||
return;
|
||||
}
|
||||
|
||||
if ($rootScope.fromEmailConfirmation) {
|
||||
$scope.confirmedEmail = true;
|
||||
$rootScope.fromEmailConfirmation = false;
|
||||
}
|
||||
|
||||
if ($rootScope.wallet) {
|
||||
go.walletHome();
|
||||
}
|
||||
|
||||
Compatibility.check($scope);
|
||||
pinService.check(function(err, value) {
|
||||
$rootScope.hasPin = value;
|
||||
});
|
||||
$scope.usingLocalStorage = config.plugins.EncryptedLocalStorage;
|
||||
|
||||
if (isCordova) {
|
||||
ls.getItem(KEY, function(err, value) {
|
||||
$scope.showDisclaimer = value ? null : true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.clear = function() {
|
||||
pinService.clearPin($scope);
|
||||
};
|
||||
|
||||
$scope.press = function(digit) {
|
||||
pinService.pressPin($scope, digit);
|
||||
};
|
||||
|
||||
$scope.skip = function () {
|
||||
pinService.skipPin($scope);
|
||||
};
|
||||
|
||||
$scope.agreeDisclaimer = function() {
|
||||
ls.setItem(KEY, true, function(err) {
|
||||
$scope.showDisclaimer = null;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.formFocus = function() {
|
||||
if ($scope.isWindowsPhoneApp) {
|
||||
$scope.hideForWP = true;
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.openWithPin = function(pin) {
|
||||
|
||||
if (!pin) {
|
||||
$scope.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
$rootScope.starting = true;
|
||||
|
||||
$timeout(function() {
|
||||
var credentials = pinService.get(pin, function(err, credentials) {
|
||||
if (err || !credentials) {
|
||||
$rootScope.starting = null;
|
||||
$scope.error = 'Wrong PIN';
|
||||
$scope.clear();
|
||||
$timeout(function() {
|
||||
$scope.error = null;
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
$scope.open(credentials.email, credentials.password);
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.openWallets = function() {
|
||||
preconditions.checkState($rootScope.iden);
|
||||
var iden = $rootScope.iden;
|
||||
$rootScope.hideNavigation = false;
|
||||
$rootScope.starting = true;
|
||||
iden.openWallets();
|
||||
};
|
||||
|
||||
$scope.createPin = function(pin) {
|
||||
preconditions.checkArgument(pin);
|
||||
preconditions.checkState($rootScope.iden);
|
||||
preconditions.checkState(_credentials && _credentials.email);
|
||||
$rootScope.starting = true;
|
||||
|
||||
$timeout(function() {
|
||||
pinService.save(pin, _credentials.email, _credentials.password, function(err) {
|
||||
_credentials.password = '';
|
||||
_credentials = null;
|
||||
$scope.askForPin = 0;
|
||||
$rootScope.hasPin = true;
|
||||
$rootScope.starting = null;
|
||||
$scope.openWallets();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.openWithCredentials = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
$scope.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
|
||||
$timeout(function() {
|
||||
$scope.open(form.email.$modelValue, form.password.$modelValue);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
$scope.pinLogout = function() {
|
||||
pinService.clear(function() {
|
||||
copay.logger.debug('PIN erased');
|
||||
delete $rootScope['hasPin'];
|
||||
applicationService.restart();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.open = function(email, password) {
|
||||
$rootScope.starting = true;
|
||||
identityService.open(email, password, function(err, iden) {
|
||||
if (err) {
|
||||
$rootScope.starting = false;
|
||||
copay.logger.warn(err);
|
||||
|
||||
var identifier = $scope.usingLocalStorage ? 'username' : 'email';
|
||||
if ((err.toString() || '').match('PNOTFOUND')) {
|
||||
$scope.error = 'Invalid ' + identifier + ' or password';
|
||||
|
||||
if ($scope.attempt++ > 1) {
|
||||
var storage = $scope.usingLocalStorage ? 'this device storage' : 'cloud storage';
|
||||
$scope.error = 'Invalid ' + identifier + ' or password. You are trying to sign in using ' + storage + '. Change it on settings if necessary.';
|
||||
};
|
||||
|
||||
$rootScope.hasPin = false;
|
||||
pinService.clear(function() {});
|
||||
|
||||
} else if ((err.toString() || '').match('Connection')) {
|
||||
$scope.error = 'Could not connect to Insight Server';
|
||||
} else if ((err.toString() || '').match('Unable')) {
|
||||
$scope.error = 'Unable to read data from the Insight Server';
|
||||
} else {
|
||||
$scope.error = 'Unknown error';
|
||||
}
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1)
|
||||
return;
|
||||
}
|
||||
|
||||
// Open successfully?
|
||||
if (iden) {
|
||||
$scope.error = null;
|
||||
$scope.confirmedEmail = false;
|
||||
|
||||
// mobile
|
||||
if ($scope.isMobile && !$rootScope.hasPin) {
|
||||
_credentials = {
|
||||
email: email,
|
||||
password: password,
|
||||
};
|
||||
$scope.askForPin = 1;
|
||||
$rootScope.starting = false;
|
||||
$rootScope.hideNavigation = true;
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
return;
|
||||
}
|
||||
// no mobile
|
||||
else {
|
||||
$scope.openWallets();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getParam(sname) {
|
||||
var params = location.search.substr(location.search.indexOf("?") + 1);
|
||||
var sval = "";
|
||||
params = params.split("&");
|
||||
// split param and value into individual pieces
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var temp = params[i].split("=");
|
||||
if ([temp[0]] == sname) {
|
||||
sval = temp[1];
|
||||
}
|
||||
}
|
||||
return sval;
|
||||
}
|
||||
});
|
|
@ -1,103 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('HomeWalletController', function($scope, $rootScope, $timeout, $filter, $modal, rateService, notification, txStatus, identityService, isCordova) {
|
||||
|
||||
$scope.openTxModal = function(tx) {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
var w = $rootScope.wallet;
|
||||
$scope.error = null;
|
||||
$scope.tx = tx;
|
||||
$scope.registeredCopayerIds = w.getRegisteredCopayerIds();
|
||||
$scope.loading = null;
|
||||
|
||||
$scope.getShortNetworkName = function() {
|
||||
var w = $rootScope.wallet;
|
||||
return w.getNetworkName().substring(0, 4);
|
||||
};
|
||||
|
||||
$scope.sign = function(ntxid) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Signing transaction...', true);
|
||||
}
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
w.signAndSend(ntxid, function(err, id, status) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = 'Transaction could not send. Please try again.';
|
||||
$scope.$digest();
|
||||
}
|
||||
else {
|
||||
$modalInstance.close(status);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.reject = function(ntxid) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Rejecting transaction...', true);
|
||||
}
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
w.reject(ntxid, function(err, status) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
$scope.$digest();
|
||||
}
|
||||
else {
|
||||
$modalInstance.close(status);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.broadcast = function(ntxid) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Sending transaction...', true);
|
||||
}
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
w.issueTx(ntxid, function(err, txid, status) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = 'Transaction could not send. Please try again.';
|
||||
$scope.$digest();
|
||||
}
|
||||
else {
|
||||
$modalInstance.close(status);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/txp-details.html',
|
||||
windowClass: 'medium',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
|
||||
modalInstance.result.then(function(status) {
|
||||
txStatus.notify(status);
|
||||
});
|
||||
|
||||
};
|
||||
});
|
|
@ -1,110 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('ImportController',
|
||||
function($scope, $rootScope, $location, $timeout, identityService, notification, isMobile, isCordova, Compatibility) {
|
||||
|
||||
$rootScope.title = 'Import wallet';
|
||||
$scope.importStatus = 'Importing wallet - Reading backup...';
|
||||
$scope.hideAdv = true;
|
||||
$scope.isSafari = isMobile.Safari();
|
||||
$scope.isCordova = isCordova;
|
||||
$scope.importOpts = {};
|
||||
$rootScope.hideWalletNavigation = true;
|
||||
|
||||
|
||||
window.ignoreMobilePause = true;
|
||||
$scope.$on('$destroy', function() {
|
||||
$timeout(function(){
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
});
|
||||
|
||||
Compatibility.check($scope);
|
||||
|
||||
var reader = new FileReader();
|
||||
|
||||
var updateStatus = function(status) {
|
||||
$scope.importStatus = status;
|
||||
}
|
||||
|
||||
|
||||
$scope.getFile = function() {
|
||||
// If we use onloadend, we need to check the readyState.
|
||||
reader.onloadend = function(evt) {
|
||||
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
|
||||
var encryptedObj = evt.target.result;
|
||||
updateStatus('Importing wallet - Procesing backup...');
|
||||
identityService.importWallet(encryptedObj, $scope.password, {}, function(err) {
|
||||
if (err) {
|
||||
$rootScope.starting = false;
|
||||
$scope.error = 'Could not read wallet. Please check your password';
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.import = function(form) {
|
||||
|
||||
if (form.$invalid) {
|
||||
$scope.error = 'There is an error in the form';
|
||||
return;
|
||||
}
|
||||
|
||||
var backupFile = $scope.file;
|
||||
var backupText = form.backupText.$modelValue;
|
||||
var backupOldWallet = form.backupOldWallet.$modelValue;
|
||||
var password = form.password.$modelValue;
|
||||
|
||||
if (backupOldWallet) {
|
||||
backupText = backupOldWallet.value;
|
||||
}
|
||||
|
||||
if (!backupFile && !backupText) {
|
||||
$scope.error = 'Please, select your backup file';
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.starting = true;
|
||||
|
||||
$timeout(function() {
|
||||
|
||||
$scope.importOpts = {};
|
||||
|
||||
var skipFields = [];
|
||||
|
||||
if ($scope.skipPublicKeyRing)
|
||||
skipFields.push('publicKeyRing');
|
||||
|
||||
if ($scope.skipTxProposals)
|
||||
skipFields.push('txProposals');
|
||||
|
||||
if (skipFields)
|
||||
$scope.importOpts.skipFields = skipFields;
|
||||
|
||||
if (backupFile) {
|
||||
reader.readAsBinaryString(backupFile);
|
||||
} else {
|
||||
updateStatus('Importing wallet - Procesing backup...');
|
||||
identityService.importWallet(backupText, $scope.password, $scope.importOpts, function(err) {
|
||||
if (err) {
|
||||
$rootScope.starting = false;
|
||||
$scope.error = 'Could not read wallet. Please check your password';
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
$rootScope.hideWalletNavigation = false;
|
||||
});
|
||||
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('IndexController', function($scope, $timeout, go, isCordova, identityService, notification) {
|
||||
$scope.init = function() {
|
||||
|
||||
};
|
||||
|
||||
$scope.resendVerificationEmail = function() {
|
||||
$scope.loading = true;
|
||||
identityService.resendVerificationEmail(function(err) {
|
||||
if (err) {
|
||||
notification.error('Could not send email', 'There was a problem sending the verification email.');
|
||||
}
|
||||
else {
|
||||
notification.success('Email sent', 'Check your inbox and confirms the email');
|
||||
$scope.hideReSendButton = true;
|
||||
}
|
||||
$scope.loading = null;
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openMenu = function() {
|
||||
go.swipe(true);
|
||||
};
|
||||
|
||||
$scope.closeMenu = function() {
|
||||
go.swipe();
|
||||
};
|
||||
|
||||
});
|
|
@ -1,160 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('JoinController',
|
||||
function($scope, $rootScope, $timeout, isMobile, notification, identityService) {
|
||||
$rootScope.fromSetup = false;
|
||||
$scope.loading = false;
|
||||
$scope.isMobile = isMobile.any();
|
||||
$rootScope.title = 'Join shared wallet';
|
||||
$rootScope.hideWalletNavigation = true;
|
||||
|
||||
|
||||
// QR code Scanner
|
||||
var cameraInput;
|
||||
var video;
|
||||
var canvas;
|
||||
var $video;
|
||||
var context;
|
||||
var localMediaStream;
|
||||
|
||||
$scope.hideAdv = true;
|
||||
|
||||
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
|
||||
if (!window.cordova && !navigator.getUserMedia)
|
||||
$scope.disableScanner = 1;
|
||||
|
||||
var _scan = function(evt) {
|
||||
if (localMediaStream) {
|
||||
context.drawImage(video, 0, 0, 300, 225);
|
||||
|
||||
try {
|
||||
qrcode.decode();
|
||||
} catch (e) {
|
||||
//qrcodeError(e);
|
||||
}
|
||||
}
|
||||
|
||||
$timeout(_scan, 500);
|
||||
};
|
||||
|
||||
var _successCallback = function(stream) {
|
||||
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
||||
localMediaStream = stream;
|
||||
video.play();
|
||||
$timeout(_scan, 1000);
|
||||
};
|
||||
|
||||
var _scanStop = function() {
|
||||
$scope.showScanner = false;
|
||||
if (!$scope.isMobile) {
|
||||
if (localMediaStream && localMediaStream.stop) localMediaStream.stop();
|
||||
localMediaStream = null;
|
||||
video.src = '';
|
||||
}
|
||||
};
|
||||
|
||||
var _videoError = function(err) {
|
||||
_scanStop();
|
||||
};
|
||||
|
||||
qrcode.callback = function(data) {
|
||||
_scanStop();
|
||||
|
||||
$scope.$apply(function() {
|
||||
$scope.connectionId = data;
|
||||
$scope.joinForm.connectionId.$setViewValue(data);
|
||||
$scope.joinForm.connectionId.$render();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancelScanner = function() {
|
||||
_scanStop();
|
||||
};
|
||||
|
||||
$scope.openScanner = function() {
|
||||
if (window.cordova) return $scope.scannerIntent();
|
||||
|
||||
$scope.showScanner = true;
|
||||
|
||||
// Wait a moment until the canvas shows
|
||||
$timeout(function() {
|
||||
canvas = document.getElementById('qr-canvas');
|
||||
context = canvas.getContext('2d');
|
||||
|
||||
if ($scope.isMobile) {
|
||||
cameraInput = document.getElementById('qrcode-camera');
|
||||
cameraInput.addEventListener('change', _scan, false);
|
||||
} else {
|
||||
video = document.getElementById('qrcode-scanner-video');
|
||||
$video = angular.element(video);
|
||||
canvas.width = 300;
|
||||
canvas.height = 225;
|
||||
context.clearRect(0, 0, 300, 225);
|
||||
|
||||
navigator.getUserMedia({
|
||||
video: true
|
||||
}, _successCallback, _videoError);
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
|
||||
$scope.scannerIntent = function() {
|
||||
window.ignoreMobilePause = true;
|
||||
cordova.plugins.barcodeScanner.scan(
|
||||
function onSuccess(result) {
|
||||
$timeout(function(){
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
if (result.cancelled) return;
|
||||
|
||||
$scope.connectionId = result.text;
|
||||
$rootScope.$digest();
|
||||
},
|
||||
function onError(error) {
|
||||
$timeout(function(){
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
alert('Scanning error');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.join = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
notification.error('Error', 'Please enter the required fields');
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.starting = true;
|
||||
identityService.joinWallet({
|
||||
secret: $scope.connectionId,
|
||||
nickname: $scope.nickname,
|
||||
privateHex: $scope.private,
|
||||
}, function(err) {
|
||||
$rootScope.starting = false;
|
||||
if (err) {
|
||||
if (err === 'joinError')
|
||||
notification.error('Fatal error connecting to Insight server');
|
||||
else if (err === 'walletFull')
|
||||
notification.error('The wallet is full');
|
||||
else if (err === 'walletAlreadyExists')
|
||||
notification.error('Wallet already exists', 'Cannot join again from the same profile');
|
||||
else if (err === 'badNetwork')
|
||||
notification.error('Network Error', 'Wallet network configuration missmatch');
|
||||
else if (err === 'badSecret')
|
||||
notification.error('Bad secret', 'The secret string you entered is invalid');
|
||||
else {
|
||||
notification.error('Error', err.message || err);
|
||||
}
|
||||
}
|
||||
$timeout(function () { $scope.$digest(); }, 1);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
$rootScope.hideWalletNavigation = false;
|
||||
});
|
||||
});
|
|
@ -1,183 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('MoreController',
|
||||
function($scope, $rootScope, $location, $filter, $timeout, balanceService, notification, rateService, backupService, identityService, isMobile, isCordova, go, pendingTxsService) {
|
||||
var w = $rootScope.wallet;
|
||||
var max = $rootScope.quotaPerItem;
|
||||
$scope.isSafari = isMobile.Safari();
|
||||
$scope.isCordova = isCordova;
|
||||
$scope.wallet = w;
|
||||
$scope.error = null;
|
||||
$scope.success = null;
|
||||
|
||||
var bits = w.sizes().total;
|
||||
w.kb = $filter('noFractionNumber')(bits / 1000, 1);
|
||||
if (max) {
|
||||
w.usage = $filter('noFractionNumber')(bits / max * 100, 0);
|
||||
}
|
||||
|
||||
$rootScope.title = 'Settings';
|
||||
|
||||
$scope.unitOpts = [{
|
||||
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
||||
shortName: 'SAT',
|
||||
value: 1,
|
||||
decimals: 0
|
||||
}, {
|
||||
name: 'bits (1,000,000 bits = 1BTC)',
|
||||
shortName: 'bits',
|
||||
value: 100,
|
||||
decimals: 2
|
||||
}, {
|
||||
name: 'mBTC (1,000 mBTC = 1BTC)',
|
||||
shortName: 'mBTC',
|
||||
value: 100000,
|
||||
decimals: 5
|
||||
}, {
|
||||
name: 'BTC',
|
||||
shortName: 'BTC',
|
||||
value: 100000000,
|
||||
decimals: 8
|
||||
}];
|
||||
|
||||
$scope.selectedAlternative = {
|
||||
name: w.settings.alternativeName,
|
||||
isoCode: w.settings.alternativeIsoCode
|
||||
};
|
||||
$scope.alternativeOpts = rateService.isAvailable() ?
|
||||
rateService.listAlternatives() : [$scope.selectedAlternative];
|
||||
|
||||
rateService.whenAvailable(function() {
|
||||
$scope.alternativeOpts = rateService.listAlternatives();
|
||||
for (var ii in $scope.alternativeOpts) {
|
||||
if (w.settings.alternativeIsoCode === $scope.alternativeOpts[ii].isoCode) {
|
||||
$scope.selectedAlternative = $scope.alternativeOpts[ii];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
for (var ii in $scope.unitOpts) {
|
||||
if (w.settings.unitName === $scope.unitOpts[ii].shortName) {
|
||||
$scope.selectedUnit = $scope.unitOpts[ii];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.hideAdv = true;
|
||||
$scope.hidePriv = true;
|
||||
$scope.hideSecret = true;
|
||||
if (w) {
|
||||
$scope.priv = w.privateKey.toObj().extendedPrivateKeyString;
|
||||
$scope.secret = w.getSecret();
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
|
||||
$scope.save = function() {
|
||||
var w = $rootScope.wallet;
|
||||
w.changeSettings({
|
||||
unitName: $scope.selectedUnit.shortName,
|
||||
unitToSatoshi: $scope.selectedUnit.value,
|
||||
unitDecimals: $scope.selectedUnit.decimals,
|
||||
alternativeName: $scope.selectedAlternative.name,
|
||||
alternativeIsoCode: $scope.selectedAlternative.isoCode,
|
||||
});
|
||||
notification.success('Success', $filter('translate')('settings successfully updated'));
|
||||
balanceService.update(w, function() {
|
||||
pendingTxsService.update();
|
||||
$rootScope.$digest();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.purge = function(deleteAll) {
|
||||
var removed = w.purgeTxProposals(deleteAll);
|
||||
if (removed) {
|
||||
balanceService.update(w, function() {
|
||||
$rootScope.$digest();
|
||||
}, true);
|
||||
}
|
||||
notification.info('Transactions Proposals Purged', removed + ' ' + $filter('translate')('transaction proposal purged'));
|
||||
};
|
||||
|
||||
$scope.updateIndexes = function() {
|
||||
var w = $rootScope.wallet;
|
||||
notification.info('Scaning for transactions', 'Using derived addresses from your wallet');
|
||||
w.updateIndexes(function(err) {
|
||||
notification.info('Scan Ended', 'Updating balance');
|
||||
if (err) {
|
||||
notification.error('Error', $filter('translate')('Error updating indexes: ') + err);
|
||||
}
|
||||
balanceService.update(w, function() {
|
||||
notification.info('Finished', 'The balance is updated using the derived addresses');
|
||||
w.sendIndexes();
|
||||
$rootScope.$digest();
|
||||
}, true);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteWallet = function() {
|
||||
$scope.loading = true;
|
||||
$timeout(function() {
|
||||
identityService.deleteWallet(w, function(err) {
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = err.message || err;
|
||||
copay.logger.warn(err);
|
||||
$timeout(function () { $scope.$digest(); });
|
||||
} else {
|
||||
if ($rootScope.wallet) {
|
||||
go.walletHome();
|
||||
}
|
||||
$timeout(function() {
|
||||
notification.success('Success', 'The wallet "' + (w.name || w.id) + '" was deleted');
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.copyText = function(text) {
|
||||
if (isCordova) {
|
||||
window.cordova.plugins.clipboard.copy(text);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.downloadWalletBackup = function() {
|
||||
backupService.walletDownload(w);
|
||||
};
|
||||
|
||||
$scope.viewWalletBackup = function() {
|
||||
$scope.loading = true;
|
||||
$timeout(function() {
|
||||
$scope.backupWalletPlainText = backupService.walletEncrypted(w);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.copyWalletBackup = function() {
|
||||
var ew = backupService.walletEncrypted(w);
|
||||
window.cordova.plugins.clipboard.copy(ew);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
};
|
||||
|
||||
$scope.sendWalletBackup = function() {
|
||||
if (isMobile.Android() || isMobile.Windows()) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.toast.showShortCenter('Preparing backup...');
|
||||
var name = (w.name || w.id);
|
||||
var ew = backupService.walletEncrypted(w);
|
||||
var properties = {
|
||||
subject: 'Copay Wallet Backup: ' + name,
|
||||
body: 'Here is the encrypted backup of the wallet '
|
||||
+ name + ': \n\n' + ew
|
||||
+ '\n\n To import this backup, copy all text between {...}, including the symbols {}',
|
||||
isHtml: false
|
||||
};
|
||||
window.plugin.email.open(properties);
|
||||
};
|
||||
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
var bitcore = require('bitcore');
|
||||
|
||||
angular.module('copayApp.controllers').controller('paymentUriController', function($rootScope, $scope, $routeParams, $location, go) {
|
||||
|
||||
// Build bitcoinURI with querystring
|
||||
var query = [];
|
||||
angular.forEach($location.search(), function(value, key) {
|
||||
query.push(key + "=" + value);
|
||||
});
|
||||
var queryString = query ? query.join("&") : null;
|
||||
var bitcoinURI = $routeParams.data + ( queryString ? '?' + queryString : '');
|
||||
var uri = new bitcore.BIP21(bitcoinURI);
|
||||
|
||||
if (uri && uri.address && (_.isString(uri.address) || uri.address.isValid()) ) {
|
||||
copay.logger.debug('Payment Intent:', bitcoinURI);
|
||||
$rootScope.pendingPayment = bitcoinURI;
|
||||
}
|
||||
|
||||
go.home();
|
||||
});
|
|
@ -1,106 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('ReceiveController',
|
||||
function($scope, $rootScope, $timeout, $modal, isCordova, isMobile) {
|
||||
|
||||
$scope.newAddr = function() {
|
||||
var w = $rootScope.wallet;
|
||||
var lastAddr = w.generateAddress(null);
|
||||
$scope.setAddressList();
|
||||
$scope.addr = lastAddr;
|
||||
};
|
||||
|
||||
$scope.copyAddress = function(addr) {
|
||||
if (isCordova) {
|
||||
window.cordova.plugins.clipboard.copy('bitcoin:' + addr);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.shareAddress = function(addr) {
|
||||
if (isCordova) {
|
||||
if (isMobile.Android() || isMobile.Windows()) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.socialsharing.share('bitcoin:' + addr, null, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
$rootScope.title = 'Receive';
|
||||
$scope.showAll = false;
|
||||
$scope.isCordova = isCordova;
|
||||
|
||||
var w = $rootScope.wallet;
|
||||
var lastAddr = _.first(w.getAddressesOrdered());
|
||||
var balance = w.balanceInfo.balanceByAddr;
|
||||
$scope.setAddressList();
|
||||
|
||||
while (balance && balance[lastAddr] > 0) {
|
||||
$scope.loading = true;
|
||||
lastAddr = w.generateAddress(null);
|
||||
};
|
||||
$scope.loading = false;
|
||||
$scope.addr = lastAddr;
|
||||
};
|
||||
|
||||
$scope.openAddressModal = function(address) {
|
||||
var scope = $scope;
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance, address) {
|
||||
$scope.address = address;
|
||||
$scope.isCordova = isCordova;
|
||||
$scope.copyAddress = function(addr) {
|
||||
scope.copyAddress(addr);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/qr-address.html',
|
||||
windowClass: 'small',
|
||||
controller: ModalInstanceCtrl,
|
||||
resolve: {
|
||||
address: function() {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.toggleShowAll = function() {
|
||||
$scope.showAll = !$scope.showAll;
|
||||
$scope.setAddressList();
|
||||
};
|
||||
|
||||
$scope.setAddressList = function() {
|
||||
if ($scope.showAll) {
|
||||
var w = $rootScope.wallet;
|
||||
var balance = w.balanceInfo.balanceByAddr;
|
||||
|
||||
var addresses = w.getAddressesOrdered();
|
||||
if (addresses) {
|
||||
$scope.addrLength = addresses.length;
|
||||
|
||||
if (!$scope.showAll)
|
||||
addresses = addresses.slice(0, 3);
|
||||
|
||||
var list = [];
|
||||
_.each(addresses, function(address, index) {
|
||||
list.push({
|
||||
'index': index,
|
||||
'address': address,
|
||||
'balance': balance ? balance[address] : null,
|
||||
'isChange': w.addressIsChange(address),
|
||||
});
|
||||
});
|
||||
$scope.addresses = list;
|
||||
}
|
||||
} else {
|
||||
$scope.addresses = [];
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
|
@ -1,602 +0,0 @@
|
|||
'use strict';
|
||||
var bitcore = require('bitcore');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
angular.module('copayApp.controllers').controller('SendController',
|
||||
function($scope, $rootScope, $window, $timeout, $modal, $filter, notification, isMobile, rateService, txStatus, isCordova) {
|
||||
|
||||
$scope.init = function() {
|
||||
var w = $rootScope.wallet;
|
||||
preconditions.checkState(w);
|
||||
|
||||
preconditions.checkState(w.settings.unitToSatoshi);
|
||||
|
||||
$scope.isMobile = isMobile.any();
|
||||
$scope.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
||||
$rootScope.wpInputFocused = false;
|
||||
|
||||
$scope.isShared = w.isShared();
|
||||
$scope.requiresMultipleSignatures = w.requiresMultipleSignatures();
|
||||
$rootScope.title = $scope.requiresMultipleSignatures ? 'Send Proposal' : 'Send';
|
||||
$scope.loading = false;
|
||||
$scope.error = $scope.success = null;
|
||||
|
||||
$scope.alternativeName = w.settings.alternativeName;
|
||||
$scope.alternativeIsoCode = w.settings.alternativeIsoCode;
|
||||
|
||||
$scope.isRateAvailable = false;
|
||||
$scope.rateService = rateService;
|
||||
$scope.showScanner = false;
|
||||
$scope.myId = w.getMyCopayerId();
|
||||
$scope.isMobile = isMobile.any();
|
||||
|
||||
if ($rootScope.pendingPayment) {
|
||||
$timeout(function() {
|
||||
$scope.setFromUri($rootScope.pendingPayment)
|
||||
$rootScope.pendingPayment = null;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
$scope.setInputs();
|
||||
$scope.setScanner();
|
||||
|
||||
rateService.whenAvailable(function() {
|
||||
$scope.isRateAvailable = true;
|
||||
$scope.$digest();
|
||||
});
|
||||
};
|
||||
|
||||
if (isCordova) {
|
||||
var openScannerCordova = $rootScope.$on('dataScanned', function(event, data) {
|
||||
$scope.sendForm.address.$setViewValue(data);
|
||||
$scope.sendForm.address.$render();
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
openScannerCordova();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.formFocus = function(what) {
|
||||
if (!$scope.isWindowsPhoneApp) return
|
||||
|
||||
if (!what) {
|
||||
$rootScope.wpInputFocused = false;
|
||||
$scope.hideAddress = false;
|
||||
$scope.hideAmount = false;
|
||||
|
||||
} else {
|
||||
$rootScope.wpInputFocused = true;
|
||||
if (what == 'amount') {
|
||||
$scope.hideAddress = true;
|
||||
} else if (what == 'msg') {
|
||||
$scope.hideAddress = true;
|
||||
$scope.hideAmount = true;
|
||||
}
|
||||
|
||||
}
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.setInputs = function() {
|
||||
var w = $rootScope.wallet;
|
||||
var unitToSat = w.settings.unitToSatoshi;
|
||||
var satToUnit = 1 / unitToSat;
|
||||
/**
|
||||
* Setting the two related amounts as properties prevents an infinite
|
||||
* recursion for watches while preserving the original angular updates
|
||||
*
|
||||
*/
|
||||
Object.defineProperty($scope,
|
||||
"_alternative", {
|
||||
get: function() {
|
||||
return this.__alternative;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.__alternative = newValue;
|
||||
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||
this._amount = parseFloat(
|
||||
(rateService.fromFiat(newValue, $scope.alternativeIsoCode) * satToUnit).toFixed(w.settings.unitDecimals), 10);
|
||||
} else {
|
||||
this._amount = 0;
|
||||
}
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty($scope,
|
||||
"_amount", {
|
||||
get: function() {
|
||||
return this.__amount;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.__amount = newValue;
|
||||
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||
this.__alternative = parseFloat(
|
||||
(rateService.toFiat(newValue * unitToSat, $scope.alternativeIsoCode)).toFixed(2), 10);
|
||||
} else {
|
||||
this.__alternative = 0;
|
||||
}
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty($scope,
|
||||
"_address", {
|
||||
get: function() {
|
||||
return this.__address;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.__address = $scope.onAddressChange(newValue);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setScanner = function() {
|
||||
navigator.getUserMedia = navigator.getUserMedia ||
|
||||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia;
|
||||
window.URL = window.URL || window.webkitURL ||
|
||||
window.mozURL || window.msURL;
|
||||
|
||||
if (!window.cordova && !navigator.getUserMedia)
|
||||
$scope.disableScanner = 1;
|
||||
};
|
||||
|
||||
|
||||
$scope.setError = function(err) {
|
||||
var w = $rootScope.wallet;
|
||||
copay.logger.warn(err);
|
||||
|
||||
var msg = err.toString();
|
||||
if (msg.match('BIG'))
|
||||
msg = 'The transaction have too many inputs. Try creating many transactions for smaller amounts'
|
||||
|
||||
if (msg.match('totalNeededAmount') || msg.match('unspent not set'))
|
||||
msg = 'Insufficient funds'
|
||||
|
||||
if (msg.match('expired'))
|
||||
msg = 'The payment request has expired';
|
||||
|
||||
if (msg.match('XMLHttpRequest'))
|
||||
msg = 'Error when sending to the blockchain. Resend it from Home';
|
||||
|
||||
var message = 'The transaction' + ($scope.requiresMultipleSignatures ? ' proposal' : '') +
|
||||
' could not be created: ' + msg;
|
||||
|
||||
$scope.error = message;
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
$scope.submitForm = function(form) {
|
||||
var w = $rootScope.wallet;
|
||||
var unitToSat = w.settings.unitToSatoshi;
|
||||
|
||||
if (form.$invalid) {
|
||||
$scope.error = 'Unable to send transaction proposal';
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Creating transaction...', true);
|
||||
}
|
||||
|
||||
$scope.loading = true;
|
||||
if ($scope.isWindowsPhoneApp)
|
||||
$rootScope.wpInputFocused = true;
|
||||
|
||||
$timeout(function () {
|
||||
var comment = form.comment.$modelValue;
|
||||
var merchantData = $scope._merchantData;
|
||||
var address, amount;
|
||||
if (!merchantData) {
|
||||
address = form.address.$modelValue;
|
||||
amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
|
||||
}
|
||||
|
||||
w.spend({
|
||||
merchantData: merchantData,
|
||||
toAddress: address,
|
||||
amountSat: amount,
|
||||
comment: comment,
|
||||
}, function (err, txid, status) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
if ($scope.isWindowsPhoneApp)
|
||||
$rootScope.wpInputFocused = false;
|
||||
|
||||
if (err) {
|
||||
$scope.setError(err);
|
||||
}
|
||||
else {
|
||||
txStatus.notify(status);
|
||||
$scope.resetForm();
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// QR code Scanner
|
||||
var cameraInput;
|
||||
var video;
|
||||
var canvas;
|
||||
var $video;
|
||||
var context;
|
||||
var localMediaStream;
|
||||
|
||||
var _scan = function(evt) {
|
||||
if ($scope.isMobile) {
|
||||
$scope.scannerLoading = true;
|
||||
var files = evt.target.files;
|
||||
|
||||
if (files.length === 1 && files[0].type.indexOf('image/') === 0) {
|
||||
var file = files[0];
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function(theFile) {
|
||||
return function(e) {
|
||||
var mpImg = new MegaPixImage(file);
|
||||
mpImg.render(canvas, {
|
||||
maxWidth: 200,
|
||||
maxHeight: 200,
|
||||
orientation: 6
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
qrcode.width = canvas.width;
|
||||
qrcode.height = canvas.height;
|
||||
qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height);
|
||||
|
||||
try {
|
||||
qrcode.decode();
|
||||
} catch (e) {
|
||||
// error decoding QR
|
||||
}
|
||||
}, 1500);
|
||||
};
|
||||
})(file);
|
||||
|
||||
// Read in the file as a data URL
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
} else {
|
||||
if (localMediaStream) {
|
||||
context.drawImage(video, 0, 0, 300, 225);
|
||||
|
||||
try {
|
||||
qrcode.decode();
|
||||
} catch (e) {
|
||||
//qrcodeError(e);
|
||||
}
|
||||
}
|
||||
|
||||
$timeout(_scan, 500);
|
||||
}
|
||||
};
|
||||
|
||||
var _successCallback = function(stream) {
|
||||
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
||||
localMediaStream = stream;
|
||||
video.play();
|
||||
$timeout(_scan, 1000);
|
||||
};
|
||||
|
||||
var _scanStop = function() {
|
||||
$scope.scannerLoading = false;
|
||||
$scope.showScanner = false;
|
||||
if (!$scope.isMobile) {
|
||||
if (localMediaStream && localMediaStream.stop) localMediaStream.stop();
|
||||
localMediaStream = null;
|
||||
video.src = '';
|
||||
}
|
||||
};
|
||||
|
||||
var _videoError = function(err) {
|
||||
_scanStop();
|
||||
};
|
||||
|
||||
qrcode.callback = function(data) {
|
||||
_scanStop();
|
||||
$scope.$apply(function() {
|
||||
$scope.sendForm.address.$setViewValue(data);
|
||||
$scope.sendForm.address.$render();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancelScanner = function() {
|
||||
_scanStop();
|
||||
};
|
||||
|
||||
$scope.openScanner = function() {
|
||||
$scope.showScanner = true;
|
||||
|
||||
// Wait a moment until the canvas shows
|
||||
$timeout(function() {
|
||||
canvas = document.getElementById('qr-canvas');
|
||||
context = canvas.getContext('2d');
|
||||
|
||||
if ($scope.isMobile) {
|
||||
cameraInput = document.getElementById('qrcode-camera');
|
||||
cameraInput.addEventListener('change', _scan, false);
|
||||
} else {
|
||||
video = document.getElementById('qrcode-scanner-video');
|
||||
$video = angular.element(video);
|
||||
canvas.width = 300;
|
||||
canvas.height = 225;
|
||||
context.clearRect(0, 0, 300, 225);
|
||||
|
||||
navigator.getUserMedia({
|
||||
video: true
|
||||
}, _successCallback, _videoError);
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
|
||||
$scope.setTopAmount = function() {
|
||||
var w = $rootScope.wallet;
|
||||
var form = $scope.sendForm;
|
||||
if (form) {
|
||||
form.amount.$setViewValue(w.balanceInfo.topAmount);
|
||||
form.amount.$render();
|
||||
form.amount.$isValid = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setForm = function(to, amount, comment) {
|
||||
var form = $scope.sendForm;
|
||||
if (to) {
|
||||
form.address.$setViewValue(to);
|
||||
form.address.$isValid = true;
|
||||
form.address.$render();
|
||||
$scope.lockAddress = true;
|
||||
}
|
||||
|
||||
if (amount) {
|
||||
form.amount.$setViewValue("" + amount);
|
||||
form.amount.$isValid = true;
|
||||
form.amount.$render();
|
||||
$scope.lockAmount = true;
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
form.comment.$setViewValue(comment);
|
||||
form.comment.$isValid = true;
|
||||
form.comment.$render();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.resetForm = function() {
|
||||
var form = $scope.sendForm;
|
||||
|
||||
$scope.fetchingURL = null;
|
||||
$scope._merchantData = $scope._domain = null;
|
||||
|
||||
$scope.lockAddress = false;
|
||||
$scope.lockAmount = false;
|
||||
|
||||
$scope._amount = $scope._address = null;
|
||||
|
||||
form.amount.$pristine = true;
|
||||
form.amount.$setViewValue('');
|
||||
form.amount.$render();
|
||||
|
||||
form.comment.$setViewValue('');
|
||||
form.comment.$render();
|
||||
form.$setPristine();
|
||||
|
||||
if (form.address) {
|
||||
form.address.$pristine = true;
|
||||
form.address.$setViewValue('');
|
||||
form.address.$render();
|
||||
}
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
var $oscope = $scope;
|
||||
$scope.openPPModal = function(merchantData) {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
var w = $rootScope.wallet;
|
||||
var satToUnit = 1 / w.settings.unitToSatoshi;
|
||||
$scope.md = merchantData;
|
||||
$scope.alternative = $oscope._alternative;
|
||||
$scope.alternativeIsoCode = $oscope.alternativeIsoCode;
|
||||
$scope.isRateAvailable = $oscope.isRateAvailable;
|
||||
$scope.unitTotal = (merchantData.total * satToUnit).toFixed(w.settings.unitDecimals);
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/paypro.html',
|
||||
windowClass: 'medium',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.setFromPayPro = function(uri) {
|
||||
|
||||
var isChromeApp = window.chrome && chrome.runtime && chrome.runtime.id;
|
||||
if (isChromeApp) {
|
||||
$scope.error = 'Payment Protocol not yet supported on ChromeApp';
|
||||
return;
|
||||
}
|
||||
|
||||
var w = $rootScope.wallet;
|
||||
var satToUnit = 1 / w.settings.unitToSatoshi;
|
||||
$scope.fetchingURL = uri;
|
||||
$scope.loading = true;
|
||||
|
||||
|
||||
// Payment Protocol URI (BIP-72)
|
||||
w.fetchPaymentRequest({
|
||||
url: uri
|
||||
}, function(err, merchantData) {
|
||||
$scope.loading = false;
|
||||
$scope.fetchingURL = null;
|
||||
|
||||
if (err) {
|
||||
copay.logger.warn(err);
|
||||
$scope.resetForm();
|
||||
var msg = err.toString();
|
||||
if (msg.match('HTTP')) {
|
||||
msg = 'Could not fetch payment information';
|
||||
}
|
||||
$scope.error = msg;
|
||||
} else {
|
||||
$scope._merchantData = merchantData;
|
||||
$scope._domain = merchantData.domain;
|
||||
$scope.setForm(null, (merchantData.total * satToUnit).toFixed(w.settings.unitDecimals));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setFromUri = function(uri) {
|
||||
function sanitizeUri(uri) {
|
||||
// Fixes when a region uses comma to separate decimals
|
||||
var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
|
||||
var match = regex.exec(uri);
|
||||
if (!match || match.length === 0) {
|
||||
return uri;
|
||||
}
|
||||
var value = match[0].replace(',', '.');
|
||||
var newUri = uri.replace(regex, value);
|
||||
return newUri;
|
||||
};
|
||||
|
||||
var w = $rootScope.wallet;
|
||||
var satToUnit = 1 / w.settings.unitToSatoshi;
|
||||
var form = $scope.sendForm;
|
||||
|
||||
uri = sanitizeUri(uri);
|
||||
|
||||
var parsed = new bitcore.BIP21(uri);
|
||||
if (!parsed.isValid() || !parsed.address.isValid()) {
|
||||
$scope.error = 'Invalid bitcoin URL';
|
||||
form.address.$isValid = false;
|
||||
return uri;
|
||||
};
|
||||
|
||||
var addr = parsed.address.toString();
|
||||
if (parsed.data.merchant)
|
||||
return $scope.setFromPayPro(parsed.data.merchant);
|
||||
|
||||
var amount = (parsed.data && parsed.data.amount) ?
|
||||
((parsed.data.amount * 100000000).toFixed(0) * satToUnit).toFixed(w.settings.unitDecimals) : 0;
|
||||
|
||||
$scope.setForm(addr, amount, parsed.data.message, true);
|
||||
return addr;
|
||||
};
|
||||
|
||||
$scope.onAddressChange = function(value) {
|
||||
$scope.error = $scope.success = null;
|
||||
if (!value) return '';
|
||||
|
||||
if (value.indexOf('bitcoin:') === 0) {
|
||||
return $scope.setFromUri(value);
|
||||
} else if (/^https?:\/\//.test(value)) {
|
||||
return $scope.setFromPayPro(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
$scope.openAddressBook = function() {
|
||||
var w = $rootScope.wallet;
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/address-book.html',
|
||||
windowClass: 'large',
|
||||
controller: function($scope, $modalInstance) {
|
||||
|
||||
$scope.showForm = null;
|
||||
$scope.addressBook = w.addressBook;
|
||||
|
||||
$scope.hasEntry = function() {
|
||||
return _.keys($scope.addressBook).length > 0 ? true : false;
|
||||
};
|
||||
|
||||
$scope.toggleAddressBookEntry = function(key) {
|
||||
w.toggleAddressBookEntry(key);
|
||||
};
|
||||
|
||||
$scope.copyToSend = function(addr) {
|
||||
$modalInstance.close(addr);
|
||||
};
|
||||
|
||||
$scope.cancel = function(form) {
|
||||
$scope.error = $scope.success = $scope.newaddress = $scope.newlabel = null;
|
||||
clearForm(form);
|
||||
$scope.toggleForm();
|
||||
};
|
||||
|
||||
$scope.toggleForm = function() {
|
||||
$scope.showForm = !$scope.showForm;
|
||||
};
|
||||
|
||||
var clearForm = function(form) {
|
||||
form.newaddress.$pristine = true;
|
||||
form.newaddress.$setViewValue('');
|
||||
form.newaddress.$render();
|
||||
|
||||
form.newlabel.$pristine = true;
|
||||
form.newlabel.$setViewValue('');
|
||||
form.newlabel.$render();
|
||||
form.$setPristine();
|
||||
};
|
||||
|
||||
// TODO change to modal
|
||||
$scope.submitAddressBook = function(form) {
|
||||
if (form.$invalid) {
|
||||
return;
|
||||
}
|
||||
$scope.loading = true;
|
||||
$timeout(function() {
|
||||
var errorMsg;
|
||||
var entry = {
|
||||
"address": form.newaddress.$modelValue,
|
||||
"label": form.newlabel.$modelValue
|
||||
};
|
||||
try {
|
||||
w.setAddressBook(entry.address, entry.label);
|
||||
} catch (e) {
|
||||
copay.logger.warn(e);
|
||||
errorMsg = e.message;
|
||||
}
|
||||
|
||||
if (errorMsg) {
|
||||
$scope.error = errorMsg;
|
||||
} else {
|
||||
clearForm(form);
|
||||
$scope.toggleForm();
|
||||
notification.success('Entry created', 'New addressbook entry created')
|
||||
}
|
||||
$scope.loading = false;
|
||||
$rootScope.$digest();
|
||||
}, 100);
|
||||
return;
|
||||
};
|
||||
|
||||
$scope.close = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
modalInstance.result.then(function(addr) {
|
||||
$scope.setForm(addr);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -1,86 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('SettingsController', function($scope, $rootScope, $window, $route, $location, notification, configService) {
|
||||
$scope.title = 'Settings';
|
||||
$scope.insightLivenet = config.network.livenet.url;
|
||||
$scope.insightTestnet = config.network.testnet.url;
|
||||
$scope.defaultLogLevel = config.logLevel || 'log';
|
||||
|
||||
var logLevels = copay.logger.getLevels();
|
||||
|
||||
$scope.availableLogLevels = [];
|
||||
|
||||
for (var key in logLevels) {
|
||||
$scope.availableLogLevels.push({
|
||||
'name': key
|
||||
});
|
||||
}
|
||||
|
||||
$scope.availableStorages = [{
|
||||
name: 'In the cloud (Insight server)',
|
||||
pluginName: 'EncryptedInsightStorage',
|
||||
}, {
|
||||
name: 'On this device (localstorage)',
|
||||
pluginName: 'EncryptedLocalStorage',
|
||||
},
|
||||
// {
|
||||
// name: 'GoogleDrive',
|
||||
// pluginName: 'GoogleDrive',
|
||||
// }
|
||||
];
|
||||
|
||||
_.each($scope.availableStorages, function(v) {
|
||||
if (config.plugins[v.pluginName])
|
||||
$scope.selectedStorage = v;
|
||||
});
|
||||
|
||||
for (var ii in $scope.availableLogLevels) {
|
||||
if ($scope.defaultLogLevel === $scope.availableLogLevels[ii].name) {
|
||||
$scope.selectedLogLevel = $scope.availableLogLevels[ii];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.save = function() {
|
||||
$scope.insightLivenet = copay.Insight.setCompleteUrl($scope.insightLivenet);
|
||||
$scope.insightTestnet = copay.Insight.setCompleteUrl($scope.insightTestnet);
|
||||
|
||||
var insightSettings = {
|
||||
livenet: {
|
||||
url: $scope.insightLivenet,
|
||||
transports: ['polling'],
|
||||
},
|
||||
testnet: {
|
||||
url: $scope.insightTestnet,
|
||||
transports: ['polling'],
|
||||
},
|
||||
}
|
||||
|
||||
var plugins = {};
|
||||
plugins[$scope.selectedStorage.pluginName] = true;
|
||||
|
||||
configService.set({
|
||||
network: insightSettings,
|
||||
plugins: plugins,
|
||||
logLevel: $scope.selectedLogLevel.name,
|
||||
EncryptedInsightStorage: _.extend(config.EncryptedInsightStorage, {
|
||||
url: insightSettings.livenet.url + '/api/email'
|
||||
}),
|
||||
rates: _.extend(config.rates, {
|
||||
url: insightSettings.livenet.url + '/api/rates'
|
||||
}),
|
||||
},
|
||||
function() {
|
||||
notification.success('Settings saved',"Settings were saved");
|
||||
$location.path('/');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
configService.reset(function() {
|
||||
notification.success('Settings reseted',"Settings were reseted");
|
||||
$location.path('/');
|
||||
});
|
||||
};
|
||||
|
||||
});
|
|
@ -1,121 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('SidebarController', function($scope, $rootScope, $location, $timeout, identityService, isMobile, isCordova, go) {
|
||||
|
||||
$scope.isMobile = isMobile.any();
|
||||
$scope.isCordova = isCordova;
|
||||
$scope.username = $rootScope.iden ? $rootScope.iden.getName() : '';
|
||||
|
||||
$scope.menu = [{
|
||||
'title': 'Home',
|
||||
'icon': 'icon-home',
|
||||
'link': 'homeWallet'
|
||||
}, {
|
||||
'title': 'Receive',
|
||||
'icon': 'icon-receive',
|
||||
'link': 'receive'
|
||||
}, {
|
||||
'title': 'Send',
|
||||
'icon': 'icon-paperplane',
|
||||
'link': 'send'
|
||||
}, {
|
||||
'title': 'History',
|
||||
'icon': 'icon-history',
|
||||
'link': 'history'
|
||||
}];
|
||||
|
||||
$scope.signout = function() {
|
||||
identityService.signout();
|
||||
};
|
||||
|
||||
$scope.isActive = function(item) {
|
||||
return item.link && item.link == $location.path().split('/')[1];
|
||||
};
|
||||
|
||||
$scope.switchWallet = function(wid) {
|
||||
$scope.walletSelection = false;
|
||||
identityService.setFocusedWallet(wid);
|
||||
go.walletHome();
|
||||
};
|
||||
|
||||
$scope.toggleWalletSelection = function() {
|
||||
$scope.walletSelection = !$scope.walletSelection;
|
||||
if (!$scope.walletSelection) return;
|
||||
$scope.setWallets();
|
||||
};
|
||||
|
||||
$scope.openScanner = function() {
|
||||
window.ignoreMobilePause = true;
|
||||
cordova.plugins.barcodeScanner.scan(
|
||||
function onSuccess(result) {
|
||||
$timeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
if (result.cancelled) return;
|
||||
|
||||
$timeout(function() {
|
||||
var data = result.text;
|
||||
$scope.$apply(function() {
|
||||
$rootScope.$emit('dataScanned', data);
|
||||
});
|
||||
}, 1000);
|
||||
},
|
||||
function onError(error) {
|
||||
$timeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
alert('Scanning error');
|
||||
}
|
||||
);
|
||||
go.send();
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
// This should be called only once.
|
||||
|
||||
// focused wallet change
|
||||
if ($rootScope.wallet) {
|
||||
$rootScope.$watch('wallet', function() {
|
||||
$scope.walletSelection = false;
|
||||
$scope.setWallets();
|
||||
});
|
||||
}
|
||||
|
||||
// wallet list change
|
||||
if ($rootScope.iden) {
|
||||
var iden = $rootScope.iden;
|
||||
iden.on('newWallet', function() {
|
||||
$scope.walletSelection = false;
|
||||
$scope.setWallets();
|
||||
});
|
||||
iden.on('walletDeleted', function(wid) {
|
||||
if (wid == $rootScope.wallet.id) {
|
||||
copay.logger.debug('Deleted focused wallet:', wid);
|
||||
|
||||
// new focus
|
||||
var newWid = $rootScope.iden.getLastFocusedWalletId();
|
||||
if (newWid && $rootScope.iden.getWalletById(newWid)) {
|
||||
identityService.setFocusedWallet(newWid);
|
||||
} else {
|
||||
copay.logger.debug('No wallets');
|
||||
identityService.noFocusedWallet(newWid);
|
||||
}
|
||||
}
|
||||
$scope.walletSelection = false;
|
||||
$scope.setWallets();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setWallets = function() {
|
||||
if (!$rootScope.iden) return;
|
||||
var ret = _.filter($rootScope.iden.getWallets(), function(w) {
|
||||
return w;
|
||||
});
|
||||
$scope.wallets = _.sortBy(ret, 'name');
|
||||
};
|
||||
|
||||
$scope.openMenu = function() {
|
||||
go.swipe(true);
|
||||
};
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('UnsupportedController',
|
||||
function($scope, $location) {
|
||||
if (localStorage && localStorage.length > 0) {
|
||||
$location.path('/');
|
||||
}
|
||||
}
|
||||
);
|
|
@ -1,34 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('VersionController',
|
||||
function($scope, $rootScope, $http, $filter, notification) {
|
||||
|
||||
var w = $rootScope.wallet;
|
||||
|
||||
$scope.version = copay.version;
|
||||
$scope.commitHash = copay.commitHash;
|
||||
$scope.networkName = w ? w.getNetworkName() : '';
|
||||
if (_.isUndefined($rootScope.checkVersion))
|
||||
$rootScope.checkVersion = true;
|
||||
|
||||
if ($rootScope.checkVersion) {
|
||||
$rootScope.checkVersion = false;
|
||||
$http.get('https://api.github.com/repos/bitpay/copay/tags').success(function(data) {
|
||||
var toInt = function(s) {
|
||||
return parseInt(s);
|
||||
};
|
||||
var latestVersion = data[0].name.replace('v', '').split('.').map(toInt);
|
||||
var currentVersion = copay.version.split('.').map(toInt);
|
||||
var title = 'Copay ' + data[0].name + ' ' + $filter('translate')('available.');
|
||||
var content;
|
||||
if (currentVersion[0] < latestVersion[0]) {
|
||||
content = 'It\'s important that you update your wallet at https://copay.io';
|
||||
notification.version(title, content, true);
|
||||
} else if (currentVersion[0] == latestVersion[0] && currentVersion[1] < latestVersion[1]) {
|
||||
var content = 'Please update your wallet at https://copay.io';
|
||||
notification.version(title, content, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
|
@ -1,432 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var bitcore = require('bitcore');
|
||||
var log = require('../util/log');
|
||||
var AuthMessage = bitcore.AuthMessage;
|
||||
var util = bitcore.util;
|
||||
var nodeUtil = require('util');
|
||||
var extend = nodeUtil._extend;
|
||||
var io = require('socket.io-client');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
function Network(opts) {
|
||||
preconditions.checkArgument(opts);
|
||||
preconditions.checkArgument(opts.url);
|
||||
opts = opts || {};
|
||||
this.maxPeers = opts.maxPeers || 12;
|
||||
this.url = opts.url;
|
||||
this.secretNumber = opts.secretNumber;
|
||||
this.cleanUp();
|
||||
|
||||
this.socketOptions = {
|
||||
reconnection: true,
|
||||
'force new connection': true,
|
||||
'secure': this.url.indexOf('https') === 0,
|
||||
};
|
||||
|
||||
if (opts.transports) {
|
||||
this.socketOptions['transports'] = opts.transports;
|
||||
}
|
||||
}
|
||||
|
||||
nodeUtil.inherits(Network, EventEmitter);
|
||||
|
||||
Network.prototype.cleanUp = function() {
|
||||
this.started = false;
|
||||
this.connectedPeers = [];
|
||||
this.peerId = null;
|
||||
this.privkey = null;
|
||||
this.key = null;
|
||||
this.copayerId = null;
|
||||
this.allowedCopayerIds = null;
|
||||
this.isInboundPeerAuth = [];
|
||||
this.copayerForPeer = {};
|
||||
this.criticalErr = '';
|
||||
if (this.socket) {
|
||||
log.info('Async DISCONNECT');
|
||||
this.socket.disconnect();
|
||||
this.socket.removeAllListeners();
|
||||
this.socket = null;
|
||||
}
|
||||
this.removeAllListeners();
|
||||
};
|
||||
|
||||
Network.parent = EventEmitter;
|
||||
|
||||
// Array helpers
|
||||
Network._inArray = function(el, array) {
|
||||
return array.indexOf(el) > -1;
|
||||
};
|
||||
|
||||
Network._arrayPushOnce = function(el, array) {
|
||||
var ret = false;
|
||||
if (!Network._inArray(el, array)) {
|
||||
array.push(el);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
Network._arrayRemove = function(el, array) {
|
||||
var pos = array.indexOf(el);
|
||||
if (pos >= 0) array.splice(pos, 1);
|
||||
return array;
|
||||
};
|
||||
|
||||
Network.prototype.connectedCopayers = function() {
|
||||
var ret = [];
|
||||
for (var i in this.connectedPeers) {
|
||||
var copayerId = this.copayerForPeer[this.connectedPeers[i]];
|
||||
if (copayerId) ret.push(copayerId);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
Network.prototype._sendHello = function(copayerId, secretNumber) {
|
||||
|
||||
this.send(copayerId, {
|
||||
type: 'hello',
|
||||
copayerId: this.copayerId,
|
||||
secretNumber: secretNumber
|
||||
});
|
||||
};
|
||||
|
||||
Network.prototype._sendRejectConnection = function(copayerId) {
|
||||
|
||||
this.send(copayerId, {
|
||||
type: 'rejectConnection',
|
||||
copayerId: this.copayerId,
|
||||
});
|
||||
};
|
||||
|
||||
Network.prototype._deletePeer = function(peerId) {
|
||||
delete this.isInboundPeerAuth[peerId];
|
||||
delete this.copayerForPeer[peerId];
|
||||
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
|
||||
};
|
||||
|
||||
Network.prototype._addCopayer = function(copayerId) {
|
||||
var peerId = this.peerFromCopayer(copayerId);
|
||||
this._addCopayerMap(peerId, copayerId);
|
||||
Network._arrayPushOnce(peerId, this.connectedPeers);
|
||||
};
|
||||
|
||||
Network.prototype._addConnectedCopayer = function(copayerId) {
|
||||
this._addCopayer(copayerId);
|
||||
this.emit('connect', copayerId);
|
||||
};
|
||||
|
||||
Network.prototype.getKey = function() {
|
||||
preconditions.checkState(this.privkey || this.key);
|
||||
if (!this.key) {
|
||||
var key = new bitcore.Key();
|
||||
key.private = new Buffer(this.privkey, 'hex');
|
||||
key.regenerateSync();
|
||||
this.key = key;
|
||||
}
|
||||
return this.key;
|
||||
};
|
||||
|
||||
//hex version of one's own nonce
|
||||
Network.prototype.setHexNonce = function(networkNonce) {
|
||||
if (networkNonce) {
|
||||
if (networkNonce.length !== 16)
|
||||
throw new Error('incorrect length of hex nonce');
|
||||
this.networkNonce = new Buffer(networkNonce, 'hex');
|
||||
} else
|
||||
this.iterateNonce();
|
||||
};
|
||||
|
||||
//hex version of copayers' nonces
|
||||
Network.prototype.setHexNonces = function(networkNonces) {
|
||||
for (var i in networkNonces) {
|
||||
if (!this.networkNonces)
|
||||
this.networkNonces = {};
|
||||
if (networkNonces[i].length === 16)
|
||||
this.networkNonces[i] = new Buffer(networkNonces[i], 'hex');
|
||||
}
|
||||
};
|
||||
|
||||
//for oneself
|
||||
Network.prototype.getHexNonce = function() {
|
||||
return this.networkNonce.toString('hex');
|
||||
};
|
||||
|
||||
//for copayers
|
||||
Network.prototype.getHexNonces = function() {
|
||||
var networkNoncesHex = [];
|
||||
for (var i in this.networkNonces) {
|
||||
networkNoncesHex[i] = this.networkNonces[i].toString('hex');
|
||||
}
|
||||
return networkNoncesHex;
|
||||
};
|
||||
|
||||
Network.prototype.iterateNonce = function() {
|
||||
if (!this.networkNonce || this.networkNonce.length !== 8) {
|
||||
this.networkNonce = new Buffer(8);
|
||||
this.networkNonce.fill(0);
|
||||
}
|
||||
//the first 4 bytes of a nonce is a unix timestamp in seconds
|
||||
//the second 4 bytes is just an iterated "sub" nonce
|
||||
//the whole thing is interpreted as one big endian number
|
||||
var noncep1 = this.networkNonce.slice(0, 4);
|
||||
noncep1.writeUInt32BE(Math.floor(Date.now() / 1000), 0);
|
||||
var noncep2uint = this.networkNonce.slice(4, 8).readUInt32BE(0);
|
||||
var noncep2 = this.networkNonce.slice(4, 8);
|
||||
noncep2.writeUInt32BE(noncep2uint + 1, 0);
|
||||
this.networkNonce = Buffer.concat([noncep1, noncep2], 8);
|
||||
return this.networkNonce;
|
||||
};
|
||||
|
||||
Network.prototype.decode = function(enc) {
|
||||
var sender = enc.pubkey;
|
||||
var key = this.getKey();
|
||||
var prevnonce = this.networkNonces ? this.networkNonces[sender] : undefined;
|
||||
var opts = {
|
||||
prevnonce: prevnonce
|
||||
};
|
||||
var decoded = AuthMessage.decode(key, enc, opts);
|
||||
|
||||
//if no error thrown in the last step, we can set the copayer's nonce
|
||||
if (!this.networkNonces)
|
||||
this.networkNonces = {};
|
||||
this.networkNonces[sender] = decoded.nonce;
|
||||
|
||||
var payload = decoded.payload;
|
||||
return payload;
|
||||
};
|
||||
|
||||
Network.prototype._onMessage = function(enc) {
|
||||
var sender = enc.pubkey;
|
||||
try {
|
||||
var payload = this.decode(enc);
|
||||
} catch (e) {
|
||||
this._deletePeer(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ignoreMessageFromTs && this.ignoreMessageFromTs === enc.ts) {
|
||||
log.debug('Ignoring trailing message. Ts:', enc.ts);
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
switch (payload.type) {
|
||||
case 'hello':
|
||||
if (typeof payload.secretNumber === 'undefined' || payload.secretNumber !== this.secretNumber) {
|
||||
this._sendRejectConnection(sender);
|
||||
this._deletePeer(enc.pubkey, 'incorrect secret number');
|
||||
return;
|
||||
}
|
||||
// if we locked allowed copayers, check if it belongs
|
||||
if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) {
|
||||
this._sendRejectConnection(sender);
|
||||
this._deletePeer(sender);
|
||||
return;
|
||||
}
|
||||
//ensure claimed public key is actually the public key of the peer
|
||||
//e.g., their public key should hash to be their peerId
|
||||
if (sender !== payload.copayerId) {
|
||||
this._sendRejectConnection(sender);
|
||||
this._deletePeer(enc.pubkey, 'incorrect pubkey for peerId');
|
||||
return;
|
||||
}
|
||||
this._addConnectedCopayer(payload.copayerId);
|
||||
break;
|
||||
default:
|
||||
this.emit('data', sender, payload, enc.ts);
|
||||
}
|
||||
};
|
||||
|
||||
Network.prototype._setupSocketHandlers = function(opts, cb) {
|
||||
preconditions.checkState(this.socket);
|
||||
log.debug('setting up connection', opts);
|
||||
var self = this;
|
||||
|
||||
self.socket.on('connect_error', function(m) {
|
||||
|
||||
// If socket is not started, destroy it and emit and error
|
||||
// If it is started, socket.io will try to reconnect.
|
||||
if (!self.started) {
|
||||
self.emit('connect_error');
|
||||
self.cleanUp();
|
||||
}
|
||||
});
|
||||
|
||||
self.socket.on('subscribed', function(m) {
|
||||
var fromTs = opts.syncedTimestamp || 0;
|
||||
|
||||
// We ask for this message, and then ignore it, only to see if the
|
||||
// server has erased our old messages.
|
||||
|
||||
if (fromTs) {
|
||||
self.ignoreMessageFromTs = fromTs;
|
||||
}
|
||||
log.info('Async: synchronizing from: ', fromTs);
|
||||
self.socket.emit('sync', fromTs);
|
||||
self.started = true;
|
||||
});
|
||||
|
||||
|
||||
self.socket.on('message', function(m) {
|
||||
// delay execution, to improve error handling
|
||||
setTimeout(function() {
|
||||
self._onMessage(m);
|
||||
}, 1);
|
||||
});
|
||||
self.socket.on('error', self._onError.bind(self));
|
||||
self.socket.on('no_messages', self.emit.bind(self, 'no_messages'));
|
||||
self.socket.on('no messages', self.emit.bind(self, 'no_messages'));
|
||||
self.socket.on('connect', function() {
|
||||
var pubkey = self.getKey().public.toString('hex');
|
||||
log.debug('Async subscribing to pubkey:', pubkey);
|
||||
|
||||
self.socket.emit('subscribe', pubkey);
|
||||
|
||||
self.socket.on('disconnect', function() {
|
||||
self.socket.emit('subscribe', pubkey);
|
||||
});
|
||||
if (typeof cb === 'function') cb();
|
||||
});
|
||||
};
|
||||
|
||||
Network.prototype._onError = function(err) {
|
||||
log.debug('RECV ERROR: ', err);
|
||||
log.debug(err.stack);
|
||||
this.criticalError = err.message;
|
||||
};
|
||||
|
||||
Network.prototype.greet = function(copayerId, secretNumber) {
|
||||
this._sendHello(copayerId, secretNumber);
|
||||
var peerId = this.peerFromCopayer(copayerId);
|
||||
this._addCopayerMap(peerId, copayerId);
|
||||
};
|
||||
|
||||
Network.prototype._addCopayerMap = function(peerId, copayerId) {
|
||||
if (!this.copayerForPeer[peerId]) {
|
||||
if (Object.keys(this.copayerForPeer).length < this.maxPeers) {
|
||||
this.copayerForPeer[peerId] = copayerId;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Network.prototype._setInboundPeerAuth = function(peerId) {
|
||||
this.isInboundPeerAuth[peerId] = true;
|
||||
};
|
||||
|
||||
Network.prototype.setCopayerId = function(copayerId) {
|
||||
preconditions.checkState(!this.started, 'network already started: can not change peerId');
|
||||
|
||||
this.copayerId = copayerId;
|
||||
this.copayerIdBuf = new Buffer(copayerId, 'hex');
|
||||
this.peerId = this.peerFromCopayer(this.copayerId);
|
||||
this._addCopayerMap(this.peerId, copayerId);
|
||||
};
|
||||
|
||||
|
||||
// TODO cache this.
|
||||
Network.prototype.peerFromCopayer = function(hex) {
|
||||
var SIN = bitcore.SIN;
|
||||
return new SIN(new Buffer(hex, 'hex')).toString();
|
||||
};
|
||||
|
||||
Network.prototype.start = function(opts, openCallback) {
|
||||
preconditions.checkArgument(opts);
|
||||
preconditions.checkArgument(opts.privkey);
|
||||
preconditions.checkArgument(opts.copayerId);
|
||||
|
||||
if (this.started) {
|
||||
log.debug('Async: Networing already started for this wallet.')
|
||||
return openCallback();
|
||||
}
|
||||
|
||||
this.privkey = opts.privkey;
|
||||
this.setCopayerId(opts.copayerId);
|
||||
this.maxPeers = opts.maxPeers || this.maxPeers;
|
||||
|
||||
this.socket = this.createSocket();
|
||||
this._setupSocketHandlers(opts, openCallback);
|
||||
};
|
||||
|
||||
Network.prototype.createSocket = function() {
|
||||
log.debug('Async: Connecting to socket:', this.url);
|
||||
return io.connect(this.url, this.socketOptions);
|
||||
};
|
||||
|
||||
Network.prototype.getOnlinePeerIDs = function() {
|
||||
return this.connectedPeers;
|
||||
};
|
||||
|
||||
|
||||
Network.prototype.getCopayerIds = function() {
|
||||
if (this.allowedCopayerIds) {
|
||||
return Object.keys(this.allowedCopayerIds);
|
||||
} else {
|
||||
var copayerIds = [];
|
||||
for (var peerId in this.copayerForPeer) {
|
||||
copayerIds.push(this.copayerForPeer[peerId]);
|
||||
}
|
||||
return copayerIds;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Network.prototype.send = function(dest, payload, cb) {
|
||||
preconditions.checkState(this.socket);
|
||||
preconditions.checkArgument(payload);
|
||||
|
||||
var self = this;
|
||||
if (!dest) {
|
||||
dest = this.getCopayerIds();
|
||||
payload.isBroadcast = 1;
|
||||
}
|
||||
|
||||
if (typeof dest === 'string')
|
||||
dest = [dest];
|
||||
|
||||
var l = dest.length;
|
||||
var i = 0;
|
||||
|
||||
for (var ii in dest) {
|
||||
var to = dest[ii];
|
||||
if (to == this.copayerId)
|
||||
continue;
|
||||
|
||||
var message = this.encode(to, payload);
|
||||
this.socket.emit('message', message);
|
||||
}
|
||||
|
||||
if (typeof cb === 'function') cb();
|
||||
};
|
||||
|
||||
|
||||
Network.prototype.encode = function(copayerId, payload, nonce) {
|
||||
this.iterateNonce();
|
||||
var opts = {
|
||||
nonce: nonce || this.networkNonce
|
||||
};
|
||||
var copayerIdBuf = new Buffer(copayerId, 'hex');
|
||||
var message = AuthMessage.encode(copayerIdBuf, this.getKey(), payload, opts);
|
||||
return message;
|
||||
};
|
||||
|
||||
Network.prototype.isOnline = function() {
|
||||
return !!this.socket;
|
||||
};
|
||||
|
||||
|
||||
Network.prototype.lockIncommingConnections = function(allowedCopayerIdsArray) {
|
||||
this.allowedCopayerIds = {};
|
||||
for (var i in allowedCopayerIdsArray) {
|
||||
this.allowedCopayerIds[allowedCopayerIdsArray[i]] = true;
|
||||
}
|
||||
};
|
||||
|
||||
Network.prototype.setCopayers = function(copayersIdsArray) {
|
||||
for (var i in copayersIdsArray) {
|
||||
this._addCopayer(copayersIdsArray[i]);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Network;
|
|
@ -1,256 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Identity = require('./Identity');
|
||||
var Wallet = require('./Wallet');
|
||||
var cryptoUtils = require('../util/crypto');
|
||||
var CryptoJS = require('node-cryptojs-aes').CryptoJS;
|
||||
var sjcl = require('../../lib/sjcl');
|
||||
var log = require('../util/log');
|
||||
var preconditions = require('preconditions').instance();
|
||||
var _ = require('lodash');
|
||||
|
||||
var Compatibility = {};
|
||||
Compatibility.iterations = 100;
|
||||
Compatibility.salt = 'mjuBtGybi/4=';
|
||||
|
||||
/**
|
||||
* Reads from localstorage wallets saved previously to 0.8
|
||||
*/
|
||||
Compatibility._getWalletIds = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
var walletIds = [];
|
||||
var uniq = {};
|
||||
var key;
|
||||
for (key in localStorage) {
|
||||
var split = key.split('::');
|
||||
if (split.length == 2) {
|
||||
var walletId = split[0];
|
||||
|
||||
if (!walletId || walletId === 'nameFor' || walletId === 'lock' || walletId === 'wallet') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof uniq[walletId] === 'undefined') {
|
||||
walletIds.push(walletId);
|
||||
uniq[walletId] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cb(walletIds);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} encryptedWallet - base64-encoded encrypted wallet
|
||||
* @param {string} password
|
||||
* @returns {Object}
|
||||
*/
|
||||
Compatibility.importLegacy = function(encryptedWallet, password) {
|
||||
var passphrase = this.kdf(password);
|
||||
var ret = Compatibility._decrypt(encryptedWallet, passphrase);
|
||||
|
||||
if (!ret) return null;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypts using the CryptoJS library (unknown encryption schema)
|
||||
*
|
||||
* Don't use CryptoJS to encrypt. This still exists for compatibility reasons only.
|
||||
*/
|
||||
Compatibility._decrypt = function(base64, passphrase) {
|
||||
var decryptedStr = null;
|
||||
try {
|
||||
var decrypted = CryptoJS.AES.decrypt(base64, passphrase);
|
||||
if (decrypted)
|
||||
decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
|
||||
} catch (e) {
|
||||
// Error while decrypting
|
||||
return null;
|
||||
}
|
||||
return decryptedStr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads an item from localstorage, decrypts it with passphrase
|
||||
*/
|
||||
Compatibility._read = function(k, passphrase, cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
|
||||
var ret = localStorage.getItem(k);
|
||||
if (!ret) return cb(null);
|
||||
var ret = self._decrypt(ret, passphrase);
|
||||
if (!ret) return cb(null);
|
||||
|
||||
ret = ret.toString(CryptoJS.enc.Utf8);
|
||||
ret = JSON.parse(ret);
|
||||
return ret;
|
||||
};
|
||||
|
||||
Compatibility.getWallets_Old = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
|
||||
var wallets = [];
|
||||
var self = this;
|
||||
|
||||
this._getWalletIds(function(ids) {
|
||||
if (!ids.length) {
|
||||
return cb([]);
|
||||
}
|
||||
|
||||
_.each(ids, function(id) {
|
||||
var name = localStorage.getItem('nameFor::' + id);
|
||||
if (name) {
|
||||
wallets.push({
|
||||
id: id,
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
});
|
||||
return cb(wallets);
|
||||
});
|
||||
};
|
||||
|
||||
Compatibility.getWallets2 = function(cb) {
|
||||
var self = this;
|
||||
var re = /wallet::([^_]+)(_?(.*))/;
|
||||
var va = /^{+/;
|
||||
|
||||
var key;
|
||||
var keys = [];
|
||||
for (key in localStorage) {
|
||||
keys.push(key);
|
||||
}
|
||||
var wallets = _.compact(_.map(keys, function(key) {
|
||||
if (key.indexOf('wallet::') !== 0)
|
||||
return null;
|
||||
var match = key.match(re);
|
||||
var matchValue = localStorage[key].match(va);
|
||||
if (match.length != 4)
|
||||
return null;
|
||||
if (matchValue)
|
||||
return null;
|
||||
return {
|
||||
id: match[1],
|
||||
name: match[3] ? match[3] : undefined,
|
||||
value: localStorage[key]
|
||||
};
|
||||
}));
|
||||
|
||||
return cb(wallets);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lists all wallets in localstorage
|
||||
*/
|
||||
Compatibility.listWalletsPre8 = function(cb) {
|
||||
var self = this;
|
||||
self.getWallets2(function(wallets) {
|
||||
self.getWallets_Old(function(wallets2) {
|
||||
var ids = _.pluck(wallets, 'id');
|
||||
_.each(wallets2, function(w) {
|
||||
if (!_.contains(ids, w.id))
|
||||
wallets.push(w);
|
||||
});
|
||||
return cb(wallets);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves a wallet that predates the 0.8 release
|
||||
*/
|
||||
Compatibility.readWalletPre8 = function(walletId, password, cb) {
|
||||
var self = this;
|
||||
var passphrase = cryptoUtils.kdf(password);
|
||||
var obj = {};
|
||||
var key;
|
||||
|
||||
for (key in localStorage) {
|
||||
if (key.indexOf('wallet::' + walletId) !== -1) {
|
||||
var ret = self._read(localStorage.getItem(key), passphrase);
|
||||
if (err) return cb(err);
|
||||
|
||||
_.each(Wallet.PERSISTED_PROPERTIES, function(p) {
|
||||
obj[p] = ret[p];
|
||||
});
|
||||
|
||||
if (!_.any(_.values(obj)))
|
||||
return cb(new Error('Wallet not found'));
|
||||
|
||||
var w, err;
|
||||
obj.id = walletId;
|
||||
try {
|
||||
w = self.fromObj(obj);
|
||||
} catch (e) {
|
||||
if (e && e.message && e.message.indexOf('MISSOPTS')) {
|
||||
err = new Error('Could not read: ' + walletId);
|
||||
} else {
|
||||
err = e;
|
||||
}
|
||||
w = null;
|
||||
}
|
||||
return cb(err, w);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Compatibility.importEncryptedWallet = function(identity, cypherText, password, opts, cb) {
|
||||
var crypto = (opts && opts.cryptoUtil) || cryptoUtils;
|
||||
|
||||
var obj = crypto.decrypt(password, cypherText);
|
||||
if (!obj) {
|
||||
// 0.7.3 broken KDF
|
||||
log.debug('Trying legacy encryption 0.7.2...');
|
||||
var passphrase = crypto.kdf(password, 'mjuBtGybi/4=', 100);
|
||||
obj = crypto.decrypt(passphrase, cypherText);
|
||||
}
|
||||
|
||||
if (!obj) {
|
||||
log.info("Could not decrypt, trying legacy..");
|
||||
obj = Compatibility.importLegacy(cypherText, password);
|
||||
};
|
||||
|
||||
if (!obj) {
|
||||
return cb('Could not decrypt', null);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
obj = JSON.parse(obj);
|
||||
} catch (e) {
|
||||
return cb('Could not read encrypted wallet', null);
|
||||
}
|
||||
return identity.importWalletFromObj(obj, opts, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Generate a WordArray expanding a password
|
||||
*
|
||||
* @param {string} password - the password to expand
|
||||
* @returns WordArray 512 bits with the expanded key generated from password
|
||||
*/
|
||||
Compatibility.kdf = function(password) {
|
||||
var hash = sjcl.hash.sha256.hash(sjcl.hash.sha256.hash(password));
|
||||
var salt = sjcl.codec.base64.toBits(this.salt);
|
||||
|
||||
var crypto2 = function(key, salt, iterations, length, alg) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.misc.pbkdf2(key, salt, iterations, length * 8,
|
||||
alg == 'sha1' ? function(key) {
|
||||
return new sjcl.misc.hmac(key, sjcl.hash.sha1)
|
||||
} : null
|
||||
))
|
||||
};
|
||||
|
||||
var key512 = crypto2(hash, salt, this.iterations, 64, 'sha1');
|
||||
var sbase64 = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(key512));
|
||||
return sbase64;
|
||||
};
|
||||
|
||||
Compatibility.deleteOldWallet = function(walletObj) {
|
||||
console.log('[Compatibility.js:249]',walletObj); //TODO
|
||||
localStorage.removeItem('wallet::' + walletObj.id + '_' + walletObj.name);
|
||||
log.info('Old wallet ' + walletObj.name + ' deleted: ' + walletObj.id);
|
||||
};
|
||||
|
||||
|
||||
module.exports = Compatibility;
|
|
@ -1,274 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// 83.8% typed (by google's closure-compiler account)
|
||||
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var HDPath = require('./HDPath');
|
||||
var _ = require('lodash');
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* HDParams is a class that encapsulates information about the current indexes
|
||||
* of a copayer
|
||||
*
|
||||
* When a copayer creates a new wallet, his receiveIndex gets updated, and an
|
||||
* address is generated from everybody's public key, using this BIP32 path:
|
||||
* <pre> m/copay'/{copayer}/0/{index} </pre>
|
||||
*
|
||||
* When a copayer generates a transaction proposal, his changeIndex gets
|
||||
* updated, and all funds from that transaction proposal go to the multisig
|
||||
* address generated from this BIP32 path for all the copayers:
|
||||
* <pre> m/copay'/{copayer}/1/{changeIndex} </pre>
|
||||
*
|
||||
* There's a shared index, <tt>HDPath.SHARED_INDEX</tt>, that serves to
|
||||
* generate addresses common to everybody.
|
||||
*
|
||||
* @TODO: Should opts.cosigner go?
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {Object} opts - options for the construction of this object
|
||||
* @param {number=} opts.cosigner - backwards compatible index of a copayer
|
||||
* @param {number} opts.copayerIndex - the copayer that generated this branch
|
||||
* of addresses
|
||||
* @param {number} opts.receiveIndex - the current index for a the last receive
|
||||
* address generated for this copayer
|
||||
* @param {number} opts.changeIndex - the current index for a the last change
|
||||
* address generated for this copayer
|
||||
*/
|
||||
function HDParams(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
//opts.cosigner is for backwards compatibility only
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @type number
|
||||
*/
|
||||
this.copayerIndex = _.isUndefined(opts.copayerIndex) ? opts.cosigner : opts.copayerIndex;
|
||||
/**
|
||||
* @public
|
||||
* @type number
|
||||
*/
|
||||
this.changeIndex = opts.changeIndex || 0;
|
||||
/**
|
||||
* @public
|
||||
* @type number
|
||||
*/
|
||||
this.receiveIndex = opts.receiveIndex || 0;
|
||||
|
||||
if (_.isUndefined(this.copayerIndex)) {
|
||||
this.copayerIndex = HDPath.SHARED_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Creates a set of HDParams, with one HDParams structure for each copayer and
|
||||
* a shared path.
|
||||
*
|
||||
* @static
|
||||
* @param {number} totalCopayers - the number of copayers in a wallet
|
||||
* @returns {HDParams[]} a list of HDParams generated for a new empty wallet
|
||||
*/
|
||||
HDParams.init = function(totalCopayers) {
|
||||
preconditions.shouldBeNumber(totalCopayers);
|
||||
|
||||
var ret = [new HDParams({receiveIndex: 1})];
|
||||
for (var i = 0 ; i < totalCopayers ; i++) {
|
||||
ret.push(new HDParams({copayerIndex: i}));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Generates a set of HDParams from a list with a object-serialized version of
|
||||
* HDParams.
|
||||
*
|
||||
* @static
|
||||
* @param {Object[]} hdParams - a list with objects
|
||||
* @returns {HDParams[]} builds a HDParams for each object literal found in the list
|
||||
*/
|
||||
HDParams.fromList = function(hdParams) {
|
||||
return hdParams.map(function(i) { return HDParams.fromObj(i); });
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Generate a HDParams from an object.
|
||||
*
|
||||
* This essentialy only calls new HDParams(data), but it's the interface being
|
||||
* used everywhere to encode/decode.
|
||||
*
|
||||
* @TODO: This should be clarified - Or abstracted away - as it's a pattern
|
||||
* used in multiple places.
|
||||
*
|
||||
* @static
|
||||
* @param {Object} data - a serialized version of HDParams
|
||||
* @return {HDParams}
|
||||
* @throws {BADDATA} - when the parameter <tt>data</tt> already is an instance of
|
||||
* HDParams.
|
||||
*/
|
||||
HDParams.fromObj = function(data) {
|
||||
if (data instanceof HDParams) {
|
||||
throw new Error('BADDATA', 'bad data format: Did you use .toObj()?');
|
||||
}
|
||||
return new HDParams(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Serializes a list of HDParams to a list of "plain" objects, according to each
|
||||
* element's <tt>toObj()</tt> method.
|
||||
*
|
||||
* @TODO: There should be a list of Classes that share this behaviour.
|
||||
*
|
||||
* @static
|
||||
* @param {HDParams[]} hdParams - a list of HDParams objects.
|
||||
* @returns {Array} an array with the <tt>toObj()</tt> serialization of each
|
||||
* element in hdParams
|
||||
*/
|
||||
HDParams.serialize = function(hdParams) {
|
||||
return hdParams.map(function(i) { return i.toObj(); });
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Creates a new HDParams set with <tt>totalCopayers+1</tt> elements.
|
||||
*
|
||||
* Sets the first (corresponding to the parameters for the shared addresses)
|
||||
* HDParams object to match <tt>shared</tt>'s values. Returns a serialized
|
||||
* version of this set
|
||||
*
|
||||
* <pre>
|
||||
* var updateResult = HDParams.update({changeIndex: 1, receiveIndex: 2}, 5);
|
||||
* // All the following asserts succeed
|
||||
* assert(_.isArray(updateResult));
|
||||
* assert(_.all(updateResult, function (hd) { return !(hd instanceOf HDParams); }));
|
||||
* assert(_.size(updateResult) === 6);
|
||||
* assert(updateResult[0].changeIndex === 1);
|
||||
* assert(updateResult[0].receiveIndex === 2);
|
||||
* </pre>
|
||||
*
|
||||
* @TODO: This method is badly coded, it does something that is very specific
|
||||
* and kind of strange. I couldn't figure out why would it be needed.
|
||||
*
|
||||
* @static
|
||||
* @param {HDParams} shared - an instance of HDParams
|
||||
* @param {number} totalCopayers
|
||||
* @return {Object[]}
|
||||
*/
|
||||
HDParams.update = function(shared, totalCopayers) {
|
||||
var hdParams = this.init(totalCopayers);
|
||||
hdParams[0].changeIndex = shared.changeIndex;
|
||||
hdParams[0].receiveIndex = shared.receiveIndex;
|
||||
return this.serialize(hdParams);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Serializes this object
|
||||
*
|
||||
* @TODO: I couldn't realize why would this be needed - calling, for example,
|
||||
* JSON.stringify would have the same result on this object than on the
|
||||
* original instance
|
||||
*
|
||||
* @returns {Object} a serialized version that should be equal (in a deep object
|
||||
* comparison) to the <tt>this</tt> instance if passed to the
|
||||
* <tt>HDParams()</tt> constructor.
|
||||
*/
|
||||
HDParams.prototype.toObj = function() {
|
||||
return {
|
||||
copayerIndex: this.copayerIndex,
|
||||
changeIndex: this.changeIndex,
|
||||
receiveIndex: this.receiveIndex
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Throws an error if a given index falls out of the range for known addresses.
|
||||
*
|
||||
* @TODO: This is not a good pattern, exceptions should be for exceptional things.
|
||||
*
|
||||
* @param {number} index the index to check for
|
||||
* @param {boolean} isChange whether to check for the change index or the
|
||||
* receive address index
|
||||
*/
|
||||
HDParams.prototype.checkRange = function(index, isChange) {
|
||||
if ((isChange && index > this.changeIndex) ||
|
||||
(!isChange && index > this.receiveIndex)) {
|
||||
throw new Error('Out of bounds at index ' + index + ' isChange: ' + isChange);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Return this instance's changeIndex value
|
||||
*
|
||||
* @TODO: Somebody did a lot of java. Not sure if we need to be so verbose. If
|
||||
* anything, let's just declare changeIndex as a read only variable
|
||||
* @returns {number} this HDParams current index to generate a change address
|
||||
*/
|
||||
HDParams.prototype.getChangeIndex = function() {
|
||||
return this.changeIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Return this instance's receiveIndex value
|
||||
*
|
||||
* @TODO: Somebody did a lot of java. Not sure if we need to be so verbose. If
|
||||
* anything, let's just declare changeIndex as a read only variable
|
||||
* @returns {number} this HDParams current index to generate a receive address
|
||||
*/
|
||||
HDParams.prototype.getReceiveIndex = function() {
|
||||
return this.receiveIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Increment this instance's changeIndex or receiveIndex value
|
||||
*
|
||||
* @TODO: Somebody did a lot of java. Not sure if we need to be so verbose.
|
||||
*
|
||||
* @param {boolean} isChange - if true, change <tt>changeIndex</tt>
|
||||
*/
|
||||
HDParams.prototype.increment = function(isChange) {
|
||||
if (isChange) {
|
||||
this.changeIndex++;
|
||||
} else {
|
||||
this.receiveIndex++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Merge this instance with another HDParams instance.
|
||||
*
|
||||
* @TODO: Device a general approach to merges.
|
||||
*
|
||||
* @param {Object} inHDParams - the object to merge to
|
||||
* @param {number} inHDParams.copayerIndex - the object to merge to
|
||||
* @returns {boolean} true if this object has changed
|
||||
*/
|
||||
HDParams.prototype.merge = function(inHDParams) {
|
||||
preconditions.shouldBeObject(inHDParams);
|
||||
preconditions.checkArgument(this.copayerIndex == inHDParams.copayerIndex);
|
||||
|
||||
var hasChanged = false;
|
||||
|
||||
if (inHDParams.changeIndex > this.changeIndex) {
|
||||
this.changeIndex = inHDParams.changeIndex;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if (inHDParams.receiveIndex > this.receiveIndex) {
|
||||
this.receiveIndex = inHDParams.receiveIndex;
|
||||
hasChanged = true;
|
||||
}
|
||||
return hasChanged;
|
||||
};
|
||||
|
||||
module.exports = HDParams;
|
|
@ -1,116 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// 90.2% typed (by google's closure-compiler account)
|
||||
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var _ = require('lodash');
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @desc
|
||||
* HDPath contains helper functions to handle BIP32 branches as
|
||||
* Copay uses them.
|
||||
* Based on https://github.com/maraoz/bips/blob/master/bip-NNNN.mediawiki
|
||||
* <pre>
|
||||
* m / purpose' / copayerIndex / change:boolean / addressIndex
|
||||
* </pre>
|
||||
*/
|
||||
var HDPath = {};
|
||||
|
||||
/**
|
||||
* @desc Copay's BIP45 purpose code
|
||||
* @const
|
||||
* @type number
|
||||
*/
|
||||
HDPath.PURPOSE = 45;
|
||||
|
||||
/**
|
||||
* @desc Maximum number for non-hardened values (BIP32)
|
||||
* @const
|
||||
* @type number
|
||||
*/
|
||||
HDPath.MAX_NON_HARDENED = 0x80000000 - 1;
|
||||
|
||||
/**
|
||||
* @desc Shared Index: used for creating addresses for no particular purpose
|
||||
* @const
|
||||
* @type number
|
||||
*/
|
||||
HDPath.SHARED_INDEX = HDPath.MAX_NON_HARDENED - 0;
|
||||
|
||||
/**
|
||||
* @desc ???
|
||||
* @const
|
||||
* @type number
|
||||
*/
|
||||
HDPath.ID_INDEX = HDPath.MAX_NON_HARDENED - 1;
|
||||
|
||||
/**
|
||||
* @desc BIP45 prefix for COPAY
|
||||
* @const
|
||||
* @type string
|
||||
*/
|
||||
HDPath.BIP45_PUBLIC_PREFIX = 'm/' + HDPath.PURPOSE + '\'';
|
||||
|
||||
/**
|
||||
* @desc Retrieve a string to be used with bitcore representing a Copay branch
|
||||
* @param {number} addressIndex - the last value of the HD derivation
|
||||
* @param {boolean} isChange - whether this is a change address or a receive
|
||||
* @param {number} copayerIndex - the index of the copayer in the pubkeyring
|
||||
* @return {string} - the path for the HD derivation
|
||||
*/
|
||||
HDPath.Branch = function(addressIndex, isChange, copayerIndex) {
|
||||
preconditions.checkArgument(_.isNumber(addressIndex));
|
||||
preconditions.checkArgument(_.isBoolean(isChange));
|
||||
|
||||
var ret = 'm/' +
|
||||
(typeof copayerIndex !== 'undefined' ? copayerIndex : HDPath.SHARED_INDEX) + '/' +
|
||||
(isChange ? 1 : 0) + '/' +
|
||||
addressIndex;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc ???
|
||||
* @param {number} addressIndex - the last value of the HD derivation
|
||||
* @param {boolean} isChange - whether this is a change address or a receive
|
||||
* @param {number} copayerIndex - the index of the copayer in the pubkeyring
|
||||
* @return {string} - the path for the HD derivation
|
||||
*/
|
||||
HDPath.FullBranch = function(addressIndex, isChange, copayerIndex) {
|
||||
preconditions.checkArgument(_.isNumber(addressIndex));
|
||||
preconditions.checkArgument(_.isBoolean(isChange));
|
||||
|
||||
var sub = HDPath.Branch(addressIndex, isChange, copayerIndex);
|
||||
sub = sub.substring(2);
|
||||
return HDPath.BIP45_PUBLIC_PREFIX + '/' + sub;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Decompose a string and retrieve its arguments as if it where a Copay address.
|
||||
* @param {string} path - the HD path
|
||||
* @returns {Object} an object with three keys: addressIndex, isChange, and
|
||||
* copayerIndex
|
||||
*/
|
||||
HDPath.indexesForPath = function(path) {
|
||||
preconditions.checkArgument(_.isString(path));
|
||||
|
||||
var s = path.split('/');
|
||||
return {
|
||||
isChange: s[3] === '1',
|
||||
addressIndex: parseInt(s[4], 10),
|
||||
copayerIndex: parseInt(s[2], 10)
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc The ID for a shared branch
|
||||
*/
|
||||
HDPath.IdFullBranch = HDPath.FullBranch(0, false, HDPath.ID_INDEX);
|
||||
/**
|
||||
* @desc Partial ID for a shared branch
|
||||
*/
|
||||
HDPath.IdBranch = HDPath.Branch(0, false, HDPath.ID_INDEX);
|
||||
|
||||
module.exports = HDPath;
|
|
@ -1,905 +0,0 @@
|
|||
'use strict';
|
||||
var _ = require('lodash');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var inherits = require('inherits');
|
||||
var events = require('events');
|
||||
var async = require('async');
|
||||
|
||||
var bitcore = require('bitcore');
|
||||
|
||||
var TxProposals = require('./TxProposals');
|
||||
var PublicKeyRing = require('./PublicKeyRing');
|
||||
var PrivateKey = require('./PrivateKey');
|
||||
var Wallet = require('./Wallet');
|
||||
var PluginManager = require('./PluginManager');
|
||||
var Async = require('./Async');
|
||||
var cryptoUtil = require('../util/crypto');
|
||||
var log = require('../util/log');
|
||||
var version = require('../../version').version;
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Identity - stores the state for a wallet in creation
|
||||
*
|
||||
* @param {Object} opts - configuration for this wallet
|
||||
* @param {string} opts.fullName
|
||||
* @param {string} opts.email
|
||||
* @param {string} opts.password
|
||||
* @param {string} opts.storage
|
||||
* @param {string} opts.pluginManager
|
||||
* @param {Object} opts.walletDefaults
|
||||
* @param {string} opts.version
|
||||
* @param {Object} opts.wallets
|
||||
* @param {Object} opts.network
|
||||
* @param {string} opts.network.testnet
|
||||
* @param {string} opts.network.livenet
|
||||
* @constructor
|
||||
*/
|
||||
function Identity(opts) {
|
||||
preconditions.checkArgument(opts);
|
||||
|
||||
opts = _.extend({}, opts);
|
||||
this.networkOpts = {
|
||||
'livenet': opts.network.livenet,
|
||||
'testnet': opts.network.testnet,
|
||||
};
|
||||
this.blockchainOpts = {
|
||||
'livenet': opts.network.livenet,
|
||||
'testnet': opts.network.testnet,
|
||||
};
|
||||
|
||||
this.fullName = opts.fullName || opts.email;
|
||||
this.email = opts.email;
|
||||
this.password = opts.password;
|
||||
|
||||
this.storage = opts.storage || opts.pluginManager.get('DB');
|
||||
this.storage.setCredentials(this.email, this.password, {});
|
||||
|
||||
this.walletDefaults = opts.walletDefaults || {};
|
||||
this.version = opts.version || version;
|
||||
|
||||
this.walletIds = opts.walletIds || [];
|
||||
this.wallets = opts.wallets || {};
|
||||
this.focusedTimestamps = opts.focusedTimestamps || {};
|
||||
this.backupNeeded = opts.backupNeeded || false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inherits(Identity, events.EventEmitter);
|
||||
|
||||
Identity.getStoragePrefix = function() {
|
||||
return 'profile::';
|
||||
};
|
||||
|
||||
Identity.getKeyForEmail = function(email) {
|
||||
return Identity.getStoragePrefix() + bitcore.util.sha256ripe160(email).toString('hex');
|
||||
};
|
||||
|
||||
Identity.prototype.getChecksumForStorage = function() {
|
||||
return JSON.stringify(_.sortBy(this.walletIds));
|
||||
};
|
||||
|
||||
Identity.prototype.getId = function() {
|
||||
return Identity.getKeyForEmail(this.email);
|
||||
};
|
||||
|
||||
Identity.prototype.getName = function() {
|
||||
return this.fullName || this.email;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an Identity
|
||||
*
|
||||
* @param opts
|
||||
* @param cb
|
||||
* @return {undefined}
|
||||
*/
|
||||
Identity.create = function(opts, cb) {
|
||||
opts = _.extend({
|
||||
backupNeeded: true
|
||||
}, opts);
|
||||
|
||||
var iden = new Identity(opts);
|
||||
iden.store(_.extend(opts, {
|
||||
failIfExists: true
|
||||
}), function(err) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, iden);
|
||||
});
|
||||
};
|
||||
|
||||
Identity.prototype.resendVerificationEmail = function(cb) {
|
||||
var self = this;
|
||||
|
||||
preconditions.checkArgument(_.isFunction(cb));
|
||||
preconditions.checkState(_.isFunction(self.storage.resendVerificationEmail));
|
||||
|
||||
self.storage.resendVerificationEmail(cb);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Open an Identity from the given storage.
|
||||
*
|
||||
* After opening a profile, and setting its wallet event handlers,
|
||||
* the client must run .netStart on each
|
||||
* (probably on iden's newWallet handler
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @param {Object} opts.storage
|
||||
* @param {string} opts.email
|
||||
* @param {string} opts.password
|
||||
* @param {Function} cb
|
||||
*/
|
||||
Identity.open = function(opts, cb) {
|
||||
preconditions.checkArgument(_.isObject(opts));
|
||||
preconditions.checkArgument(_.isFunction(cb));
|
||||
|
||||
var storage = opts.storage || opts.pluginManager.get('DB');
|
||||
storage.setCredentials(opts.email, opts.password, opts);
|
||||
storage.getItem(Identity.getKeyForEmail(opts.email), function(err, data, headers) {
|
||||
var exported;
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
try {
|
||||
exported = JSON.parse(data);
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
return cb(null, new Identity(_.extend(opts, exported)), headers);
|
||||
});
|
||||
};
|
||||
|
||||
Identity.prototype.verifyChecksum = function(cb) {
|
||||
var self = this;
|
||||
|
||||
self.storage.getItem(Identity.getKeyForEmail(self.email), function(err, data, headers) {
|
||||
var iden;
|
||||
if (err) return cb(err);
|
||||
try {
|
||||
iden = JSON.parse(data);
|
||||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
return cb(null, self.getChecksumForStorage() == self.getChecksumForStorage.call(iden));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} walletId
|
||||
* @returns {Wallet}
|
||||
*/
|
||||
Identity.prototype.getWalletById = function(walletId) {
|
||||
return this.wallets[walletId];
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {Wallet[]}
|
||||
*/
|
||||
Identity.prototype.getWallets = function() {
|
||||
return _.values(this.wallets);
|
||||
};
|
||||
|
||||
/**
|
||||
* addWallet
|
||||
*
|
||||
* @param w
|
||||
*/
|
||||
Identity.prototype.addWallet = function(w) {
|
||||
this.wallets[w.getId()] = w;
|
||||
this.walletIds = _.union(this.walletIds, [w.getId()]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Deletes a wallet. This involves removing it from the storage instance
|
||||
*
|
||||
* @param {string} walletId
|
||||
* @callback cb
|
||||
* @return {err}
|
||||
*/
|
||||
Identity.prototype.deleteWallet = function(walletId, cb) {
|
||||
preconditions.checkArgument(_.isString(walletId));
|
||||
var self = this;
|
||||
|
||||
self.verifyChecksum(function(err, match) {
|
||||
if (err) return cb(err);
|
||||
if (!match) return cb('The profile is out of sync. Please re-login to get the latest changes.');
|
||||
|
||||
var w = self.getWalletById(walletId);
|
||||
w.close();
|
||||
|
||||
delete self.wallets[walletId];
|
||||
delete self.focusedTimestamps[walletId];
|
||||
self.walletIds = _.without(self.walletIds, walletId);
|
||||
|
||||
self.storage.removeItem(Wallet.getStorageKey(walletId), function(err) {
|
||||
if (err) return cb(err);
|
||||
self.emitAndKeepAlive('walletDeleted', walletId);
|
||||
if (!self.walletIds.length) {
|
||||
self.emitAndKeepAlive('noWallets')
|
||||
}
|
||||
self.store({
|
||||
noWallets: true
|
||||
}, cb);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* readAndBindWallet
|
||||
*
|
||||
* @param {string} wid walletId to be readed
|
||||
* @param {function} cb
|
||||
*
|
||||
*/
|
||||
Identity.prototype.readAndBindWallet = function(walletId, cb) {
|
||||
var self = this;
|
||||
self.retrieveWalletFromStorage(walletId, {}, function(error, wallet) {
|
||||
if (!error) {
|
||||
self.addWallet(wallet);
|
||||
self.bindWallet(wallet);
|
||||
}
|
||||
return cb(error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Identity.prototype.emitAndKeepAlive = function(args) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
log.debug('Ident Emitting:', args);
|
||||
//this.keepAlive(); // TODO
|
||||
this.emit.apply(this, arguments);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc open profile's wallets. Call it AFTER setting
|
||||
* the proper even listeners. no callback.
|
||||
*
|
||||
*/
|
||||
Identity.prototype.openWallets = function() {
|
||||
var self = this;
|
||||
|
||||
|
||||
if (_.isEmpty(self.walletIds)) {
|
||||
self.emitAndKeepAlive('noWallets')
|
||||
return;
|
||||
}
|
||||
|
||||
// First read the lastFocused wallet
|
||||
self.walletIds.sort(function(a, b) {
|
||||
var va = self.focusedTimestamps[a] || 0;
|
||||
var vb = self.focusedTimestamps[b] || 0;
|
||||
|
||||
return va < vb ? 1 : (va === vb ? 0 : -1);
|
||||
});
|
||||
|
||||
// opens the wallets, in the order they were last accessed. Emits open events (newWallet)
|
||||
async.eachSeries(self.walletIds, function(walletId, a_cb) {
|
||||
self.readAndBindWallet(walletId, a_cb);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} walletId
|
||||
* @param {} opts
|
||||
* opts.importWallet
|
||||
* @param {Function} cb
|
||||
*/
|
||||
Identity.prototype.retrieveWalletFromStorage = function(walletId, opts, cb) {
|
||||
var self = this;
|
||||
|
||||
var importFunction = opts.importWallet || Wallet.fromUntrustedObj;
|
||||
|
||||
this.storage.getItem(Wallet.getStorageKey(walletId), function(error, walletData) {
|
||||
if (error) {
|
||||
return cb(error);
|
||||
}
|
||||
try {
|
||||
log.info('## OPENING Wallet:', walletId);
|
||||
if (_.isString(walletData)) {
|
||||
walletData = JSON.parse(walletData);
|
||||
}
|
||||
var readOpts = {
|
||||
networkOpts: self.networkOpts,
|
||||
blockchainOpts: self.blockchainOpts,
|
||||
skipFields: []
|
||||
};
|
||||
} catch (e) {
|
||||
log.debug("ERROR: ", e.message);
|
||||
if (e && e.message && e.message.indexOf('MISSOPTS') !== -1) {
|
||||
return cb(new Error('WERROR: Could not read: ' + walletId + ': ' + e.message));
|
||||
} else {
|
||||
return cb(e);
|
||||
}
|
||||
}
|
||||
return cb(null, importFunction(walletData, readOpts));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Wallet} wallet
|
||||
* @param {Function} cb
|
||||
*/
|
||||
Identity.prototype.storeWallet = function(wallet, cb) {
|
||||
var self = this;
|
||||
|
||||
preconditions.checkArgument(wallet && _.isObject(wallet));
|
||||
|
||||
wallet.setVersion(this.version);
|
||||
var val = wallet.toObj();
|
||||
var key = wallet.getStorageKey();
|
||||
log.debug('Storing wallet:' + wallet.getName());
|
||||
|
||||
this.storage.setItem(key, val, function(err) {
|
||||
if (err) {
|
||||
log.error('Wallet:' + wallet.getName() + ' could not be stored:', err);
|
||||
log.error('Wallet:' + wallet.getName() + ' Size:', JSON.stringify(wallet.sizes()));
|
||||
|
||||
if (err.match('OVERQUOTA')) {
|
||||
self.emitAndKeepAlive('walletStorageError', wallet.getId(), 'Storage limits on remote server exceeded');
|
||||
} else {
|
||||
self.emitAndKeepAlive('walletStorageError', wallet.getId(), err);
|
||||
}
|
||||
}
|
||||
if (cb)
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Identity} identity
|
||||
* @param {Wallet} wallet
|
||||
* @param {Function} cb
|
||||
*/
|
||||
Identity.storeWalletDebounced = _.debounce(function(identity, wallet, cb) {
|
||||
identity.storeWallet(wallet, cb);
|
||||
}, 3000);
|
||||
|
||||
|
||||
|
||||
Identity.prototype.toObj = function() {
|
||||
return _.pick(this, 'walletIds', 'version', 'fullName', 'password', 'email', 'backupNeeded', 'focusedTimestamps');
|
||||
};
|
||||
|
||||
Identity.prototype.exportEncryptedWithWalletInfo = function(opts) {
|
||||
var crypto = opts.cryptoUtil || cryptoUtil;
|
||||
|
||||
return crypto.encrypt(this.password, this.exportWithWalletInfo(opts));
|
||||
};
|
||||
|
||||
Identity.prototype.setBackupNeeded = function(backupNeeded) {
|
||||
var self = this;
|
||||
|
||||
self.backupNeeded = !!backupNeeded;
|
||||
|
||||
self.verifyChecksum(function(err, match) {
|
||||
if (err) {
|
||||
log.error(err);
|
||||
return;
|
||||
}
|
||||
if (!match) {
|
||||
log.error('The profile is out of sync. Please re-login to get the latest changes.');
|
||||
return;
|
||||
}
|
||||
|
||||
self.store({
|
||||
noWallets: true
|
||||
}, function() {});
|
||||
});
|
||||
}
|
||||
|
||||
Identity.prototype.exportWithWalletInfo = function(opts) {
|
||||
return _.extend({
|
||||
wallets: _.map(this.getWallets(), function(wallet) {
|
||||
return wallet.toObj();
|
||||
})
|
||||
},
|
||||
_.pick(this, 'version', 'fullName', 'password', 'email', 'backupNeeded')
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} opts
|
||||
* @param {Function} cb
|
||||
*/
|
||||
Identity.prototype.store = function(opts, cb) {
|
||||
var self = this;
|
||||
opts = opts || {};
|
||||
|
||||
var storeFunction = opts.failIfExists ? self.storage.createItem : self.storage.setItem;
|
||||
|
||||
storeFunction.call(self.storage, this.getId(), this.toObj(), function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (opts.noWallets) return cb();
|
||||
|
||||
async.each(self.getWallets(), function(wallet, in_cb) {
|
||||
self.storeWallet(wallet, in_cb);
|
||||
}, cb);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} opts
|
||||
* @param {Function} cb
|
||||
*/
|
||||
Identity.prototype.remove = function(opts, cb) {
|
||||
log.debug('Deleting profile');
|
||||
|
||||
var self = this;
|
||||
opts = opts || {};
|
||||
|
||||
async.each(self.getWallets(), function(w, cb) {
|
||||
w.close();
|
||||
self.storage.removeItem(Wallet.getStorageKey(w.getId()), function(err) {
|
||||
if (err) return cb(err);
|
||||
cb();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.storage.removeItem(self.getId(), function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.storage.clear(function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.emitAndKeepAlive('closed');
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Identity.prototype._cleanUp = function() {
|
||||
var self = this;
|
||||
|
||||
_.each(this.getWallets(), function(w) {
|
||||
w.close();
|
||||
delete self.wallets[w.getId()];
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Closes the wallet and disconnects all services
|
||||
*/
|
||||
Identity.prototype.close = function(cb) {
|
||||
var self = this;
|
||||
|
||||
function doClose() {
|
||||
self._cleanUp();
|
||||
self.emitAndKeepAlive('closed');
|
||||
if (cb) return cb();
|
||||
};
|
||||
|
||||
self.verifyChecksum(function(err, match) {
|
||||
if (!err && match) {
|
||||
self.store({
|
||||
noWallets: true,
|
||||
}, function(err) {
|
||||
return doClose();
|
||||
});
|
||||
} else {
|
||||
return doClose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Identity.prototype.importWalletFromObj = function(obj, opts, cb) {
|
||||
var self = this;
|
||||
preconditions.checkArgument(cb);
|
||||
|
||||
self.verifyChecksum(function(err, match) {
|
||||
if (err) return cb(err);
|
||||
if (!match) return cb('The profile is out of sync. Please re-login to get the latest changes.');
|
||||
|
||||
var importFunction = opts.importWallet || Wallet.fromUntrustedObj;
|
||||
|
||||
var readOpts = {
|
||||
networkOpts: self.networkOpts,
|
||||
blockchainOpts: self.blockchainOpts,
|
||||
skipFields: opts.skipFields,
|
||||
};
|
||||
|
||||
var w = importFunction(obj, readOpts);
|
||||
if (!w) return cb(new Error('Could not decrypt'));
|
||||
log.debug('Wallet decrypted:' + w.getName());
|
||||
|
||||
self._checkVersion(w.version);
|
||||
log.debug('Updating Indexes for wallet:' + w.getName());
|
||||
w.updateIndexes(function(err) {
|
||||
log.debug('Adding wallet to profile:' + w.getName());
|
||||
self.storeWallet(w, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.addWallet(w);
|
||||
self.updateFocusedTimestamp(w.getId());
|
||||
self.bindWallet(w);
|
||||
|
||||
self.backupNeeded = true;
|
||||
self.store({
|
||||
noWallets: true,
|
||||
}, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Identity.prototype.importMultipleWalletsFromObj = function(objs, opts) {
|
||||
var self = this;
|
||||
opts = opts || {};
|
||||
|
||||
async.eachSeries(objs, function(walletData, cb) {
|
||||
if (!walletData)
|
||||
return cb();
|
||||
self.importWalletFromObj(walletData, opts, cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Wallet} wallet
|
||||
* @param {Function} cb
|
||||
*/
|
||||
Identity.prototype.closeWallet = function(wallet, cb) {
|
||||
preconditions.checkState(wallet, 'Wallet not found');
|
||||
|
||||
var self = this;
|
||||
wallet.close(function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
Identity.importFromEncryptedFullJson = function(ejson, password, opts, cb) {
|
||||
var crypto = opts.cryptoUtil || cryptoUtil;
|
||||
|
||||
var str = crypto.decrypt(password, ejson);
|
||||
if (!str) {
|
||||
// 0.7.3 broken KDF
|
||||
log.debug('Trying legacy encryption...');
|
||||
var passphrase = crypto.kdf(password, 'mjuBtGybi/4=', 100);
|
||||
str = crypto.decrypt(passphrase, ejson);
|
||||
}
|
||||
|
||||
if (!str)
|
||||
return cb('BADSTR');
|
||||
|
||||
return Identity.importFromFullJson(str, password, opts, cb);
|
||||
};
|
||||
|
||||
Identity.importFromFullJson = function(str, password, opts, cb) {
|
||||
preconditions.checkArgument(str);
|
||||
var json;
|
||||
try {
|
||||
json = JSON.parse(str);
|
||||
} catch (e) {
|
||||
return cb('BADSTR: Unable to retrieve json from string', str);
|
||||
}
|
||||
|
||||
var email = json.email;
|
||||
|
||||
opts.email = email;
|
||||
opts.password = password;
|
||||
|
||||
if (!email)
|
||||
return cb('BADSTR');
|
||||
|
||||
var iden = new Identity(opts);
|
||||
|
||||
opts.failIfExists = true;
|
||||
|
||||
json.wallets = json.wallets || {};
|
||||
|
||||
iden.store(opts, function(err) {
|
||||
if (err) return cb(err); //profile already exists
|
||||
|
||||
return cb(null, iden, json.wallets);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc binds a wallet's events and emits 'newWallet'
|
||||
* @param {string} walletId Wallet id to be binded
|
||||
* @emits newWallet (walletId)
|
||||
*/
|
||||
Identity.prototype.bindWallet = function(w) {
|
||||
preconditions.checkArgument(w && this.getWalletById(w.getId()));
|
||||
|
||||
var self = this;
|
||||
log.debug('Binding wallet:' + w.getName());
|
||||
|
||||
w.on('txProposalsUpdated', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
w.on('paymentAck', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
w.on('newAddresses', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
w.on('settingsUpdated', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
w.on('txProposalEvent', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
w.on('addressBookUpdated', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
w.on('publicKeyRingUpdated', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
w.on('ready', function() {
|
||||
Identity.storeWalletDebounced(self, w);
|
||||
});
|
||||
|
||||
this.emitAndKeepAlive('newWallet', w.getId());
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc This method prepares options for a new Wallet
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.id
|
||||
* @param {PrivateKey=} opts.privateKey
|
||||
* @param {string=} opts.privateKeyHex
|
||||
* @param {number} opts.requiredCopayers
|
||||
* @param {number} opts.totalCopayers
|
||||
* @param {PublicKeyRing=} opts.publicKeyRing
|
||||
* @param {string} opts.nickname
|
||||
* @param {string} opts.password
|
||||
* @param {boolean} opts.spendUnconfirmed this.walletDefaults.spendUnconfirmed
|
||||
* @param {number} opts.reconnectDelay time in milliseconds
|
||||
* @param {number=} opts.version
|
||||
* @param {callback} opts.version
|
||||
* @return {Wallet}
|
||||
*/
|
||||
Identity.prototype.createWallet = function(opts, cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
|
||||
var self = this;
|
||||
|
||||
self.verifyChecksum(function(err, match) {
|
||||
if (err) return cb(err);
|
||||
if (!match) return cb('The profile is out of sync. Please re-login to get the latest changes.');
|
||||
|
||||
opts = opts || {};
|
||||
opts.networkName = opts.networkName || 'testnet';
|
||||
|
||||
log.debug('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey'));
|
||||
|
||||
var privOpts = {
|
||||
networkName: opts.networkName,
|
||||
};
|
||||
|
||||
if (opts.privateKeyHex && opts.privateKeyHex.length > 1) {
|
||||
privOpts.extendedPrivateKeyString = opts.privateKeyHex;
|
||||
}
|
||||
|
||||
opts.privateKey = opts.privateKey || new PrivateKey(privOpts);
|
||||
|
||||
var requiredCopayers = opts.requiredCopayers || self.walletDefaults.requiredCopayers;
|
||||
var totalCopayers = opts.totalCopayers || self.walletDefaults.totalCopayers;
|
||||
opts.lockTimeoutMin = self.walletDefaults.idleDurationMin;
|
||||
|
||||
opts.publicKeyRing = opts.publicKeyRing || new PublicKeyRing({
|
||||
networkName: opts.networkName,
|
||||
requiredCopayers: requiredCopayers,
|
||||
totalCopayers: totalCopayers,
|
||||
});
|
||||
opts.publicKeyRing.addCopayer(
|
||||
opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(),
|
||||
opts.nickname || self.getName()
|
||||
);
|
||||
log.debug('\t### PublicKeyRing Initialized');
|
||||
|
||||
opts.txProposals = opts.txProposals || new TxProposals({
|
||||
networkName: opts.networkName,
|
||||
});
|
||||
var walletClass = opts.walletClass || Wallet;
|
||||
|
||||
log.debug('\t### TxProposals Initialized');
|
||||
|
||||
|
||||
opts.networkOpts = self.networkOpts;
|
||||
opts.blockchainOpts = self.blockchainOpts;
|
||||
|
||||
opts.spendUnconfirmed = opts.spendUnconfirmed || self.walletDefaults.spendUnconfirmed;
|
||||
opts.reconnectDelay = opts.reconnectDelay || self.walletDefaults.reconnectDelay;
|
||||
opts.requiredCopayers = requiredCopayers;
|
||||
opts.totalCopayers = totalCopayers;
|
||||
opts.version = opts.version || self.version;
|
||||
|
||||
var w = new walletClass(opts);
|
||||
|
||||
if (self.getWalletById(w.getId())) {
|
||||
return cb('walletAlreadyExists');
|
||||
}
|
||||
|
||||
self.storeWallet(w, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.addWallet(w);
|
||||
self.updateFocusedTimestamp(w.getId());
|
||||
self.bindWallet(w);
|
||||
|
||||
self.backupNeeded = true;
|
||||
self.store({
|
||||
noWallets: true,
|
||||
}, function(err) {
|
||||
return cb(err, w);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Checks if a version is compatible with the current version
|
||||
* @param {string} inVersion - a version, with major, minor, and revision, period-separated (x.y.z)
|
||||
* @throws {Error} if there's a major version difference
|
||||
*/
|
||||
Identity.prototype._checkVersion = function(inVersion) {
|
||||
if (inVersion) {
|
||||
var thisV = this.version.split('.');
|
||||
var thisV0 = parseInt(thisV[0]);
|
||||
var inV = inVersion.split('.');
|
||||
var inV0 = parseInt(inV[0]);
|
||||
}
|
||||
|
||||
//We only check for major version differences
|
||||
if (thisV0 < inV0) {
|
||||
throw new Error('Major difference in software versions' +
|
||||
'. Received:' + inVersion +
|
||||
'. Current version:' + this.version +
|
||||
'. Aborting.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc Pass through to {@link Wallet#secret}
|
||||
*/
|
||||
Identity.prototype.decodeSecret = function(secret) {
|
||||
try {
|
||||
return Wallet.decodeSecret(secret);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getLastFocusedWalletId
|
||||
*
|
||||
* @return {string} walletId
|
||||
*/
|
||||
Identity.prototype.getLastFocusedWalletId = function() {
|
||||
if (this.walletIds.length == 0) return undefined;
|
||||
|
||||
var max = _.max(this.focusedTimestamps);
|
||||
|
||||
if (!max)
|
||||
return this.walletIds[0];
|
||||
|
||||
return _.findKey(this.focusedTimestamps, function(ts) {
|
||||
return ts == max;
|
||||
}) || this.walletIds[0];
|
||||
};
|
||||
|
||||
Identity.prototype.updateFocusedTimestamp = function(wid) {
|
||||
preconditions.checkArgument(wid && this.getWalletById(wid));
|
||||
this.focusedTimestamps[wid] = Date.now();
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback walletCreationCallback
|
||||
* @param {?} err - an error, if any, that happened during the wallet creation
|
||||
* @param {Wallet=} wallet - the wallet created
|
||||
*/
|
||||
|
||||
/**
|
||||
* @desc Start the network functionality.
|
||||
*
|
||||
* Start up the Network instance and try to join a wallet defined by the
|
||||
* parameter <tt>secret</tt> using the parameter <tt>nickname</tt>. Encode
|
||||
* information locally using <tt>passphrase</tt>. <tt>privateHex</tt> is the
|
||||
* private extended master key. <tt>cb</tt> has two params: error and wallet.
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {string} opts.secret - the wallet secret
|
||||
* @param {string} opts.nickname - a nickname for the current user
|
||||
* @param {string} opts.privateHex - the private extended master key
|
||||
* @param {walletCreationCallback} cb - a callback
|
||||
*/
|
||||
Identity.prototype.joinWallet = function(opts, cb) {
|
||||
preconditions.checkArgument(opts);
|
||||
preconditions.checkArgument(opts.secret);
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
var decodedSecret = this.decodeSecret(opts.secret);
|
||||
if (!decodedSecret || !decodedSecret.networkName || !decodedSecret.pubKey) {
|
||||
return cb('badSecret');
|
||||
}
|
||||
|
||||
var privOpts = {
|
||||
networkName: decodedSecret.networkName,
|
||||
};
|
||||
|
||||
if (opts.privateHex && opts.privateHex.length > 1) {
|
||||
privOpts.extendedPrivateKeyString = opts.privateHex;
|
||||
}
|
||||
|
||||
//Create our PrivateK
|
||||
var privateKey = new PrivateKey(privOpts);
|
||||
log.debug('\t### PrivateKey Initialized');
|
||||
var joinOpts = {
|
||||
copayerId: privateKey.getId(),
|
||||
privkey: privateKey.getIdPriv(),
|
||||
key: privateKey.getIdKey(),
|
||||
secretNumber: decodedSecret.secretNumber,
|
||||
};
|
||||
|
||||
|
||||
var joinNetwork = opts.Async || new Async(this.networkOpts[decodedSecret.networkName]);
|
||||
|
||||
// This is a hack to reconize if the connection was rejected or the peer wasn't there.
|
||||
var connectedOnce = false;
|
||||
joinNetwork.on('connected', function(sender, data) {
|
||||
connectedOnce = true;
|
||||
});
|
||||
|
||||
joinNetwork.on('connect_error', function() {
|
||||
return cb('connectionError');
|
||||
});
|
||||
|
||||
joinNetwork.on('serverError', function() {
|
||||
return cb('joinError');
|
||||
});
|
||||
|
||||
joinNetwork.start(joinOpts, function() {
|
||||
|
||||
joinNetwork.greet(decodedSecret.pubKey, joinOpts.secretNumber);
|
||||
joinNetwork.on('data', function(sender, data) {
|
||||
if (data.type === 'walletId' && data.opts) {
|
||||
if (!data.networkName || data.networkName !== decodedSecret.networkName) {
|
||||
return cb('badNetwork');
|
||||
}
|
||||
data.opts.networkName = data.networkName;
|
||||
|
||||
var walletOpts = _.clone(data.opts);
|
||||
walletOpts.id = data.walletId;
|
||||
walletOpts.network = joinNetwork;
|
||||
|
||||
walletOpts.privateKey = privateKey;
|
||||
walletOpts.nickname = opts.nickname || self.getName();
|
||||
|
||||
if (opts.password)
|
||||
walletOpts.password = opts.password;
|
||||
|
||||
self.createWallet(walletOpts, function(err, w) {
|
||||
if (w) {
|
||||
w.sendWalletReady(decodedSecret.pubKey);
|
||||
} else {
|
||||
if (!err) {
|
||||
err = 'walletFull';
|
||||
}
|
||||
}
|
||||
return cb(err, w);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports = Identity;
|
|
@ -1,346 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var async = require('async');
|
||||
var request = require('request');
|
||||
var io = require('socket.io-client');
|
||||
var _ = require('lodash');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
var bitcore = require('bitcore');
|
||||
|
||||
var log = require('../util/log.js');
|
||||
|
||||
|
||||
/*
|
||||
This class lets interface with the blockchain, making general queries and
|
||||
subscribing to transactions on addresses and blocks.
|
||||
|
||||
Opts:
|
||||
- url
|
||||
- reconnection (optional)
|
||||
- reconnectionDelay (optional)
|
||||
|
||||
Events:
|
||||
- tx: activity on subscribed address.
|
||||
- block: a new block that includes a subscribed address.
|
||||
- connect: the connection with the blockchain is ready.
|
||||
- disconnect: the connection with the blochckain is unavailable.
|
||||
*/
|
||||
|
||||
var Insight = function(opts) {
|
||||
preconditions.checkArgument(opts)
|
||||
.shouldBeObject(opts)
|
||||
.checkArgument(opts.url)
|
||||
|
||||
this.status = this.STATUS.DISCONNECTED;
|
||||
this.subscribed = {};
|
||||
this.listeningBlocks = false;
|
||||
|
||||
this.url = opts.url;
|
||||
this.opts = {
|
||||
'reconnection': opts.reconnection || true,
|
||||
'reconnectionDelay': opts.reconnectionDelay || 1000,
|
||||
'secure': opts.url.indexOf('https') === 0
|
||||
};
|
||||
|
||||
|
||||
if (opts.transports) {
|
||||
this.opts['transports'] = opts.transports;
|
||||
}
|
||||
|
||||
this.socket = this.getSocket();
|
||||
}
|
||||
|
||||
Insight.setCompleteUrl = function(uri) {
|
||||
|
||||
if (!uri) return uri;
|
||||
|
||||
var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
|
||||
|
||||
var parts = [
|
||||
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
|
||||
];
|
||||
|
||||
function parseuri(str) {
|
||||
var m = re.exec(str || ''),
|
||||
uri = {},
|
||||
i = 14;
|
||||
|
||||
while (i--) {
|
||||
uri[parts[i]] = m[i] || '';
|
||||
}
|
||||
|
||||
return uri;
|
||||
};
|
||||
|
||||
var opts_host;
|
||||
var opts_secure;
|
||||
var opts_port;
|
||||
var opts_protocol;
|
||||
if (uri) {
|
||||
uri = parseuri(uri);
|
||||
opts_host = uri.host;
|
||||
opts_protocol = uri.protocol;
|
||||
opts_secure = uri.protocol == 'https' || uri.protocol == 'wss';
|
||||
opts_port = uri.port;
|
||||
}
|
||||
|
||||
var this_secure = null != opts_secure ? opts_secure :
|
||||
('https:' == location.protocol);
|
||||
|
||||
var opts_hostname;
|
||||
if (opts_host) {
|
||||
var pieces = opts_host.split(':');
|
||||
opts_hostname = pieces.shift();
|
||||
if (pieces.length) opts_port = pieces.pop();
|
||||
}
|
||||
|
||||
var this_port = opts_port ||
|
||||
(this_secure ? 443 : 80);
|
||||
|
||||
var newUri = opts_protocol + '://' + opts_host + ':' + this_port;
|
||||
|
||||
return newUri;
|
||||
}
|
||||
|
||||
util.inherits(Insight, EventEmitter);
|
||||
|
||||
Insight.prototype.STATUS = {
|
||||
CONNECTED: 'connected',
|
||||
DISCONNECTED: 'disconnected',
|
||||
DESTROYED: 'destroyed'
|
||||
}
|
||||
|
||||
/** @private */
|
||||
Insight.prototype.subscribeToBlocks = function() {
|
||||
var socket = this.getSocket();
|
||||
if (this.listeningBlocks || !socket.connected) return;
|
||||
|
||||
var self = this;
|
||||
socket.on('block', function(blockHash) {
|
||||
self.emit('block', blockHash);
|
||||
});
|
||||
this.listeningBlocks = true;
|
||||
}
|
||||
|
||||
/** @private */
|
||||
Insight.prototype._getSocketIO = function(url, opts) {
|
||||
log.debug('Insight: Connecting to socket:', this.url);
|
||||
return io(this.url, this.opts);
|
||||
};
|
||||
|
||||
|
||||
Insight.prototype._setMainHandlers = function(url, opts) {
|
||||
// Emmit connection events
|
||||
var self = this;
|
||||
this.socket.on('connect', function() {
|
||||
self.status = self.STATUS.CONNECTED;
|
||||
self.subscribeToBlocks();
|
||||
self.emit('connect', 0);
|
||||
});
|
||||
|
||||
this.socket.on('connect_error', function() {
|
||||
if (self.status != self.STATUS.CONNECTED) return;
|
||||
self.status = self.STATUS.DISCONNECTED;
|
||||
self.emit('disconnect');
|
||||
});
|
||||
|
||||
this.socket.on('connect_timeout', function() {
|
||||
if (self.status != self.STATUS.CONNECTED) return;
|
||||
self.status = self.STATUS.DISCONNECTED;
|
||||
self.emit('disconnect');
|
||||
});
|
||||
|
||||
this.socket.on('reconnect', function(attempt) {
|
||||
if (self.status != self.STATUS.DISCONNECTED) return;
|
||||
self.emit('reconnect', attempt);
|
||||
self.reSubscribe();
|
||||
self.status = self.STATUS.CONNECTED;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @private */
|
||||
Insight.prototype.getSocket = function() {
|
||||
|
||||
if (!this.socket) {
|
||||
this.socket = this._getSocketIO(this.url, this.opts);
|
||||
this._setMainHandlers();
|
||||
}
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
/** @private */
|
||||
Insight.prototype.request = function(path, cb) {
|
||||
preconditions.checkArgument(path).shouldBeFunction(cb);
|
||||
request(this.url + path, cb);
|
||||
}
|
||||
|
||||
/** @private */
|
||||
Insight.prototype.requestPost = function(path, data, cb) {
|
||||
preconditions.checkArgument(path).checkArgument(data).shouldBeFunction(cb);
|
||||
request({
|
||||
method: "POST",
|
||||
url: this.url + path,
|
||||
json: data
|
||||
}, cb);
|
||||
}
|
||||
|
||||
Insight.prototype.destroy = function() {
|
||||
var socket = this.getSocket();
|
||||
this.socket.disconnect();
|
||||
this.socket.removeAllListeners();
|
||||
this.socket = null;
|
||||
this.subscribed = {};
|
||||
this.status = this.STATUS.DESTROYED;
|
||||
this.removeAllListeners();
|
||||
};
|
||||
|
||||
Insight.prototype.subscribe = function(addresses) {
|
||||
addresses = Array.isArray(addresses) ? addresses : [addresses];
|
||||
var self = this;
|
||||
|
||||
function handlerFor(self, address) {
|
||||
return function(txid) {
|
||||
// verify the address is still subscribed
|
||||
if (!self.subscribed[address]) return;
|
||||
|
||||
self.emit('tx', {
|
||||
address: address,
|
||||
txid: txid
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var s = self.getSocket();
|
||||
addresses.forEach(function(address) {
|
||||
preconditions.checkArgument(new bitcore.Address(address).isValid());
|
||||
|
||||
// skip already subscibed
|
||||
if (!self.subscribed[address]) {
|
||||
var handler = handlerFor(self, address);
|
||||
self.subscribed[address] = handler;
|
||||
// log.debug('Subscribe to: ', address);
|
||||
s.emit('subscribe', address);
|
||||
s.on(address, handler);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Insight.prototype.getSubscriptions = function(addresses) {
|
||||
return this.subscribed;
|
||||
}
|
||||
|
||||
|
||||
Insight.prototype.reSubscribe = function() {
|
||||
log.debug('insight reSubscribe');
|
||||
var allAddresses = Object.keys(this.subscribed);
|
||||
this.subscribed = {};
|
||||
var s = this.socket;
|
||||
if (s) {
|
||||
s.removeAllListeners();
|
||||
this._setMainHandlers();
|
||||
this.subscribe(allAddresses);
|
||||
this.subscribeToBlocks();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Insight.prototype.broadcast = function(rawtx, cb) {
|
||||
preconditions.checkArgument(rawtx);
|
||||
preconditions.shouldBeFunction(cb);
|
||||
|
||||
this.requestPost('/api/tx/send', {
|
||||
rawtx: rawtx
|
||||
}, function(err, res, body) {
|
||||
if (err || res.statusCode != 200) return cb(err || body);
|
||||
return cb(null, body ? body.txid : null);
|
||||
});
|
||||
};
|
||||
|
||||
Insight.prototype.getTransaction = function(txid, cb) {
|
||||
preconditions.shouldBeFunction(cb);
|
||||
this.request('/api/tx/' + txid, function(err, res, body) {
|
||||
if (err || res.statusCode != 200 || !body) return cb(err || res);
|
||||
cb(null, JSON.parse(body));
|
||||
});
|
||||
};
|
||||
|
||||
Insight.prototype.getTransactions = function(addresses, from, to, cb) {
|
||||
preconditions.shouldBeArray(addresses);
|
||||
preconditions.shouldBeFunction(cb);
|
||||
|
||||
var qs = '';
|
||||
if (_.isNumber(from)) {
|
||||
qs += '?from=' + from;
|
||||
if (_.isNumber(to)) {
|
||||
qs += '&to=' + to;
|
||||
}
|
||||
}
|
||||
|
||||
this.requestPost('/api/addrs/txs' + qs, {
|
||||
addrs: addresses.join(',')
|
||||
}, function(err, res, txs) {
|
||||
if (err || res.statusCode != 200) return cb(err || res);
|
||||
cb(null, txs);
|
||||
});
|
||||
};
|
||||
|
||||
Insight.prototype.getUnspent = function(addresses, cb) {
|
||||
preconditions.shouldBeArray(addresses);
|
||||
preconditions.shouldBeFunction(cb);
|
||||
|
||||
this.requestPost('/api/addrs/utxo', {
|
||||
addrs: addresses.join(',')
|
||||
}, function(err, res, unspentRaw) {
|
||||
if (err || res.statusCode != 200) return cb(err || res);
|
||||
|
||||
// This filter out possible broken unspent, as reported on
|
||||
// https://github.com/bitpay/copay/issues/1585
|
||||
// and later gitter conversation.
|
||||
|
||||
var unspent = _.filter(unspentRaw, 'scriptPubKey');
|
||||
cb(null, unspent);
|
||||
});
|
||||
};
|
||||
|
||||
Insight.prototype.getActivity = function(addresses, cb) {
|
||||
preconditions.shouldBeArray(addresses);
|
||||
|
||||
this.getTransactions(addresses, null, null, function then(err, txs) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var flatArray = function(xss) {
|
||||
return xss.reduce(function(r, xs) {
|
||||
return r.concat(xs);
|
||||
}, []);
|
||||
};
|
||||
var getInputs = function(t) {
|
||||
return t.vin.map(function(vin) {
|
||||
return vin.addr
|
||||
});
|
||||
};
|
||||
var getOutputs = function(t) {
|
||||
return flatArray(
|
||||
t.vout.map(function(vout) {
|
||||
return vout.scriptPubKey.addresses;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
var activityMap = new Array(addresses.length);
|
||||
var activeAddress = flatArray(txs.map(function(t) {
|
||||
return getInputs(t).concat(getOutputs(t));
|
||||
}));
|
||||
activeAddress.forEach(function(addr) {
|
||||
var index = addresses.indexOf(addr);
|
||||
if (index != -1) activityMap[index] = true;
|
||||
});
|
||||
|
||||
cb(null, activityMap);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Insight;
|
|
@ -1,57 +0,0 @@
|
|||
'use strict';
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var log = require('../util/log');
|
||||
|
||||
function PluginManager(config) {
|
||||
this.registered = {};
|
||||
this.scripts = [];
|
||||
|
||||
for (var ii in config.plugins) {
|
||||
var pluginName = ii;
|
||||
|
||||
if (!config.plugins[pluginName])
|
||||
continue;
|
||||
|
||||
log.info('Loading plugin: ' + pluginName);
|
||||
var pluginClass;
|
||||
if(config.pluginsPath){
|
||||
pluginClass = require(config.pluginsPath + pluginName);
|
||||
} else {
|
||||
pluginClass = require('../js/plugins/' + pluginName);
|
||||
}
|
||||
var pluginObj = new pluginClass(config[pluginName]);
|
||||
pluginObj.init();
|
||||
this._register(pluginObj, pluginName);
|
||||
}
|
||||
};
|
||||
|
||||
var KIND_UNIQUE = PluginManager.KIND_UNIQUE = 1;
|
||||
var KIND_MULTIPLE = PluginManager.KIND_MULTIPLE = 2;
|
||||
|
||||
PluginManager.TYPE = {};
|
||||
PluginManager.TYPE['DB'] = KIND_UNIQUE;
|
||||
|
||||
PluginManager.prototype._register = function(obj, name) {
|
||||
preconditions.checkArgument(obj.type, 'Plugin has not type:' + name);
|
||||
var type = obj.type;
|
||||
var kind = PluginManager.TYPE[type];
|
||||
|
||||
preconditions.checkArgument(kind, 'Unknown plugin type:' + name);
|
||||
preconditions.checkState(kind !== PluginManager.KIND_UNIQUE || !this.registered[type], 'Plugin kind already registered:' + name);
|
||||
|
||||
if (kind === PluginManager.KIND_UNIQUE) {
|
||||
this.registered[type] = obj;
|
||||
} else {
|
||||
this.registered[type] = this.registered[type] || [];
|
||||
this.registered[type].push(obj);
|
||||
}
|
||||
|
||||
this.scripts = this.scripts.concat(obj.scripts || []);
|
||||
};
|
||||
|
||||
|
||||
PluginManager.prototype.get = function(type) {
|
||||
return this.registered[type];
|
||||
};
|
||||
|
||||
module.exports = PluginManager;
|
|
@ -1,264 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// 62.9% typed (by google's closure-compiler account)
|
||||
|
||||
var bitcore = require('bitcore');
|
||||
var HK = bitcore.HierarchicalKey;
|
||||
var WalletKey = bitcore.WalletKey;
|
||||
var networks = bitcore.networks;
|
||||
var util = bitcore.util;
|
||||
var _ = require('lodash');
|
||||
var preconditions = require('preconditions').instance();
|
||||
var HDPath = require('./HDPath');
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Wrapper for bitcore.HierarchicalKey to be used inside of Copay.
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.networkName if set to 'testnet', use the test3 bitcoin
|
||||
* network constants (livenet otherwise)
|
||||
* @param {string} opts.extendedPrivateKeyString if set, use this private key
|
||||
* string, othewise create a new
|
||||
* private key
|
||||
* @constructor
|
||||
*/
|
||||
function PrivateKey(opts) {
|
||||
opts = opts || {};
|
||||
this.network = opts.networkName === 'testnet' ? networks.testnet : networks.livenet;
|
||||
var init = opts.extendedPrivateKeyString || this.network.name;
|
||||
this.bip = new HK(init);
|
||||
this.privateKeyCache = {};
|
||||
this.publicHex = this.deriveBIP45Branch().eckey.public.toString('hex');
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Retrieve this derivated private key's public key in hexa format
|
||||
*
|
||||
* The value returned is calculated using the path from PrivateKey's
|
||||
* <tt>HDParams.IdFullBranch</tt>. This key is used to identify the copayer
|
||||
* (signing messages mostly).
|
||||
*
|
||||
* @returns {string} the public key in a hexadecimal string
|
||||
*/
|
||||
PrivateKey.prototype.getId = function() {
|
||||
if (!this.id) {
|
||||
this.cacheId();
|
||||
}
|
||||
return this.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Retrieve this private key's private key in hex format
|
||||
*
|
||||
* The value returned is calculated using the path from PrivateKey's
|
||||
* <tt>HDParams.IdFullBranch</tt>. This key is used to identify the copayer
|
||||
* (signing messages mostly).
|
||||
*
|
||||
* @returns {string} the private key in a hexadecimal string
|
||||
*/
|
||||
PrivateKey.prototype.getIdPriv = function() {
|
||||
if (!this.idpriv) {
|
||||
this.cacheId();
|
||||
}
|
||||
return this.idpriv;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Retrieve this private key's private key
|
||||
*
|
||||
* The value returned is calculated using the path from PrivateKey's
|
||||
* <tt>HDParams.IdFullBranch</tt>. This key is used to identify the copayer
|
||||
* (signing messages mostly).
|
||||
*
|
||||
* @returns {bitcore.PrivateKey} the private key
|
||||
*/
|
||||
PrivateKey.prototype.getIdKey = function() {
|
||||
if (!this.idkey) {
|
||||
this.cacheId();
|
||||
}
|
||||
return this.idkey;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Caches the result of deriving IdFullBranch
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
PrivateKey.prototype.cacheId = function() {
|
||||
var path = HDPath.IdFullBranch;
|
||||
var idhk = this.bip.derive(path);
|
||||
this.idkey = idhk.eckey;
|
||||
this.id = idhk.eckey.public.toString('hex');
|
||||
this.idpriv = idhk.eckey.private.toString('hex');
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Derive the master branch for Copay.
|
||||
*/
|
||||
PrivateKey.prototype.deriveBIP45Branch = function() {
|
||||
if (!this.bip45Branch) {
|
||||
this.bip45Branch = this.bip.derive(HDPath.BIP45_PUBLIC_PREFIX);
|
||||
}
|
||||
return this.bip45Branch;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Returns an object with information needed to rebuild a PrivateKey
|
||||
* (as most of its properties are derived from the extended private key).
|
||||
*
|
||||
* @TODO: Figure out if this is the correct pattern
|
||||
* This is a static method and is probably used for serialization.
|
||||
*
|
||||
* @static
|
||||
* @param {Object} data
|
||||
* @param {*} data.networkName - a name for a bitcoin network
|
||||
* @param {*} data.extendedPrivateKeyString - a bip32 extended private key
|
||||
* @returns {Object} an object with two properties: networkName and
|
||||
* extendedPrivateKeyString, taken from the <tt>data</tt>
|
||||
* parameter.
|
||||
*/
|
||||
PrivateKey.trim = function(data) {
|
||||
var opts = {};
|
||||
['networkName', 'extendedPrivateKeyString'].forEach(function(k){
|
||||
opts[k] = data[k];
|
||||
});
|
||||
return opts
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Generate a private Key from a serialized object
|
||||
*
|
||||
* @TODO: This method uses PrivateKey.trim but it's actually not needed...
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {*} data.networkName - a name for a bitcoin network
|
||||
* @param {*} data.extendedPrivateKeyString - a bip32 extended private key
|
||||
* @returns {PrivateKey}
|
||||
*/
|
||||
PrivateKey.fromObj = function(obj) {
|
||||
return new PrivateKey(PrivateKey.trim(obj));
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Serialize a private key, keeping only the data necessary to rebuild it
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
PrivateKey.prototype.toObj = function() {
|
||||
return {
|
||||
extendedPrivateKeyString: this.getExtendedPrivateKeyString(),
|
||||
networkName: this.network.name
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Retrieve a BIP32 extended public key as generated by bitcore
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
PrivateKey.prototype.getExtendedPublicKeyString = function() {
|
||||
return this.bip.extendedPublicKeyString();
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Retrieve a BIP32 extended private key as generated by bitcore
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
PrivateKey.prototype.getExtendedPrivateKeyString = function() {
|
||||
return this.bip.extendedPrivateKeyString();
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve a HierarchicalKey derived from the given path as generated by
|
||||
* bitcore
|
||||
* @param {string} path - a string for derivation (something like "m/234'/1/2")
|
||||
* @returns {bitcore.HierarchicalKey}
|
||||
*/
|
||||
PrivateKey.prototype._getHK = function(path) {
|
||||
if (_.isUndefined(path)) {
|
||||
return this.bip;
|
||||
}
|
||||
var ret = this.bip.derive(path);
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve an array of WalletKey derived from given paths. {@see PrivateKey#getForPath}
|
||||
*
|
||||
* @param {string[]} paths - the paths to derive
|
||||
* @returns {bitcore.WalletKey[]} - the derived keys
|
||||
*/
|
||||
PrivateKey.prototype.getForPaths = function(paths) {
|
||||
return paths.map(this.getForPath.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve a WalletKey derived from a path.
|
||||
*
|
||||
* @param {string} paths - the path to derive
|
||||
* @returns {bitcore.WalletKey} - the derived key
|
||||
*/
|
||||
PrivateKey.prototype.getForPath = function(path) {
|
||||
var pk = this.privateKeyCache[path];
|
||||
if (!pk) {
|
||||
var derivedHK = this._getHK(path);
|
||||
pk = this.privateKeyCache[path] = derivedHK.eckey.private.toString('hex');
|
||||
}
|
||||
var wk = new WalletKey({
|
||||
network: this.network
|
||||
});
|
||||
wk.fromObj({
|
||||
priv: pk
|
||||
});
|
||||
return wk;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve a Branch for Copay using the given path
|
||||
*
|
||||
* @TODO: Investigate when is this called and if this is really needed
|
||||
*
|
||||
* @param {number} index - the index of the key to generate
|
||||
* @param {boolean} isChange - whether this is a change adderess or a receive
|
||||
* @param {number} cosigner - the cosigner index
|
||||
* @return {bitcore.HierarchicalKey}
|
||||
*/
|
||||
PrivateKey.prototype.get = function(index, isChange, cosigner) {
|
||||
|
||||
// TODO: Add parameter validation?
|
||||
|
||||
var path = HDPath.FullBranch(index, isChange, cosigner);
|
||||
return this.getForPath(path);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve multiple branches for Copay up to the received indexes
|
||||
*
|
||||
* @TODO: Investigate when is this called and if this is really needed
|
||||
*
|
||||
* @param {number} receiveIndex - the number of receive addresses to generate
|
||||
* @param {number} changeIndex - the number of change addresses to generate
|
||||
* @param {number} cosigner - the cosigner index
|
||||
* @return {bitcore.HierarchicalKey}
|
||||
*/
|
||||
PrivateKey.prototype.getAll = function(receiveIndex, changeIndex, cosigner) {
|
||||
preconditions.checkArgument(!_.isUndefined(receiveIndex) && !_.isUndefined(changeIndex));
|
||||
|
||||
var ret = [];
|
||||
for (var i = 0; i < receiveIndex; i++) {
|
||||
ret.push(this.get(i, false, cosigner));
|
||||
}
|
||||
for (var i = 0; i < changeIndex; i++) {
|
||||
ret.push(this.get(i, true, cosigner));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
module.exports = PrivateKey;
|
|
@ -1,803 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var preconditions = require('preconditions').instance();
|
||||
var _ = require('lodash');
|
||||
var log = require('../util/log');
|
||||
var bitcore = require('bitcore');
|
||||
var HK = bitcore.HierarchicalKey;
|
||||
var Address = bitcore.Address;
|
||||
var Script = bitcore.Script;
|
||||
var PrivateKey = require('./PrivateKey');
|
||||
var HDPath = require('./HDPath');
|
||||
var HDParams = require('./HDParams');
|
||||
|
||||
/**
|
||||
* @desc Represents a public key ring, the set of all public keys and the used indexes
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId
|
||||
* @param {string} opts.network 'livenet' to signal the bitcoin main network, all others are testnet
|
||||
* @param {number=} opts.requiredCopayers - defaults to 3
|
||||
* @param {number=} opts.totalCopayers - defaults to 5
|
||||
* @param {Object[]} [opts.indexes] - an array to be deserialized using {@link HDParams#fromList}
|
||||
* (defaults to all indexes in zero)
|
||||
* @param {Object=} opts.nicknameFor - nicknames for other copayers
|
||||
*/
|
||||
function PublicKeyRing(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
this.walletId = opts.walletId;
|
||||
|
||||
this.network = opts.networkName === 'livenet' ?
|
||||
bitcore.networks.livenet : bitcore.networks.testnet;
|
||||
|
||||
this.requiredCopayers = opts.requiredCopayers || 3;
|
||||
this.totalCopayers = opts.totalCopayers || 5;
|
||||
|
||||
this.copayersHK = [];
|
||||
|
||||
this.indexes = opts.indexes ? HDParams.fromList(opts.indexes) : HDParams.init(this.totalCopayers);
|
||||
|
||||
this.publicKeysCache = {};
|
||||
this.nicknameFor = opts.nicknameFor || {};
|
||||
this.copayerIds = [];
|
||||
|
||||
this.resetCache();
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.resetCache = function() {
|
||||
this.cache = {};
|
||||
this.cache.addressToPath = {};
|
||||
this.cache.pathToAddress = {};
|
||||
|
||||
// Non persistent cache
|
||||
this._isChange = {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc Returns an object with only the keys needed to rebuild a PublicKeyRing
|
||||
*
|
||||
* @TODO: Figure out if this is the correct pattern
|
||||
* This is a static method and is probably used for serialization.
|
||||
*
|
||||
* @static
|
||||
* @param {Object} data
|
||||
* @param {string} data.walletId - a string to identify a wallet
|
||||
* @param {string} data.networkName - the name of the bitcoin network
|
||||
* @param {number} data.requiredCopayers - the number of required copayers
|
||||
* @param {number} data.totalCopayers - the number of copayers in the ring
|
||||
* @param {Object[]} data.indexes - an array of objects that can be turned into
|
||||
* an array of HDParams
|
||||
* @param {Object} data.nicknameFor - a registry of nicknames for other copayers
|
||||
* @param {string[]} data.copayersExtPubKeys - the extended public keys of copayers
|
||||
* @returns {Object} a trimmed down version of PublicKeyRing that can be used
|
||||
* as a parameter
|
||||
*/
|
||||
PublicKeyRing.trim = function(data) {
|
||||
preconditions.checkArgument(data);
|
||||
var opts = {};
|
||||
['walletId', 'networkName', 'requiredCopayers', 'totalCopayers',
|
||||
'indexes', 'nicknameFor', 'copayersExtPubKeys'
|
||||
].forEach(function(k) {
|
||||
opts[k] = data[k];
|
||||
});
|
||||
return opts;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Deserializes a PublicKeyRing from a plain object
|
||||
*
|
||||
* If the <tt>data</tt> parameter is an instance of PublicKeyRing already,
|
||||
* it will fail, throwing an assertion error.
|
||||
*
|
||||
* @static
|
||||
* @param {object} data - a serialized version of PublicKeyRing {@see PublicKeyRing#trim}
|
||||
* @return {PublicKeyRing} - the deserialized object
|
||||
*/
|
||||
PublicKeyRing.fromObj = function(opts) {
|
||||
preconditions.checkArgument(!(opts instanceof PublicKeyRing), 'bad opts format: Did you use .toObj()?');
|
||||
|
||||
// Support old indexes schema
|
||||
if (!Array.isArray(opts.indexes)) {
|
||||
opts.indexes = HDParams.update(opts.indexes, opts.totalCopayers);
|
||||
}
|
||||
|
||||
var pkr = new PublicKeyRing(opts);
|
||||
|
||||
for (var k in opts.copayersExtPubKeys) {
|
||||
pkr.addCopayer(opts.copayersExtPubKeys[k]);
|
||||
}
|
||||
|
||||
if (opts.cache && opts.cache.addressToPath) {
|
||||
log.debug('PublicKeyRing: Using address cache');
|
||||
pkr.cache.addressToPath = opts.cache.addressToPath;
|
||||
pkr.rebuildCache();
|
||||
}
|
||||
|
||||
return pkr;
|
||||
};
|
||||
|
||||
|
||||
PublicKeyRing.prototype.rebuildCache = function() {
|
||||
if (!this.cache.addressToPath)
|
||||
return;
|
||||
|
||||
var self = this;
|
||||
_.each(this.cache.addressToPath, function(path, address) {
|
||||
self.cache.pathToAddress[path] = address;
|
||||
});
|
||||
};
|
||||
|
||||
PublicKeyRing.fromUntrustedObj = function(opts) {
|
||||
return PublicKeyRing.fromObj(PublicKeyRing.trim(opts));
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Serialize this object to a plain object with all the data needed to
|
||||
* rebuild it
|
||||
*
|
||||
* @return {Object} a serialized version of a PublicKeyRing
|
||||
*/
|
||||
PublicKeyRing.prototype.toObj = function() {
|
||||
return {
|
||||
walletId: this.walletId,
|
||||
networkName: this.network.name,
|
||||
requiredCopayers: this.requiredCopayers,
|
||||
totalCopayers: this.totalCopayers,
|
||||
indexes: HDParams.serialize(this.indexes),
|
||||
|
||||
copayersExtPubKeys: this.copayersHK.map(function(b) {
|
||||
return b.extendedPublicKeyString();
|
||||
}),
|
||||
nicknameFor: this.nicknameFor,
|
||||
|
||||
// We only store addressToPath and derive the reset from it
|
||||
cache: {
|
||||
addressToPath: this.cache.addressToPath
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
PublicKeyRing.prototype.toTrimmedObj = function() {
|
||||
return PublicKeyRing.trim(this.toObj());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve a copayer's public key as a hexadecimal encoded string
|
||||
*
|
||||
* @param {number} copayerId - the copayer id
|
||||
* @returns {string} the extended public key of the i-th copayer
|
||||
*/
|
||||
PublicKeyRing.prototype.getCopayerId = function(copayerId) {
|
||||
preconditions.checkArgument(!_.isUndefined(copayerId))
|
||||
preconditions.checkArgument(_.isNumber(copayerId));
|
||||
|
||||
return this.copayerIds[copayerId];
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Get the amount of registered copayers in this PubKeyRing
|
||||
*
|
||||
* @returns {number} amount of copayers present
|
||||
*/
|
||||
PublicKeyRing.prototype.registeredCopayers = function() {
|
||||
return this.copayersHK.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Returns true if all the needed copayers have joined the public key ring
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
PublicKeyRing.prototype.isComplete = function() {
|
||||
return this.remainingCopayers() == 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Returns the number of copayers yet to join to make the public key ring complete
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
PublicKeyRing.prototype.remainingCopayers = function() {
|
||||
return this.totalCopayers - this.registeredCopayers();
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Returns an array of copayer's public keys
|
||||
*
|
||||
* @returns {string[]} a list of hexadecimal strings with the public keys for
|
||||
* the copayers in this ring
|
||||
*/
|
||||
PublicKeyRing.prototype.getAllCopayerIds = function() {
|
||||
return this.copayerIds;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Gets the current user's copayerId
|
||||
*
|
||||
* @returns {string} the extended public key hexadecimal-encoded
|
||||
*/
|
||||
PublicKeyRing.prototype.myCopayerId = function() {
|
||||
return this.getCopayerId(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Throws an error if the public key ring isn't complete
|
||||
*/
|
||||
PublicKeyRing.prototype._checkKeys = function() {
|
||||
if (!this.isComplete()) throw new Error('dont have required keys yet');
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Updates the internal register of the public hex string for a copayer, based
|
||||
* on the value of the hierarchical key stored in copayersHK
|
||||
*
|
||||
* @private
|
||||
* @param {number} index - the index of the copayer to update
|
||||
*/
|
||||
PublicKeyRing.prototype._updateBip = function(index) {
|
||||
var hk = this.copayersHK[index].derive(HDPath.IdBranch);
|
||||
this.copayerIds[index] = hk.eckey.public.toString('hex');
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Sets a nickname for one of the copayers
|
||||
*
|
||||
* @private
|
||||
* @param {number} index - the index of the copayer to update
|
||||
* @param {string} nickname - the new nickname for that copayer
|
||||
*/
|
||||
PublicKeyRing.prototype._setNicknameForIndex = function(index, nickname) {
|
||||
this.nicknameFor[this.copayerIds[index]] = nickname;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Fetch the name of a copayer
|
||||
*
|
||||
* @param {number} index - the index of the copayer
|
||||
* @return {string} the nickname of the index-th copayer
|
||||
*/
|
||||
PublicKeyRing.prototype.nicknameForIndex = function(index) {
|
||||
return this.nicknameFor[this.copayerIds[index]];
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Fetch the name of a copayer using its public key
|
||||
*
|
||||
* @param {string} copayerId - the public key ring of a copayer, hex encoded
|
||||
* @return {string} the nickname of the copayer with such pubkey
|
||||
*/
|
||||
PublicKeyRing.prototype.nicknameForCopayer = function(copayerId) {
|
||||
return this.nicknameFor[copayerId] || 'NN';
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Add a copayer into the public key ring.
|
||||
*
|
||||
* @param {string} newHexaExtendedPublicKey - an hex encoded string with the copayer's pubkey
|
||||
* @param {string} nickname - a nickname for this copayer
|
||||
* @return {string} the newHexaExtendedPublicKey parameter
|
||||
*/
|
||||
PublicKeyRing.prototype.addCopayer = function(newHexaExtendedPublicKey, nickname) {
|
||||
preconditions.checkArgument(newHexaExtendedPublicKey && _.isString(newHexaExtendedPublicKey));
|
||||
preconditions.checkArgument(!this.isComplete());
|
||||
preconditions.checkArgument(!nickname || _.isString(nickname));
|
||||
preconditions.checkArgument(!_.any(this.copayersHK,
|
||||
function(copayer) {
|
||||
return copayer.extendedPublicKeyString === newHexaExtendedPublicKey;
|
||||
}
|
||||
));
|
||||
|
||||
var newCopayerIndex = this.copayersHK.length;
|
||||
var hierarchicalKey = new HK(newHexaExtendedPublicKey);
|
||||
|
||||
this.copayersHK.push(hierarchicalKey);
|
||||
this._updateBip(newCopayerIndex);
|
||||
|
||||
if (nickname) {
|
||||
this._setNicknameForIndex(newCopayerIndex, nickname);
|
||||
}
|
||||
return newHexaExtendedPublicKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Get all the public keys for the copayers in this ring, for a given branch of Copay
|
||||
*
|
||||
* @param {number} index - the index for the shared address
|
||||
* @param {boolean} isChange - whether to derive a change address o receive address
|
||||
* @param {number} copayerIndex - the index of the copayer that requested the derivation
|
||||
* @return {Buffer[]} an array of derived public keys in hexa format
|
||||
*/
|
||||
PublicKeyRing.prototype.getPubKeys = function(index, isChange, copayerIndex) {
|
||||
this._checkKeys();
|
||||
|
||||
log.warn('Slow pubkey derivation...');
|
||||
var path = HDPath.Branch(index, isChange, copayerIndex);
|
||||
var pubKeys = _.map(this.copayersHK, function(hdKey) {
|
||||
return hdKey.derive(path).eckey.public;
|
||||
});
|
||||
|
||||
return pubKeys;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Generate a new Script for a copay address generated by index, isChange, and copayerIndex
|
||||
*
|
||||
* @TODO this could be cached
|
||||
*
|
||||
* @param {number} index - the index for the shared address
|
||||
* @param {boolean} isChange - whether to derive a change address o receive address
|
||||
* @param {number} copayerIndex - the index of the copayer that requested the derivation
|
||||
* @returns {bitcore.Script}
|
||||
*/
|
||||
PublicKeyRing.prototype.getRedeemScript = function(index, isChange, copayerIndex) {
|
||||
var pubKeys = this.getPubKeys(index, isChange, copayerIndex);
|
||||
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
||||
return script;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Get the address for a multisig based on the given params.
|
||||
*
|
||||
* Caches the address to the branch in the member addressToPath
|
||||
*
|
||||
* @param {number} index - the index for the shared address
|
||||
* @param {boolean} isChange - whether to derive a change address o receive address
|
||||
* @param {number} copayerIndex - the index of the copayer that requested the derivation
|
||||
* @returns {bitcore.Address}
|
||||
*/
|
||||
PublicKeyRing.prototype._getAddress = function(index, isChange, id) {
|
||||
var copayerIndex = this.getCosigner(id);
|
||||
var path = HDPath.FullBranch(index, isChange, copayerIndex);
|
||||
if (this.cache.pathToAddress[path])
|
||||
return this.cache.pathToAddress[path];
|
||||
|
||||
log.info('Generating Address:', index, isChange, copayerIndex);
|
||||
var script = this.getRedeemScript(index, isChange, copayerIndex);
|
||||
var address = Address.fromScript(script, this.network.name).toString();
|
||||
|
||||
this._cacheAddress(address, path, isChange);
|
||||
return address;
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype._cacheAddress = function(address, path, isChange) {
|
||||
this.cache.addressToPath[address] = path;
|
||||
this.cache.pathToAddress[path] = address;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Get the parameters used to derive a pubkey or a cosigner index
|
||||
*
|
||||
* Overloaded to receive a PubkeyString or a consigner index
|
||||
*
|
||||
* @param {number|string} id public key in hex format, or the copayer's index
|
||||
* @return ????
|
||||
*/
|
||||
PublicKeyRing.prototype.getHDParams = function(id) {
|
||||
var copayerIndex = this.getCosigner(id);
|
||||
var index = this.indexes.filter(function(i) {
|
||||
return i.copayerIndex == copayerIndex
|
||||
});
|
||||
if (index.length != 1) throw new Error('no index for copayerIndex');
|
||||
|
||||
return index[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Get the path used to derive a pubkey or a cosigner index for an address
|
||||
*
|
||||
* @param {string} address a multisig p2sh address
|
||||
* @return {HDPath}
|
||||
*/
|
||||
PublicKeyRing.prototype.pathForAddress = function(address) {
|
||||
this._checkCache();
|
||||
var path = this.cache.addressToPath[address];
|
||||
if (!path) throw new Error('Couldn\'t find path for address ' + address);
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Generates a new address and updates the last index used
|
||||
*
|
||||
* @param {truthy} isChange - generate a change address if true, otherwise
|
||||
* generates a receive
|
||||
* @param {number|string} pubkey - the pubkey for the copayer that generates the
|
||||
* address (or index in the keyring)
|
||||
* @returns {bitpay.Address}
|
||||
*/
|
||||
PublicKeyRing.prototype.generateAddress = function(isChange, pubkey) {
|
||||
isChange = !!isChange;
|
||||
var hdParams = this.getHDParams(pubkey);
|
||||
var index = isChange ? hdParams.getChangeIndex() : hdParams.getReceiveIndex();
|
||||
var ret = this._getAddress(index, isChange, hdParams.copayerIndex);
|
||||
hdParams.increment(isChange);
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Is an address is from this wallet?
|
||||
*
|
||||
* @param {string} address
|
||||
* @return {boolean}
|
||||
*/
|
||||
PublicKeyRing.prototype.addressIsOwn = function(address) {
|
||||
return !!this.cache.addressToPath[address];
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Is an address is a change address?
|
||||
*
|
||||
* @param {string} address
|
||||
* @return {boolean}
|
||||
*/
|
||||
PublicKeyRing.prototype.addressIsChange = function(address) {
|
||||
this._checkCache();
|
||||
|
||||
var path = this.cache.addressToPath[address];
|
||||
if (!path)
|
||||
return null;
|
||||
|
||||
var p = HDPath.indexesForPath(path);
|
||||
|
||||
//Memoization Only, never stored.
|
||||
this._isChange[address] = p.isChange;
|
||||
return !!this._isChange[address];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Maps a copayer's public key to his index in the keyring
|
||||
*
|
||||
* @param {number|string|undefined} pubKey - if undefined, returns the SHARED_INDEX
|
||||
* - if a number, just return it
|
||||
* - if a string, assume is the hex encoded public key
|
||||
* @returns {number} the index of the copayer with the given pubkey
|
||||
*/
|
||||
PublicKeyRing.prototype.getCosigner = function(pubKey) {
|
||||
if (_.isUndefined(pubKey)) return HDPath.SHARED_INDEX;
|
||||
if (_.isNumber(pubKey)) return pubKey;
|
||||
|
||||
var sorted = this.copayersHK.map(function(h, i) {
|
||||
return h.eckey.public.toString('hex');
|
||||
}).sort(function(h1, h2) {
|
||||
return h1.localeCompare(h2);
|
||||
});
|
||||
|
||||
var index = sorted.indexOf(pubKey);
|
||||
if (index == -1) throw new Error('public key is not on the ring');
|
||||
|
||||
return index;
|
||||
};
|
||||
|
||||
|
||||
|
||||
PublicKeyRing.prototype.buildAddressCache = function() {
|
||||
var ret = [];
|
||||
var self = this;
|
||||
|
||||
_.each(this.indexes, function(index) {
|
||||
for (var i = 0; i < index.receiveIndex; i++) {
|
||||
self._getAddress(i, false, index.copayerIndex);
|
||||
}
|
||||
for (var i = 0; i < index.changeIndex; i++) {
|
||||
self._getAddress(i, true, index.copayerIndex);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
PublicKeyRing.prototype.size = function(opts) {
|
||||
var self = this;
|
||||
return _.reduce(this.indexes, function(sum, index) {
|
||||
return sum + index.receiveIndex + index.changeIndex
|
||||
}, 0);
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype._checkCache = function(opts) {
|
||||
if (_.isEmpty(this.cache.addressToPath)) {
|
||||
this.buildAddressCache();
|
||||
}
|
||||
if (_.size(this.cache.addressToPath) !== this.size()) {
|
||||
this.buildAddressCache();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Gets information about addresses for a copayer
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @returns {AddressInfo[]}
|
||||
*/
|
||||
PublicKeyRing.prototype.getAddresses = function() {
|
||||
this._checkCache();
|
||||
return _.keys(this.cache.addressToPath);
|
||||
};
|
||||
|
||||
/**
|
||||
* getAddressesOrdered
|
||||
* {@link Wallet#getAddressesOrdered}
|
||||
*
|
||||
* @param pubkey
|
||||
* @return {string[]}
|
||||
*/
|
||||
PublicKeyRing.prototype.getAddressesOrdered = function(pubkey) {
|
||||
this._checkCache();
|
||||
|
||||
var info = _.map(this.cache.addressToPath, function(path, addr) {
|
||||
var p = HDPath.indexesForPath(path);
|
||||
p.address = addr;
|
||||
return p;
|
||||
});
|
||||
|
||||
var copayerIndex = this.getCosigner(pubkey);
|
||||
var l = info.length;
|
||||
|
||||
var sortedInfo = _.sortBy(info, function(i) {
|
||||
var goodness = ((i.copayerIndex !== copayerIndex) ? 2 * l : 0) + (i.isChange ? l : 0) + l - i.addressIndex;
|
||||
return goodness;
|
||||
});
|
||||
|
||||
return _.pluck(sortedInfo, 'address');
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Gets information about addresses for a copayer
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @returns {AddressInfo[]}
|
||||
*/
|
||||
PublicKeyRing.prototype.getReceiveAddresses = function() {
|
||||
this._checkCache();
|
||||
|
||||
var self = this;
|
||||
return _.filter(this.getAddresses(), function(addr) {
|
||||
return !self.addressIsChange(addr);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve the public keys for all cosigners for a given path
|
||||
*
|
||||
* @param {string} path - the BIP32 path
|
||||
* @return {Buffer[]} the public keys, in buffer format
|
||||
*/
|
||||
PublicKeyRing.prototype._getForPath = function(path) {
|
||||
var p = HDPath.indexesForPath(path);
|
||||
return this.getPubKeys(p.addressIndex, p.isChange, p.copayerIndex);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve the public keys for derived addresses and the public keys for copayers
|
||||
*
|
||||
* @TODO: Should this exist? A user should just call _getForPath(paths)
|
||||
*
|
||||
* @param {string[]} paths - the paths to be derived
|
||||
* @return {Object} with keys pubKeys and copayerIds
|
||||
*/
|
||||
PublicKeyRing.prototype.forPaths = function(paths) {
|
||||
return {
|
||||
pubKeys: paths.map(this._getForPath.bind(this)),
|
||||
copayerIds: this.copayerIds,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Retrieve the public keys for all cosigners for multiple paths
|
||||
*
|
||||
* @param {string[]} paths - the BIP32 paths
|
||||
* @return {Array[]} the public keys, in buffer format (matrix of Buffer, Buffer[][])
|
||||
*/
|
||||
PublicKeyRing.prototype._getForPaths = function(paths) {
|
||||
preconditions.checkArgument(!_.isUndefined(paths));
|
||||
preconditions.checkArgument(_.isArray(paths));
|
||||
preconditions.checkArgument(_.all(paths, _.isString));
|
||||
|
||||
return paths.map(this._getForPath.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Returns a map from a pubkey of an address to the id that generated it
|
||||
*
|
||||
* @param {string[]} pubkeys - the pubkeys to query
|
||||
* @param {string[]} paths - the paths to query
|
||||
*/
|
||||
PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
|
||||
preconditions.checkArgument(pubkeys);
|
||||
preconditions.checkArgument(paths);
|
||||
|
||||
var inKeyMap = {},
|
||||
ret = {};
|
||||
for (var i in pubkeys) {
|
||||
inKeyMap[pubkeys[i]] = 1;
|
||||
};
|
||||
|
||||
var keys = this._getForPaths(paths);
|
||||
for (var i in keys) {
|
||||
for (var copayerIndex in keys[i]) {
|
||||
var kHex = keys[i][copayerIndex].toString('hex');
|
||||
if (inKeyMap[kHex]) {
|
||||
ret[kHex] = this.copayerIds[copayerIndex];
|
||||
delete inKeyMap[kHex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_.size(inKeyMap)) {
|
||||
for (var i in inKeyMap) {
|
||||
log.error('Pubkey ' + i + ' not identified');
|
||||
}
|
||||
throw new Error('Pubkeys not identified');
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Returns a map from address -> public key needed
|
||||
*
|
||||
* @param {HDPath[]} paths - paths to be solved
|
||||
* @returns {Object} a map from addresses to Buffer with the hex pubkeys
|
||||
*/
|
||||
PublicKeyRing.prototype.getRedeemScriptMap = function(paths) {
|
||||
var ret = {};
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
var path = paths[i];
|
||||
var p = HDPath.indexesForPath(path);
|
||||
var script = this.getRedeemScript(p.addressIndex, p.isChange, p.copayerIndex);
|
||||
ret[Address.fromScript(script, this.network.name).toString()] = script.getBuffer().toString('hex');
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Check if another PubKeyRing is similar to this one (checks network name,
|
||||
* requiredCopayers, and totalCopayers). If ignoreId is falsy, also check that
|
||||
* both walletIds match.
|
||||
*
|
||||
* @private
|
||||
* @param {PubKeyRing} inPKR - the other PubKeyRing
|
||||
* @param {boolean} ignoreId - whether to ignore checking for equal walletId
|
||||
* @throws {Error} if the wallets mismatch
|
||||
* @return true
|
||||
*/
|
||||
|
||||
PublicKeyRing.prototype._checkInPKR = function(inPKR, ignoreId) {
|
||||
preconditions.checkArgument(_.isObject(inPKR));
|
||||
|
||||
if (!ignoreId && this.walletId !== inPKR.walletId)
|
||||
throw new Error('inPKR walletId mismatch');
|
||||
|
||||
if (this.network.name !== inPKR.network.name)
|
||||
throw new Error('Network mismatch. Should be ' + this.network.name +
|
||||
' and found ' + inPKR.network.name);
|
||||
|
||||
if (this.requiredCopayers && inPKR.requiredCopayers &&
|
||||
(this.requiredCopayers !== inPKR.requiredCopayers))
|
||||
throw new Error('inPKR requiredCopayers mismatch ' + this.requiredCopayers +
|
||||
'!=' + inPKR.requiredCopayers);
|
||||
|
||||
if (this.totalCopayers && inPKR.totalCopayers &&
|
||||
this.totalCopayers !== inPKR.totalCopayers)
|
||||
throw new Error('inPKR totalCopayers mismatch' + this.totalCopayers +
|
||||
'!=' + inPKR.requiredCopayers);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Merges the public keys of the wallet passed in as a parameter with ours.
|
||||
*
|
||||
* @param {PublicKeyRing} inPKR
|
||||
* @return {boolean} true if there where changes in our internal state
|
||||
*/
|
||||
PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||
var self = this;
|
||||
var hasChanged = false;
|
||||
|
||||
if (self.isComplete())
|
||||
return;
|
||||
|
||||
inPKR.copayersHK.forEach(function(b) {
|
||||
var epk = b.extendedPublicKeyString();
|
||||
var haveIt = _.any(self.copayersHK, function(hk) {
|
||||
return hk.extendedPublicKeyString() === epk;
|
||||
});
|
||||
|
||||
if (!haveIt) {
|
||||
if (self.isComplete()) {
|
||||
throw new Error('trying to add more pubkeys, when PKR isComplete at merge');
|
||||
}
|
||||
var l2 = self.copayersHK.length;
|
||||
self.copayersHK.push(new HK(epk));
|
||||
self._updateBip(l2);
|
||||
if (inPKR.nicknameFor[self.getCopayerId(l2)])
|
||||
self._setNicknameForIndex(l2, inPKR.nicknameFor[self.getCopayerId(l2)]);
|
||||
hasChanged = true;
|
||||
}
|
||||
});
|
||||
return hasChanged;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Merges the indexes for addresses generated with another copy of a list of
|
||||
* HDParams
|
||||
*
|
||||
* @param {HDParams[]} indexes - indexes as received from another sources
|
||||
* @return {boolean} true if the internal state has changed
|
||||
*/
|
||||
PublicKeyRing.prototype.mergeIndexes = function(indexes) {
|
||||
var self = this;
|
||||
var hasChanged = false;
|
||||
|
||||
indexes.forEach(function(theirs) {
|
||||
var mine = self.getHDParams(theirs.copayerIndex);
|
||||
hasChanged |= mine.merge(theirs);
|
||||
});
|
||||
|
||||
return !!hasChanged
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Merges this public key ring with another one, optionally ignoring the
|
||||
* wallet id
|
||||
*
|
||||
* @param {PublicKeyRing} inPkr
|
||||
* @param {boolean} ignoreId
|
||||
* @return {boolean} true if the internal state has changed
|
||||
*/
|
||||
PublicKeyRing.prototype.merge = function(inPKR, ignoreId) {
|
||||
|
||||
this._checkInPKR(inPKR, ignoreId);
|
||||
var hasChanged = false;
|
||||
hasChanged |= this.mergeIndexes(inPKR.indexes);
|
||||
hasChanged |= this._mergePubkeys(inPKR);
|
||||
|
||||
return !!hasChanged;
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = PublicKeyRing;
|
|
@ -1,624 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
var bitcore = require('bitcore');
|
||||
var util = bitcore.util;
|
||||
var Transaction = bitcore.Transaction;
|
||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||
var Script = bitcore.Script;
|
||||
var Key = bitcore.Key;
|
||||
|
||||
var log = require('../util/log');
|
||||
|
||||
var TX_MAX_SIZE_KB = 50;
|
||||
var VERSION = 1;
|
||||
var CORE_FIELDS = ['builderObj', 'inputChainPaths', 'version', 'comment', 'paymentProtocolURL', 'paymentAckMemo'];
|
||||
|
||||
|
||||
function TxProposal(opts) {
|
||||
preconditions.checkArgument(opts);
|
||||
preconditions.checkArgument(opts.inputChainPaths, 'no inputChainPaths');
|
||||
preconditions.checkArgument(opts.builder, 'no builder');
|
||||
preconditions.checkArgument(opts.inputChainPaths, 'no inputChainPaths');
|
||||
|
||||
this.inputChainPaths = opts.inputChainPaths;
|
||||
this.version = opts.version;
|
||||
this.builder = opts.builder;
|
||||
this.createdTs = opts.createdTs;
|
||||
|
||||
// Copayer Actions ( copayerId: timeStamp )
|
||||
this.signedBy = opts.signedBy || {};
|
||||
this.seenBy = opts.seenBy || {};
|
||||
this.rejectedBy = opts.rejectedBy || {};
|
||||
|
||||
this.sentTs = opts.sentTs || null;
|
||||
this.sentTxid = opts.sentTxid || null;
|
||||
this.comment = opts.comment || null;
|
||||
this.readonly = opts.readonly || null;
|
||||
this.merchant = opts.merchant || null;
|
||||
this.paymentAckMemo = opts.paymentAckMemo || null;
|
||||
this.paymentProtocolURL = opts.paymentProtocolURL || null;
|
||||
|
||||
this.resetCache();
|
||||
|
||||
// New Tx Proposal
|
||||
if (_.isEmpty(this.seenBy) && opts.creator) {
|
||||
var now = Date.now();
|
||||
var me = {};
|
||||
me[opts.creator] = now;
|
||||
|
||||
this.seenBy = me;
|
||||
this.signedBy = {};
|
||||
this.creator = opts.creator;
|
||||
this.createdTs = now;
|
||||
if (opts.signWith) {
|
||||
if (!this.sign(opts.signWith, opts.creator))
|
||||
throw new Error('Could not sign generated tx');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TxProposal.prototype._checkPayPro = function() {
|
||||
if (!this.merchant) return;
|
||||
|
||||
if (this.paymentProtocolURL !== this.merchant.request_url)
|
||||
throw new Error('PayPro: Mismatch on Payment URLs');
|
||||
|
||||
if (!this.merchant.outs || this.merchant.outs.length !== 1)
|
||||
throw new Error('PayPro: Unsopported number of outputs');
|
||||
|
||||
if (this.merchant.expires < (this.getSent() || Date.now() / 1000.))
|
||||
throw new Error('PayPro: Request expired');
|
||||
|
||||
if (!this.merchant.total || !this.merchant.outs[0].amountSatStr || !this.merchant.outs[0].address)
|
||||
throw new Error('PayPro: Missing amount');
|
||||
|
||||
var outs = JSON.parse(this.builder.vanilla.outs);
|
||||
if (_.size(outs) != 1)
|
||||
throw new Error('PayPro: Wrong outs in Tx');
|
||||
|
||||
var ppOut = this.merchant.outs[0];
|
||||
var txOut = outs[0];
|
||||
|
||||
if (ppOut.address !== txOut.address)
|
||||
throw new Error('PayPro: Wrong out address in Tx');
|
||||
|
||||
if (ppOut.amountSatStr !== txOut.amountSatStr + '')
|
||||
throw new Error('PayPro: Wrong amount in Tx');
|
||||
|
||||
};
|
||||
|
||||
|
||||
TxProposal.prototype.isFullySigned = function() {
|
||||
return this.builder && this.builder.isFullySigned();
|
||||
};
|
||||
|
||||
|
||||
TxProposal.prototype.getMySignatures = function() {
|
||||
preconditions.checkState(this._mySignatures, 'Still no signatures from us');
|
||||
return _.clone(this._mySignatures);
|
||||
};
|
||||
|
||||
TxProposal.prototype._setMySignatures = function(signaturesBefore) {
|
||||
var mySigs = [];
|
||||
_.each(this.getSignatures(), function(signatures, index) {
|
||||
var diff = _.difference(signatures, signaturesBefore[index]);
|
||||
preconditions.checkState(diff.length == 1, 'more that one signature added!');
|
||||
mySigs.push(diff[0].toString('hex'));
|
||||
})
|
||||
this._mySignatures = mySigs;
|
||||
return;
|
||||
};
|
||||
|
||||
TxProposal.prototype.sign = function(keys, signerId) {
|
||||
var before = this.countSignatures();
|
||||
var signaturesBefore = this.getSignatures();
|
||||
this.builder.sign(keys);
|
||||
|
||||
var signaturesAdded = this.countSignatures() > before;
|
||||
if (signaturesAdded) {
|
||||
this.signedBy[signerId] = Date.now();
|
||||
this._setMySignatures(signaturesBefore);
|
||||
}
|
||||
return signaturesAdded;
|
||||
};
|
||||
|
||||
TxProposal.prototype._check = function() {
|
||||
|
||||
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {
|
||||
throw new Error('Invalid tx proposal');
|
||||
}
|
||||
|
||||
// Should be able to build
|
||||
var tx = this.builder.build();
|
||||
|
||||
var txSize = tx.getSize();
|
||||
if (txSize / 1024 > TX_MAX_SIZE_KB)
|
||||
throw new Error('BIG: Invalid TX proposal. Too big: ' + txSize + ' bytes');
|
||||
|
||||
if (!tx.ins.length)
|
||||
throw new Error('Invalid tx proposal: no ins');
|
||||
|
||||
_.each(tx.ins, function(value, index) {
|
||||
var scriptSig = value.s;
|
||||
if (!scriptSig || !scriptSig.length) {
|
||||
throw new Error('Invalid tx proposal: no signatures');
|
||||
}
|
||||
var hashType = tx.getHashType(index);
|
||||
if (hashType && hashType !== Transaction.SIGHASH_ALL)
|
||||
throw new Error('Invalid tx proposal: bad signatures');
|
||||
});
|
||||
this._checkPayPro();
|
||||
};
|
||||
|
||||
|
||||
TxProposal.prototype.addMerchantData = function(merchantData) {
|
||||
preconditions.checkArgument(merchantData.pr);
|
||||
preconditions.checkArgument(merchantData.request_url);
|
||||
var m = _.clone(merchantData);
|
||||
|
||||
if (!this.paymentProtocolURL)
|
||||
this.paymentProtocolURL = m.request_url;
|
||||
|
||||
// remove unneeded data
|
||||
m.raw = m.pr.pki_data = m.pr.signature = undefined;
|
||||
this.merchant = m;
|
||||
this._checkPayPro();
|
||||
};
|
||||
|
||||
TxProposal.prototype.getSignatures = function() {
|
||||
var ins = this.builder.build().ins;
|
||||
var sigs = _.map(ins, function(value) {
|
||||
var script = new bitcore.Script(value.s);
|
||||
var nchunks = script.chunks.length;
|
||||
return _.map(script.chunks.slice(1, nchunks - 1), function(buffer) {
|
||||
return buffer.toString('hex');
|
||||
});
|
||||
});
|
||||
|
||||
return sigs;
|
||||
};
|
||||
|
||||
TxProposal.prototype.rejectCount = function() {
|
||||
return _.size(this.rejectedBy);
|
||||
};
|
||||
|
||||
|
||||
TxProposal.prototype.isFinallyRejected = function(maxRejectCount) {
|
||||
return this.rejectCount() > maxRejectCount;
|
||||
};
|
||||
|
||||
TxProposal.prototype.isPending = function(maxRejectCount) {
|
||||
preconditions.checkArgument(_.isNumber(maxRejectCount));
|
||||
|
||||
if (this.isFinallyRejected(maxRejectCount) || this.sentTxid)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
TxProposal.prototype._setSigned = function(copayerId) {
|
||||
|
||||
// Sign powns rejected
|
||||
if (this.rejectedBy[copayerId]) {
|
||||
log.info("WARN: a previously rejected transaction was signed by:", copayerId);
|
||||
delete this.rejectedBy[copayerId];
|
||||
}
|
||||
|
||||
this.signedBy[copayerId] = Date.now();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @desc verify signatures of ONE copayer, using an array of signatures for each input
|
||||
*
|
||||
* @param {string[]} signatures, of the same copayer, one for each input
|
||||
* @return {string[]} array for signing pubkeys for each input
|
||||
*/
|
||||
TxProposal.prototype._addSignatureAndVerify = function(signatures) {
|
||||
var self = this;
|
||||
|
||||
var ret = [];
|
||||
var tx = self.builder.build();
|
||||
|
||||
var inputsFullySigned = 0;
|
||||
var newScriptSigs = [];
|
||||
|
||||
this.resetCache();
|
||||
_.each(tx.ins, function(input, index) {
|
||||
var scriptSig = new Script(input.s);
|
||||
|
||||
var info = TxProposal.infoFromRedeemScript(scriptSig);
|
||||
var txSigHash = tx.hashForSignature(info.script, parseInt(index), Transaction.SIGHASH_ALL);
|
||||
var keys = TxProposal.formatKeys(info.keys);
|
||||
var sig = new Buffer(signatures[index], 'hex');
|
||||
|
||||
var hashType = sig[sig.length - 1];
|
||||
if (hashType !== Transaction.SIGHASH_ALL)
|
||||
throw new Error('BADSIG: Invalid signature: Bad hash type');
|
||||
|
||||
var sigRaw = new Buffer(sig.slice(0, sig.length - 1));
|
||||
var signingPubKeyHex = self._verifyOneSignature(keys, sigRaw, txSigHash);
|
||||
if (!signingPubKeyHex)
|
||||
throw new Error('BADSIG: Invalid signatures: invalid for input:' + index);
|
||||
|
||||
// now insert it
|
||||
var keysHex = _.pluck(keys, 'keyHex');
|
||||
var prio = _.indexOf(keysHex, signingPubKeyHex);
|
||||
preconditions.checkState(prio >= 0);
|
||||
|
||||
var currentKeys = self.getSignersPubKeys()[index];
|
||||
|
||||
if (_.indexOf(currentKeys, signingPubKeyHex) >= 0)
|
||||
throw new Error('BADSIG: Already have this signature');
|
||||
|
||||
var currentPrios = _.map(currentKeys, function(key) {
|
||||
var prio = _.indexOf(keysHex, key);
|
||||
preconditions.checkState(prio >= 0);
|
||||
return prio;
|
||||
});
|
||||
|
||||
var insertAt = 0;
|
||||
while (!_.isUndefined(currentPrios[insertAt]) && prio > currentPrios[insertAt])
|
||||
insertAt++;
|
||||
|
||||
// Insert it! (1 is OP_0!)
|
||||
scriptSig.chunks.splice(1 + insertAt, 0, sig);
|
||||
scriptSig.updateBuffer();
|
||||
|
||||
if (info.nreq == currentKeys.length + 1) {
|
||||
inputsFullySigned++;
|
||||
}
|
||||
newScriptSigs.push(scriptSig.buffer);
|
||||
});
|
||||
preconditions.checkState(newScriptSigs.length === tx.ins.length);
|
||||
|
||||
// If we reach here, all signatures are OK, let's update the TX.
|
||||
_.each(tx.ins, function(input, index) {
|
||||
input.s = newScriptSigs[index];
|
||||
|
||||
// just to keep TransactionBuilder updated
|
||||
if (tx.ins.length == inputsFullySigned)
|
||||
self.builder.inputsSigned++;
|
||||
});
|
||||
};
|
||||
|
||||
TxProposal.prototype.resetCache = function() {
|
||||
this.cache = {
|
||||
pubkeysForScript: {},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* addSignature
|
||||
*
|
||||
* @param {string[]} signatures from *ONE* copayer, one signature for each TX input.
|
||||
* @return {boolean} true = signatures added
|
||||
*/
|
||||
TxProposal.prototype.addSignature = function(copayerId, signatures) {
|
||||
preconditions.checkArgument(_.isArray(signatures));
|
||||
|
||||
if (this.isFullySigned())
|
||||
return false;
|
||||
|
||||
var tx = this.builder.build();
|
||||
preconditions.checkArgument(signatures.length === tx.ins.length, 'Wrong number of signatures given');
|
||||
|
||||
this._addSignatureAndVerify(signatures);
|
||||
|
||||
this._setSigned(copayerId);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* getSignersPubKey
|
||||
* @desc get Pubkeys of signers, for each input. this is CPU intensive
|
||||
*
|
||||
* @return {string[][]} array of hashes for signing pubkeys for each input
|
||||
*/
|
||||
TxProposal.prototype.getSignersPubKeys = function(forceUpdate) {
|
||||
var self = this;
|
||||
|
||||
|
||||
var signersPubKey = [];
|
||||
|
||||
if (!self.cache.signersPubKey || forceUpdate) {
|
||||
|
||||
log.debug('PERFORMANCE WARN: Verifying *all* TX signatures:', self.getId());
|
||||
|
||||
var tx = self.builder.build();
|
||||
_.each(tx.ins, function(input, index) {
|
||||
|
||||
if (!self.cache.pubkeysForScript[input.s]) {
|
||||
var scriptSig = new Script(input.s);
|
||||
var signatureCount = scriptSig.countSignatures();
|
||||
|
||||
var info = TxProposal.infoFromRedeemScript(scriptSig);
|
||||
var txSigHash = tx.hashForSignature(info.script, parseInt(index), Transaction.SIGHASH_ALL);
|
||||
var inputSignersPubKey = self.verifySignatures(info.keys, scriptSig, txSigHash);
|
||||
|
||||
// Does scriptSig has strings that are not signatures?
|
||||
if (inputSignersPubKey.length !== signatureCount)
|
||||
throw new Error('Invalid signature');
|
||||
|
||||
self.cache.pubkeysForScript[input.s] = inputSignersPubKey;
|
||||
}
|
||||
|
||||
signersPubKey[index] = self.cache.pubkeysForScript[input.s];
|
||||
});
|
||||
self.cache.signersPubKey = signersPubKey;
|
||||
} else {
|
||||
log.debug('Using signatures verification cache')
|
||||
}
|
||||
|
||||
return self.cache.signersPubKey;
|
||||
};
|
||||
|
||||
TxProposal.prototype.getId = function() {
|
||||
preconditions.checkState(this.builder);
|
||||
|
||||
if (!this.ntxid) {
|
||||
this.ntxid = this.builder.build().getNormalizedHash().toString('hex');
|
||||
}
|
||||
return this.ntxid;
|
||||
};
|
||||
|
||||
TxProposal.prototype.toObj = function() {
|
||||
var o = JSON.parse(JSON.stringify(this));
|
||||
delete o['builder'];
|
||||
delete o['cache'];
|
||||
o.builderObj = this.builder.toObj();
|
||||
return o;
|
||||
};
|
||||
|
||||
|
||||
TxProposal._trim = function(o) {
|
||||
var ret = {};
|
||||
CORE_FIELDS.forEach(function(k) {
|
||||
ret[k] = o[k];
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
TxProposal.fromObj = function(o, forceOpts) {
|
||||
preconditions.checkArgument(o.builderObj);
|
||||
delete o['builder'];
|
||||
forceOpts = forceOpts || {};
|
||||
o.builderObj.opts = o.builderObj.opts || {};
|
||||
|
||||
// force opts is requested.
|
||||
_.each(forceOpts, function(value, key) {
|
||||
o.builderObj.opts[key] = value;
|
||||
});
|
||||
|
||||
// Handle undef fee options
|
||||
if (_.isUndefined(forceOpts.fee) && _.isUndefined(forceOpts.feeSat)) {
|
||||
o.builderObj.opts.fee = undefined;
|
||||
o.builderObj.opts.feeSat = undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
o.builder = TransactionBuilder.fromObj(o.builderObj);
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
return null;
|
||||
}
|
||||
return new TxProposal(o);
|
||||
};
|
||||
|
||||
TxProposal.fromUntrustedObj = function(o, forceOpts) {
|
||||
var trimmed = TxProposal._trim(o);
|
||||
var txp = TxProposal.fromObj(trimmed, forceOpts);
|
||||
if (!txp)
|
||||
throw new Error('Invalid Transaction');
|
||||
|
||||
txp._check();
|
||||
return txp;
|
||||
};
|
||||
|
||||
TxProposal.prototype.toObjTrim = function() {
|
||||
return TxProposal._trim(this.toObj());
|
||||
};
|
||||
|
||||
TxProposal.formatKeys = function(keys) {
|
||||
var ret = [];
|
||||
for (var i in keys) {
|
||||
if (!Buffer.isBuffer(keys[i]))
|
||||
throw new Error('keys must be buffers');
|
||||
|
||||
var k = new Key();
|
||||
k.public = keys[i];
|
||||
ret.push({
|
||||
keyObj: k,
|
||||
keyHex: keys[i].toString('hex'),
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc Verify a single signature, for a given hash, tested against a given list of public keys.
|
||||
* @param keys
|
||||
* @param sigRaw
|
||||
* @param txSigHash
|
||||
* @return {string?} on valid signature, return the signing public key hex representation
|
||||
*/
|
||||
TxProposal.prototype._verifyOneSignature = function(keys, sigRaw, txSigHash) {
|
||||
preconditions.checkArgument(Buffer.isBuffer(txSigHash));
|
||||
preconditions.checkArgument(Buffer.isBuffer(sigRaw));
|
||||
preconditions.checkArgument(_.isArray(keys));
|
||||
preconditions.checkArgument(keys[0].keyObj);
|
||||
|
||||
var signingKey = _.find(keys, function(key) {
|
||||
var ret = false;
|
||||
try {
|
||||
ret = key.keyObj.verifySignatureSync(txSigHash, sigRaw);
|
||||
} catch (e) {};
|
||||
return ret;
|
||||
});
|
||||
|
||||
return signingKey ? signingKey.keyHex : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc verify transaction signatures
|
||||
*
|
||||
* @param inKeys
|
||||
* @param scriptSig
|
||||
* @param txSigHash
|
||||
* @return {string[]} signing pubkeys, in order of apperance
|
||||
*/
|
||||
TxProposal.prototype.verifySignatures = function(inKeys, scriptSig, txSigHash) {
|
||||
preconditions.checkArgument(Buffer.isBuffer(txSigHash));
|
||||
preconditions.checkArgument(inKeys);
|
||||
preconditions.checkState(Buffer.isBuffer(inKeys[0]));
|
||||
var self = this;
|
||||
|
||||
if (scriptSig.chunks[0] !== 0)
|
||||
throw new Error('Invalid scriptSig');
|
||||
|
||||
var keys = TxProposal.formatKeys(inKeys);
|
||||
var ret = [];
|
||||
for (var i = 1; i <= scriptSig.countSignatures(); i++) {
|
||||
var chunk = scriptSig.chunks[i];
|
||||
log.debug('\t Verifying CHUNK:', i);
|
||||
var sigRaw = new Buffer(chunk.slice(0, chunk.length - 1));
|
||||
|
||||
var signingPubKeyHex = self._verifyOneSignature(keys, sigRaw, txSigHash);
|
||||
if (!signingPubKeyHex)
|
||||
throw new Error('Found a signature that is invalid');
|
||||
|
||||
ret.push(signingPubKeyHex);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
TxProposal.infoFromRedeemScript = function(s) {
|
||||
var redeemScript = new Script(s.chunks[s.chunks.length - 1]);
|
||||
if (!redeemScript)
|
||||
throw new Error('Bad scriptSig (no redeemscript)');
|
||||
|
||||
var nreq = nreq = redeemScript.chunks[0] - 80; //see OP_2-OP_16
|
||||
var pubkeys = redeemScript.capture();
|
||||
if (!pubkeys || !pubkeys.length)
|
||||
throw new Error('Bad scriptSig (no pubkeys)');
|
||||
|
||||
return {
|
||||
nreq: nreq,
|
||||
keys: pubkeys,
|
||||
script: redeemScript,
|
||||
};
|
||||
};
|
||||
|
||||
TxProposal.prototype.getSeen = function(copayerId) {
|
||||
return this.seenBy[copayerId];
|
||||
};
|
||||
|
||||
TxProposal.prototype.setSeen = function(copayerId) {
|
||||
if (!this.seenBy[copayerId])
|
||||
this.seenBy[copayerId] = Date.now();
|
||||
};
|
||||
|
||||
TxProposal.prototype.setRejected = function(copayerId) {
|
||||
|
||||
if (this.signedBy[copayerId])
|
||||
throw new Error('Can not reject a signed TX');
|
||||
|
||||
if (!this.rejectedBy[copayerId])
|
||||
this.rejectedBy[copayerId] = Date.now();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
TxProposal.prototype.setSent = function(sentTxid) {
|
||||
this.sentTxid = sentTxid;
|
||||
this.sentTs = Date.now();
|
||||
return this;
|
||||
};
|
||||
|
||||
TxProposal.prototype.getSent = function() {
|
||||
return this.sentTs;
|
||||
}
|
||||
|
||||
TxProposal.prototype.setCopayers = function(pubkeyToCopayerMap) {
|
||||
var newCopayer = {},
|
||||
oldCopayers = {},
|
||||
newSignedBy = {},
|
||||
readOnlyPeers = {},
|
||||
isNew = 1;
|
||||
|
||||
for (var k in this.signedBy) {
|
||||
oldCopayers[k] = 1;
|
||||
isNew = 0;
|
||||
};
|
||||
|
||||
if (isNew == 0) {
|
||||
if (!this.creator || !this.createdTs)
|
||||
throw new Error('Existing TX has no creator');
|
||||
|
||||
if (!this.signedBy[this.creator])
|
||||
throw new Error('Existing TX is not signed by creator');
|
||||
|
||||
|
||||
if (Object.keys(this.signedBy).length === 0)
|
||||
throw new Error('Existing TX has no signatures');
|
||||
}
|
||||
|
||||
|
||||
var iSig = this.getSignersPubKeys();
|
||||
for (var i in iSig) {
|
||||
var copayerId = pubkeyToCopayerMap[iSig[i]];
|
||||
|
||||
if (!copayerId)
|
||||
throw new Error('Found unknown signature')
|
||||
|
||||
if (oldCopayers[copayerId]) {
|
||||
//Already have it. Do nothing
|
||||
} else {
|
||||
newCopayer[copayerId] = Date.now();
|
||||
delete oldCopayers[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(newCopayer).length > 1)
|
||||
throw new Error('New TX must have only 1 new signature');
|
||||
|
||||
// Handler creator / createdTs.
|
||||
// from senderId, and must be signed by senderId * DISABLED*
|
||||
//
|
||||
if (isNew) {
|
||||
this.creator = Object.keys(newCopayer)[0];
|
||||
this.seenBy[this.creator] = this.createdTs = Date.now();
|
||||
}
|
||||
|
||||
//Ended. Update this
|
||||
_.extend(this.signedBy, newCopayer);
|
||||
|
||||
// signedBy has preference over rejectedBy
|
||||
for (var i in this.signedBy) {
|
||||
delete this.rejectedBy[i];
|
||||
}
|
||||
|
||||
return Object.keys(newCopayer);
|
||||
};
|
||||
|
||||
//This should be on bitcore / Transaction
|
||||
TxProposal.prototype.countSignatures = function() {
|
||||
var tx = this.builder.build();
|
||||
var ret = 0;
|
||||
for (var i in tx.ins) {
|
||||
ret += tx.countInputSignatures(i);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
module.exports = TxProposal;
|
|
@ -1,156 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
var bitcore = require('bitcore');
|
||||
var util = bitcore.util;
|
||||
var Transaction = bitcore.Transaction;
|
||||
var Script = bitcore.Script;
|
||||
var Key = bitcore.Key;
|
||||
var buffertools = bitcore.buffertools;
|
||||
|
||||
var log = require('../util/log');
|
||||
var TxProposal = require('./TxProposal');;
|
||||
|
||||
function TxProposals(opts) {
|
||||
opts = opts || {};
|
||||
this.walletId = opts.walletId;
|
||||
this.network = opts.networkName === 'livenet' ?
|
||||
bitcore.networks.livenet : bitcore.networks.testnet;
|
||||
this.txps = {};
|
||||
}
|
||||
|
||||
// fromObj => from a trusted source
|
||||
TxProposals.fromObj = function(o, forceOpts) {
|
||||
var ret = new TxProposals({
|
||||
networkName: o.networkName,
|
||||
walletId: o.walletId,
|
||||
});
|
||||
|
||||
o.txps.forEach(function(o2) {
|
||||
try {
|
||||
var t = TxProposal.fromObj(o2, forceOpts);
|
||||
} catch (e) {
|
||||
log.info('Ignoring corrupted TxProposal:', o2, e);
|
||||
}
|
||||
if (t && t.builder) {
|
||||
var id = t.getId();
|
||||
ret.txps[id] = t;
|
||||
}
|
||||
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
TxProposals.prototype.length = function() {
|
||||
return Object.keys(this.txps).length;
|
||||
};
|
||||
|
||||
|
||||
TxProposals.prototype.getNtxidsSince = function(sinceTs) {
|
||||
preconditions.checkArgument(sinceTs);
|
||||
var ret = [];
|
||||
|
||||
for (var ii in this.txps) {
|
||||
var txp = this.txps[ii];
|
||||
if (txp.createdTs >= sinceTs)
|
||||
ret.push(ii);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
|
||||
TxProposals.prototype.getNtxids = function() {
|
||||
return Object.keys(this.txps);
|
||||
};
|
||||
|
||||
TxProposals.prototype.deleteOne = function(ntxid) {
|
||||
preconditions.checkState(this.txps[ntxid], 'Unknown TXP: ' + ntxid);
|
||||
delete this.txps[ntxid];
|
||||
};
|
||||
|
||||
TxProposals.prototype.deleteAll = function() {
|
||||
this.txps = {};
|
||||
};
|
||||
|
||||
TxProposals.prototype.deletePending = function(maxRejectCount) {
|
||||
for (var ntxid in this.txps) {
|
||||
if (this.txps[ntxid].isPending(maxRejectCount))
|
||||
delete this.txps[ntxid];
|
||||
};
|
||||
};
|
||||
|
||||
TxProposals.prototype.toObj = function() {
|
||||
var ret = [];
|
||||
for (var id in this.txps) {
|
||||
var t = this.txps[id];
|
||||
if (!t.sent)
|
||||
ret.push(t.toObj());
|
||||
}
|
||||
return {
|
||||
txps: ret,
|
||||
walletId: this.walletId,
|
||||
networkName: this.network.name,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Add a LOCALLY CREATED (trusted) tx proposal
|
||||
TxProposals.prototype.add = function(txp) {
|
||||
var ntxid = txp.getId();
|
||||
this.txps[ntxid] = txp;
|
||||
return ntxid;
|
||||
};
|
||||
|
||||
|
||||
TxProposals.prototype.exist = function(ntxid) {
|
||||
return this.txps[ntxid] ? true : false;
|
||||
};
|
||||
|
||||
|
||||
TxProposals.prototype.get = function(ntxid) {
|
||||
var ret = this.txps[ntxid];
|
||||
if (!ret)
|
||||
throw new Error('Unknown TXP: ' + ntxid);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
//returns the unspent txid-vout used in PENDING Txs
|
||||
TxProposals.prototype.getUsedUnspent = function(maxRejectCount) {
|
||||
var ret = {};
|
||||
var self = this;
|
||||
|
||||
_.each(this.txps, function(txp) {
|
||||
if (!txp.isPending(maxRejectCount))
|
||||
return
|
||||
|
||||
_.each(txp.builder.getSelectedUnspent(), function(u) {
|
||||
ret[u.txid + ',' + u.vout] = 1;
|
||||
});
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* purge
|
||||
*
|
||||
* @param deleteAll
|
||||
* @return {undefined}
|
||||
*/
|
||||
TxProposals.prototype.purge = function(deleteAll, maxRejectCount) {
|
||||
var m = _.size(this.txps);
|
||||
|
||||
if (deleteAll) {
|
||||
this.deleteAll();
|
||||
} else {
|
||||
this.deletePending(maxRejectCount);
|
||||
}
|
||||
var n = _.size(this.txps);
|
||||
return m - n;
|
||||
};
|
||||
|
||||
module.exports = TxProposals;
|
2846
js/models/Wallet.js
2846
js/models/Wallet.js
File diff suppressed because it is too large
Load Diff
|
@ -1,116 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
function WalletLock(storage, walletId, timeoutMin) {
|
||||
preconditions.checkArgument(storage);
|
||||
preconditions.checkArgument(walletId);
|
||||
|
||||
this.storage = storage;
|
||||
this.timeoutMin = timeoutMin || 5;
|
||||
this.key = WalletLock._keyFor(walletId);
|
||||
}
|
||||
|
||||
WalletLock.prototype.getSessionId = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
|
||||
self.sessionStorage.getItem('sessionId', function(sessionId) {
|
||||
if (sessionId)
|
||||
return cb(sessionId);
|
||||
|
||||
sessionId = bitcore.SecureRandom.getRandomBuffer(8).toString('hex');
|
||||
self.sessionStorage.setItem('sessionId', sessionId, function() {
|
||||
return cb(sessionId);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
WalletLock.prototype.init = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
|
||||
self.storage.getSessionId(function(sid) {
|
||||
preconditions.checkState(sid);
|
||||
|
||||
self.sessionId = sid;
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
WalletLock._keyFor = function(walletId) {
|
||||
return 'lock' + '::' + walletId;
|
||||
};
|
||||
|
||||
WalletLock.prototype._isLockedByOther = function(cb) {
|
||||
var self = this;
|
||||
|
||||
this.storage.getGlobal(this.key, function(json) {
|
||||
var wl = json ? JSON.parse(json) : null;
|
||||
if (!wl || !wl.expireTs)
|
||||
return cb(false);
|
||||
|
||||
var expiredSince = Date.now() - wl.expireTs;
|
||||
if (expiredSince >= 0)
|
||||
return cb(false);
|
||||
|
||||
var isMyself = wl.sessionId === self.sessionId;
|
||||
|
||||
if (isMyself)
|
||||
return cb(false);
|
||||
|
||||
// Seconds remainding
|
||||
return cb(parseInt(-expiredSince / 1000));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
WalletLock.prototype._setLock = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
preconditions.checkState(this.sessionId);
|
||||
var self = this;
|
||||
|
||||
this.storage.setGlobal(this.key, {
|
||||
sessionId: this.sessionId,
|
||||
expireTs: Date.now() + this.timeoutMin * 60 * 1000,
|
||||
}, function() {
|
||||
|
||||
cb(null);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
WalletLock.prototype._doKeepAlive = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
preconditions.checkState(this.sessionId);
|
||||
|
||||
var self = this;
|
||||
|
||||
this._isLockedByOther(function(t) {
|
||||
if (t)
|
||||
return cb(new Error('LOCKED: Wallet is locked for ' + t + ' srcs'));
|
||||
|
||||
self._setLock(cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
WalletLock.prototype.keepAlive = function(cb) {
|
||||
var self = this;
|
||||
|
||||
if (!self.sessionId) {
|
||||
return self.init(self._doKeepAlive.bind(self, cb));
|
||||
};
|
||||
|
||||
return this._doKeepAlive(cb);
|
||||
};
|
||||
|
||||
|
||||
WalletLock.prototype.release = function(cb) {
|
||||
this.storage.removeGlobal(this.key, cb);
|
||||
};
|
||||
|
||||
|
||||
module.exports = WalletLock;
|
|
@ -1,59 +0,0 @@
|
|||
var cryptoUtil = require('../util/crypto');
|
||||
var InsightStorage = require('./InsightStorage');
|
||||
var inherits = require('inherits');
|
||||
var log = require('../util/log');
|
||||
var SEPARATOR = '%^#@';
|
||||
|
||||
function EncryptedInsightStorage(config) {
|
||||
InsightStorage.apply(this, [config]);
|
||||
}
|
||||
inherits(EncryptedInsightStorage, InsightStorage);
|
||||
|
||||
EncryptedInsightStorage.prototype._brokenDecrypt = function(body) {
|
||||
var key = cryptoUtil.kdf(this.password + this.email, 'mjuBtGybi/4=', 100);
|
||||
log.debug('Trying legacy decrypt')
|
||||
var decryptedJson = cryptoUtil.decrypt(key, body);
|
||||
return decryptedJson;
|
||||
};
|
||||
|
||||
EncryptedInsightStorage.prototype.resendVerificationEmail = function(callback) {
|
||||
InsightStorage.prototype.resendVerificationEmail.apply(this, [callback]);
|
||||
};
|
||||
|
||||
EncryptedInsightStorage.prototype.getItem = function(name, callback) {
|
||||
var self = this;
|
||||
InsightStorage.prototype.getItem.apply(this, [name,
|
||||
function(err, body, headers) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
var decryptedJson = cryptoUtil.decrypt(self.email + SEPARATOR + self.password, body);
|
||||
|
||||
if (!decryptedJson) {
|
||||
log.debug('Could not decrypt value using current decryption schema');
|
||||
decryptedJson = self._brokenDecrypt(body);
|
||||
}
|
||||
|
||||
if (!decryptedJson) {
|
||||
log.debug('Could not decrypt value.');
|
||||
return callback('PNOTFOUND');
|
||||
}
|
||||
return callback(null, decryptedJson, headers);
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
||||
EncryptedInsightStorage.prototype.setItem = function(name, value, callback) {
|
||||
var record = cryptoUtil.encrypt(this.email + SEPARATOR + this.password, value);
|
||||
InsightStorage.prototype.setItem.apply(this, [name, record, callback]);
|
||||
};
|
||||
|
||||
EncryptedInsightStorage.prototype.removeItem = function(name, callback) {
|
||||
InsightStorage.prototype.removeItem.apply(this, [name, callback]);
|
||||
};
|
||||
|
||||
EncryptedInsightStorage.prototype.clear = function(callback) {
|
||||
InsightStorage.prototype.clear.apply(this, [callback]);
|
||||
};
|
||||
|
||||
module.exports = EncryptedInsightStorage;
|
|
@ -1,77 +0,0 @@
|
|||
var cryptoUtil = require('../util/crypto');
|
||||
var log = require('../util/log');
|
||||
var LocalStorage = require('./LocalStorage');
|
||||
var inherits = require('inherits');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
var SEPARATOR = '@#$';
|
||||
|
||||
function EncryptedLocalStorage(config) {
|
||||
LocalStorage.apply(this, [config]);
|
||||
}
|
||||
inherits(EncryptedLocalStorage, LocalStorage);
|
||||
|
||||
|
||||
EncryptedLocalStorage.prototype._brokenDecrypt = function(body) {
|
||||
var key = cryptoUtil.kdf(this.password + this.email, 'mjuBtGybi/4=', 100);
|
||||
log.debug('Trying legacy decrypt')
|
||||
var decryptedJson = cryptoUtil.decrypt(key, body);
|
||||
return decryptedJson;
|
||||
};
|
||||
|
||||
|
||||
|
||||
EncryptedLocalStorage.prototype._brokenDecryptUndef = function(body) {
|
||||
var badkey = undefined + SEPARATOR + undefined;
|
||||
return cryptoUtil.decrypt(badkey, body);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
EncryptedLocalStorage.prototype.getItem = function(name, callback) {
|
||||
var self = this;
|
||||
preconditions.checkState(self.email);
|
||||
|
||||
LocalStorage.prototype.getItem.apply(this, [name,
|
||||
function(err, body) {
|
||||
|
||||
|
||||
var decryptedJson = cryptoUtil.decrypt(self.email + SEPARATOR + self.password, body);
|
||||
if (!decryptedJson) {
|
||||
log.debug('Could not decrypt value using current decryption schema');
|
||||
decryptedJson = self._brokenDecrypt(body);
|
||||
}
|
||||
|
||||
if (!decryptedJson) {
|
||||
decryptedJson = self._brokenDecryptUndef(body);
|
||||
}
|
||||
|
||||
if (!decryptedJson) {
|
||||
log.debug('Could not decrypt value.');
|
||||
return callback('PNOTFOUND');
|
||||
}
|
||||
|
||||
return callback(null, decryptedJson);
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
||||
EncryptedLocalStorage.prototype.setItem = function(name, value, callback) {
|
||||
if (!_.isString(value)) {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
var record = cryptoUtil.encrypt(this.email + SEPARATOR + this.password, value);
|
||||
LocalStorage.prototype.setItem.apply(this, [name, record, callback]);
|
||||
};
|
||||
|
||||
EncryptedLocalStorage.prototype.removeItem = function(name, callback) {
|
||||
LocalStorage.prototype.removeItem.apply(this, [name, callback]);
|
||||
};
|
||||
|
||||
EncryptedLocalStorage.prototype.clear = function(callback) {
|
||||
LocalStorage.prototype.clear.apply(this, [callback]);
|
||||
};
|
||||
|
||||
|
||||
module.exports = EncryptedLocalStorage;
|
|
@ -1,301 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var loaded = 0;
|
||||
var SCOPES = 'https://www.googleapis.com/auth/drive';
|
||||
var log = require('../util/log');
|
||||
|
||||
function GoogleDrive(config) {
|
||||
preconditions.checkArgument(config && config.clientId, 'No clientId at GoogleDrive config');
|
||||
|
||||
this.clientId = config.clientId;
|
||||
this.home = config.home || 'copay';
|
||||
this.idCache = {};
|
||||
|
||||
this.type = 'DB';
|
||||
|
||||
this.scripts = [{
|
||||
then: this.initLoaded.bind(this),
|
||||
src: 'https://apis.google.com/js/client.js?onload=InitGoogleDrive'
|
||||
}];
|
||||
|
||||
this.isReady = false;
|
||||
this.useImmediate = true;
|
||||
this.ts = 100;
|
||||
};
|
||||
|
||||
window.InitGoogleDrive = function() {
|
||||
log.debug('googleDrive loadeded'); //TODO
|
||||
loaded = 1;
|
||||
};
|
||||
|
||||
GoogleDrive.prototype.init = function() {};
|
||||
|
||||
/**
|
||||
* Called when the client library is loaded to start the auth flow.
|
||||
*/
|
||||
GoogleDrive.prototype.initLoaded = function() {
|
||||
if (!loaded) {
|
||||
window.setTimeout(this.initLoaded.bind(this), 500);
|
||||
} else {
|
||||
window.setTimeout(this.checkAuth.bind(this), 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user has authorized the application.
|
||||
*/
|
||||
GoogleDrive.prototype.checkAuth = function() {
|
||||
|
||||
log.debug('Google Drive: Checking Auth');
|
||||
gapi.auth.authorize({
|
||||
'client_id': this.clientId,
|
||||
'scope': SCOPES,
|
||||
'immediate': this.useImmediate,
|
||||
},
|
||||
this.handleAuthResult.bind(this));
|
||||
};
|
||||
|
||||
GoogleDrive.prototype.setCredentils = function(email, password, opts, callback) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when authorization server replies.
|
||||
*/
|
||||
GoogleDrive.prototype.handleAuthResult = function(authResult) {
|
||||
var self = this;
|
||||
log.debug('Google Drive: authResult', authResult); //TODO
|
||||
|
||||
if (authResult.error) {
|
||||
if (authResult.error) {
|
||||
self.useImmediate = false;
|
||||
return this.checkAuth();
|
||||
};
|
||||
throw new Error(authResult.error);
|
||||
}
|
||||
|
||||
gapi.client.load('drive', 'v2', function() {
|
||||
self.isReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
GoogleDrive.prototype.checkReady = function() {
|
||||
if (!this.isReady)
|
||||
throw new Error('goggle drive is not ready!');
|
||||
};
|
||||
|
||||
GoogleDrive.prototype._httpGet = function(theUrl) {
|
||||
var accessToken = gapi.auth.getToken().access_token;
|
||||
var xmlHttp = null;
|
||||
|
||||
xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("GET", theUrl, false);
|
||||
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
|
||||
xmlHttp.send(null);
|
||||
return xmlHttp.responseText;
|
||||
}
|
||||
|
||||
GoogleDrive.prototype.createItem = function(name, value, callback) {
|
||||
this.getItem(name, function(err, retrieved) {
|
||||
if (err || !retrieved) {
|
||||
return this.setItem(name, value, callback);
|
||||
} else {
|
||||
return callback('EEXISTS');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
GoogleDrive.prototype.getItem = function(k, cb) {
|
||||
//console.log('[googleDrive.js.95:getItem:]', k); //TODO
|
||||
var self = this;
|
||||
|
||||
self.checkReady();
|
||||
self._idForName(k, function(kId) {
|
||||
// console.log('[googleDrive.js.89:kId:]', kId); //TODO
|
||||
if (!kId)
|
||||
return cb(null);
|
||||
|
||||
|
||||
var args = {
|
||||
'path': '/drive/v2/files/' + kId,
|
||||
'method': 'GET',
|
||||
};
|
||||
// console.log('[googleDrive.js.95:args:]', args); //TODO
|
||||
|
||||
var request = gapi.client.request(args);
|
||||
request.execute(function(res) {
|
||||
// console.log('[googleDrive.js.175:res:]', res); //TODO
|
||||
if (!res || !res.downloadUrl)
|
||||
return cb(null);
|
||||
|
||||
return cb(self._httpGet(res.downloadUrl));
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
GoogleDrive.prototype.setItem = function(k, v, cb) {
|
||||
// console.log('[googleDrive.js.111:setItem:]', k, v); //TODO
|
||||
var self = this;
|
||||
|
||||
self.checkReady();
|
||||
self._idForName(this.home, function(parentId) {
|
||||
preconditions.checkState(parentId);
|
||||
// console.log('[googleDrive.js.118:parentId:]', parentId); //TODO
|
||||
self._idForName(k, function(kId) {
|
||||
|
||||
// console.log('[googleDrive.js.105]', parentId, kId); //TODO
|
||||
|
||||
|
||||
var boundary = '-------314159265358979323846';
|
||||
var delimiter = "\r\n--" + boundary + "\r\n";
|
||||
var close_delim = "\r\n--" + boundary + "--";
|
||||
|
||||
var metadata = {
|
||||
'title': k,
|
||||
'mimeType': 'application/octet-stream',
|
||||
'parents': [{
|
||||
'id': parentId
|
||||
}],
|
||||
};
|
||||
|
||||
var base64Data = btoa(v);
|
||||
var multipartRequestBody =
|
||||
delimiter +
|
||||
'Content-Type: application/json\r\n\r\n' +
|
||||
JSON.stringify(metadata) +
|
||||
delimiter +
|
||||
'Content-Type: application/octet-stream \r\n' +
|
||||
'Content-Transfer-Encoding: base64\r\n' +
|
||||
'\r\n' +
|
||||
base64Data +
|
||||
close_delim;
|
||||
|
||||
var args = {
|
||||
'path': '/upload/drive/v2/files' + (kId ? '/' + kId : ''),
|
||||
'method': kId ? 'PUT' : 'POST',
|
||||
'params': {
|
||||
'uploadType': 'multipart',
|
||||
},
|
||||
'headers': {
|
||||
'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
|
||||
},
|
||||
'body': multipartRequestBody
|
||||
}
|
||||
// console.log('[googleDrive.js.148:args:]', args); //TODO
|
||||
|
||||
var request = gapi.client.request(args);
|
||||
request.execute(function(ret) {
|
||||
return cb(ret.kind === 'drive#file' ? null : new Error('error saving file on drive'));
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
GoogleDrive.prototype.removeItem = function(k, cb) {
|
||||
var self = this;
|
||||
|
||||
self.checkReady();
|
||||
self._idForName(this.home, function(parentId) {
|
||||
preconditions.checkState(parentId);
|
||||
self._idForName(k, function(kId) {
|
||||
|
||||
var args = {
|
||||
'path': '/drive/v2/files/' + kId,
|
||||
'method': 'DELETE',
|
||||
};
|
||||
var request = gapi.client.request(args);
|
||||
request.execute(function() {
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
GoogleDrive.prototype.clear = function() {
|
||||
this.checkReady();
|
||||
throw new Error('clear not implemented');
|
||||
};
|
||||
|
||||
|
||||
GoogleDrive.prototype._mkdir = function(cb) {
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
|
||||
log.debug('Creating drive folder ' + this.home);
|
||||
|
||||
var request = gapi.client.request({
|
||||
'path': '/drive/v2/files',
|
||||
'method': 'POST',
|
||||
'body': JSON.stringify({
|
||||
'title': this.home,
|
||||
'mimeType': "application/vnd.google-apps.folder",
|
||||
}),
|
||||
});
|
||||
request.execute(function() {
|
||||
self._idForName(self.home, cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
GoogleDrive.prototype._idForName = function(name, cb) {
|
||||
// console.log('[googleDrive.js.199:_idForName:]', name); //TODO
|
||||
preconditions.checkArgument(name);
|
||||
preconditions.checkArgument(cb);
|
||||
var self = this;
|
||||
|
||||
if (!self.isReady) {
|
||||
log.debug('Waiting for Google Drive');
|
||||
self.ts = self.ts * 1.5;
|
||||
return setTimeout(self._idForName.bind(self, name, cb), self.ts);
|
||||
}
|
||||
|
||||
if (self.idCache[name]) {
|
||||
// console.log('[googleDrive.js.212:] FROM CACHE', name, self.idCache[name]); //TODO
|
||||
return cb(self.idCache[name]);
|
||||
}
|
||||
|
||||
log.debug('GoogleDrive Querying for: ', name); //TODO
|
||||
var args;
|
||||
|
||||
var idParent = name == this.home ? 'root' : self.idCache[this.home];
|
||||
|
||||
if (!idParent) {
|
||||
return self._mkdir(function() {
|
||||
self._idForName(name, cb);
|
||||
});
|
||||
}
|
||||
// console.log('[googleDrive.js.177:idParent:]', idParent); //TODO
|
||||
preconditions.checkState(idParent);
|
||||
|
||||
args = {
|
||||
'path': '/drive/v2/files',
|
||||
'method': 'GET',
|
||||
'params': {
|
||||
'q': "title='" + name + "' and trashed = false and '" + idParent + "' in parents",
|
||||
}
|
||||
};
|
||||
|
||||
var request = gapi.client.request(args);
|
||||
request.execute(function(res) {
|
||||
var i = res.items && res.items[0] ? res.items[0].id : false;
|
||||
if (i)
|
||||
self.idCache[name] = i;
|
||||
// console.log('[googleDrive.js.238] CACHING ' + name + ':' + i); //TODO
|
||||
return cb(self.idCache[name]);
|
||||
});
|
||||
};
|
||||
|
||||
GoogleDrive.prototype._checkHomeDir = function(cb) {
|
||||
var self = this;
|
||||
|
||||
this._idForName(this.home, function(homeId) {
|
||||
if (!homeId)
|
||||
return self._mkdir(cb);
|
||||
|
||||
return cb(homeId);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = GoogleDrive;
|
|
@ -1,287 +0,0 @@
|
|||
var request = require('request');
|
||||
var cryptoUtil = require('../util/crypto');
|
||||
var bitcore = require('bitcore');
|
||||
var buffers = require('buffer');
|
||||
var querystring = require('querystring');
|
||||
var Identity = require('../models/Identity');
|
||||
var log = require('../util/log');
|
||||
|
||||
var SEPARATOR = '|';
|
||||
|
||||
function InsightStorage(config) {
|
||||
this.type = 'DB';
|
||||
this.storeUrl = config.url || 'https://insight.bitpay.com:443/api/email',
|
||||
this.request = config.request || request;
|
||||
|
||||
this.iterations = config.iterations || 1000;
|
||||
this.salt = config.salt || 'jBbYTj8zTrOt6V';
|
||||
}
|
||||
|
||||
InsightStorage.prototype.init = function() {};
|
||||
|
||||
InsightStorage.prototype.setCredentials = function(email, password, opts) {
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
this._cachedKey = null;
|
||||
};
|
||||
|
||||
InsightStorage.prototype.createItem = function(name, value, callback) {
|
||||
var self = this;
|
||||
|
||||
this.getItem(name, function(err, retrieved) {
|
||||
if (err || !retrieved) {
|
||||
return self.setItem(name, value, callback);
|
||||
} else {
|
||||
return callback('EEXISTS');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
InsightStorage.prototype.resendVerificationEmail = function (callback) {
|
||||
var passphrase = this.getPassphrase();
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
var resendUrl = this.storeUrl + '/resend_email';
|
||||
|
||||
log.debug('Resending verification email: ' + this.email);
|
||||
this.request.get({
|
||||
url: resendUrl,
|
||||
headers: {
|
||||
'Authorization': authHeader
|
||||
},
|
||||
body: null,
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode === 409) {
|
||||
return callback('BADCREDENTIALS: Invalid username or password');
|
||||
} else if (response.statusCode !== 200) {
|
||||
return callback('Unable to process the request');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
function mayBeOldPassword(password) {
|
||||
// Test for base64
|
||||
return /^[a-zA-Z0-9\/=\+]+$/.test(password);
|
||||
}
|
||||
|
||||
InsightStorage.prototype.getItem = function(name, callback) {
|
||||
var passphrase = this.getPassphrase();
|
||||
var self = this;
|
||||
|
||||
this._makeGetRequest(passphrase, name, function(err, body, headers) {
|
||||
if (err) log.warn(err);
|
||||
if (err && err.indexOf('PNOTFOUND') !== -1 && mayBeOldPassword(self.password)) {
|
||||
return self._brokenGetItem(name, callback);
|
||||
}
|
||||
return callback(err, body, headers);
|
||||
});
|
||||
};
|
||||
|
||||
/* This key need to have DIFFERENT
|
||||
* settings(salt,iterations) than the kdf for wallet/profile encryption
|
||||
* in Encrpted*Storage. The user should be able
|
||||
* to change the settings on config.js to modify salt / iterations
|
||||
* for encryption, but
|
||||
* mantain the same key & passphrase. This is why those settings are
|
||||
* not shared with encryption
|
||||
*/
|
||||
InsightStorage.prototype.getKey = function() {
|
||||
if (!this._cachedKey) {
|
||||
this._cachedKey = cryptoUtil.kdf(this.password + SEPARATOR + this.email, this.salt, this.iterations);
|
||||
}
|
||||
return this._cachedKey;
|
||||
};
|
||||
|
||||
InsightStorage.prototype.getPassphrase = function() {
|
||||
return bitcore.util.twoSha256(this.getKey()).toString('base64');
|
||||
};
|
||||
|
||||
/**
|
||||
* XmlHttpRequest's getAllResponseHeaders() method returns a string of response
|
||||
* headers according to the format described here:
|
||||
* http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method
|
||||
* This method parses that string into a user-friendly key/value pair object.
|
||||
*/
|
||||
InsightStorage.parseResponseHeaders = function (headerStr) {
|
||||
var headers = {};
|
||||
if (!headerStr) {
|
||||
return headers;
|
||||
}
|
||||
var headerPairs = headerStr.split('\u000d\u000a');
|
||||
for (var i = 0, len = headerPairs.length; i < len; i++) {
|
||||
var headerPair = headerPairs[i];
|
||||
var index = headerPair.indexOf('\u003a\u0020');
|
||||
if (index > 0) {
|
||||
var key = headerPair.substring(0, index);
|
||||
var val = headerPair.substring(index + 2);
|
||||
headers[key] = val;
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
InsightStorage.prototype._makeGetRequest = function(passphrase, key, callback) {
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
var retrieveUrl = this.storeUrl + '/retrieve';
|
||||
var getParams = {
|
||||
url: retrieveUrl + '?' + querystring.encode({
|
||||
key: key,
|
||||
rand: Math.random() // prevent cache
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': authHeader
|
||||
}
|
||||
};
|
||||
this.request.get(getParams,
|
||||
function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode === 403) {
|
||||
return callback('PNOTFOUND: Profile not found');
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
return callback('Unable to read item from insight');
|
||||
}
|
||||
return callback(null, body, InsightStorage.parseResponseHeaders(response.getAllResponseHeaders()));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
InsightStorage.prototype._brokenGetItem = function(name, callback) {
|
||||
var passphrase = this._makeBrokenSecret();
|
||||
var self = this;
|
||||
log.debug('using legacy get');
|
||||
this._makeGetRequest(passphrase, name, function(err, body) {
|
||||
if (!err) {
|
||||
return self._changePassphrase(function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null, body);
|
||||
});
|
||||
}
|
||||
return callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
InsightStorage.prototype._makeBrokenSecret = function() {
|
||||
var key = cryptoUtil.kdf(this.password + this.email, 'mjuBtGybi/4=', 100);
|
||||
return cryptoUtil.kdf(key, this.password, 100);
|
||||
};
|
||||
|
||||
InsightStorage.prototype._changePassphrase = function(callback) {
|
||||
var passphrase = this._makeBrokenSecret();
|
||||
var newPassphrase = this.getPassphrase();
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
|
||||
var url = this.storeUrl + '/change_passphrase';
|
||||
this.request.post({
|
||||
url: url,
|
||||
headers: {
|
||||
'Authorization': authHeader
|
||||
},
|
||||
body: querystring.encode({
|
||||
newPassphrase: newPassphrase
|
||||
})
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode === 409) {
|
||||
return callback('BADCREDENTIALS: Invalid username or password');
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
return callback('Unable to store data on insight');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
InsightStorage.prototype.setItem = function(name, value, callback) {
|
||||
var passphrase = this.getPassphrase();
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
var registerUrl = this.storeUrl + '/save';
|
||||
|
||||
log.debug('setItem ' + name + ' size:'+ (value.length/1024).toFixed(1) + 'kb' );
|
||||
this.request.post({
|
||||
url: registerUrl,
|
||||
headers: {
|
||||
'Authorization': authHeader
|
||||
},
|
||||
body: querystring.encode({
|
||||
key: name,
|
||||
record: value
|
||||
})
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode === 409) {
|
||||
return callback('BADCREDENTIALS: Invalid username or password');
|
||||
} else if (response.statusCode === 406) {
|
||||
return callback('OVERQUOTA: Quota exceeded');
|
||||
} else if (response.statusCode === 501) {
|
||||
return callback('EMAILERROR: Error sending verification email');
|
||||
} else if (response.statusCode !== 200) {
|
||||
return callback('Unable to store data on insight');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
InsightStorage.prototype.removeItem = function(key, callback) {
|
||||
var passphrase = this.getPassphrase();
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
var deleteUrl = this.storeUrl + '/delete/item';
|
||||
var getParams = {
|
||||
url: deleteUrl + '?' + querystring.encode({
|
||||
key: key
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': authHeader
|
||||
}
|
||||
};
|
||||
log.debug('Erasing: ' + key);
|
||||
this.request.get(getParams, function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode === 409) {
|
||||
return callback('BADCREDENTIALS: Invalid username or password');
|
||||
} else if (response.statusCode !== 200) {
|
||||
return callback('Unable to remove data on insight');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
InsightStorage.prototype.clear = function(callback) {
|
||||
var passphrase = this.getPassphrase();
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
var deleteUrl = this.storeUrl + '/delete/profile';
|
||||
|
||||
log.debug('Clearing storage for: ' + this.email);
|
||||
this.request.post({
|
||||
url: deleteUrl,
|
||||
headers: {
|
||||
'Authorization': authHeader
|
||||
},
|
||||
body: null,
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode === 409) {
|
||||
return callback('BADCREDENTIALS: Invalid username or password');
|
||||
} else if (response.statusCode !== 200) {
|
||||
return callback('Unable to remove data on insight');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = InsightStorage;
|
|
@ -1,106 +0,0 @@
|
|||
'use strict';
|
||||
var _ = require('lodash');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var isChromeApp = typeof window !== "undefined" && window.chrome && chrome.runtime && chrome.runtime.id;
|
||||
|
||||
|
||||
function LocalStorage(opts) {
|
||||
this.type = 'DB';
|
||||
opts = opts || {};
|
||||
|
||||
|
||||
|
||||
|
||||
this.ls = opts.ls ||
|
||||
((typeof localStorage !== "undefined") ? localStorage : null);
|
||||
|
||||
if (isChromeApp && !this.ls) {
|
||||
this.ls = localStorage = chrome.storage.local;
|
||||
window.localStorage = chrome.storage.local;
|
||||
}
|
||||
|
||||
preconditions.checkState(this.ls,
|
||||
'localstorage not available, cannot run plugin');
|
||||
};
|
||||
|
||||
LocalStorage.prototype.init = function() {};
|
||||
|
||||
LocalStorage.prototype.setCredentials = function(email, password, opts) {
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
};
|
||||
|
||||
LocalStorage.prototype.getItem = function(k, cb) {
|
||||
if (isChromeApp) {
|
||||
chrome.storage.local.get(k,
|
||||
function(data) {
|
||||
//TODO check for errors
|
||||
return cb(null, data[k]);
|
||||
});
|
||||
} else {
|
||||
return cb(null, this.ls.getItem(k));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as setItem, but fails if an item already exists
|
||||
*/
|
||||
LocalStorage.prototype.createItem = function(name, value, callback) {
|
||||
var self = this;
|
||||
self.getItem(name,
|
||||
function(err, data) {
|
||||
if (data) {
|
||||
return callback('EEXISTS');
|
||||
} else {
|
||||
return self.setItem(name, value, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
LocalStorage.prototype.setItem = function(k, v, cb) {
|
||||
if (isChromeApp) {
|
||||
var obj = {};
|
||||
obj[k] = v;
|
||||
|
||||
chrome.storage.local.set(obj, cb);
|
||||
} else {
|
||||
this.ls.setItem(k, v);
|
||||
return cb();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LocalStorage.prototype.removeItem = function(k, cb) {
|
||||
if (isChromeApp) {
|
||||
chrome.storage.local.remove(k, cb);
|
||||
} else {
|
||||
this.ls.removeItem(k);
|
||||
return cb();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LocalStorage.prototype.clear = function(cb) {
|
||||
// NOP
|
||||
return cb();
|
||||
};
|
||||
|
||||
LocalStorage.prototype.allKeys = function(cb) {
|
||||
if (isChromeApp) {
|
||||
chrome.storage.local.get(null, function(items) {
|
||||
return cb(null, _.keys(items));
|
||||
});
|
||||
} else {
|
||||
var ret = [];
|
||||
var l = this.ls.length;
|
||||
|
||||
for (var i = 0; i < l; i++)
|
||||
ret.push(this.ls.key(i));
|
||||
|
||||
return cb(null, ret);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = LocalStorage;
|
|
@ -1,28 +0,0 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.services')
|
||||
.factory('applicationService', function($rootScope, $location, $timeout, go, isCordova) {
|
||||
var root = {};
|
||||
var isChromeApp = window.chrome && chrome.runtime && chrome.runtime.id;
|
||||
|
||||
root.restart = function() {
|
||||
if (isCordova) {
|
||||
$rootScope.iden = $rootScope.wallet = undefined;
|
||||
go.path('/');
|
||||
$timeout(function(){
|
||||
$rootScope.$digest();
|
||||
},1);
|
||||
|
||||
} else {
|
||||
|
||||
// Go home reloading the application
|
||||
var hashIndex = window.location.href.indexOf('#!/');
|
||||
if (isChromeApp) {
|
||||
chrome.runtime.reload();
|
||||
} else {
|
||||
window.location = window.location.href.substr(0, hashIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
|
@ -1,86 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var BackupService = function($rootScope, notification) {
|
||||
this.$rootScope = $rootScope;
|
||||
this.notifications = notification;
|
||||
};
|
||||
|
||||
BackupService.prototype.getCopayer = function(wallet) {
|
||||
return wallet.totalCopayers > 1 ? wallet.getMyCopayerNickname() : '';
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BackupService.prototype._download = function(ew, walletName, filename) {
|
||||
|
||||
var NewBlob = function(data, datatype) {
|
||||
var out;
|
||||
|
||||
try {
|
||||
out = new Blob([data], {
|
||||
type: datatype
|
||||
});
|
||||
console.debug("case 1");
|
||||
} catch (e) {
|
||||
window.BlobBuilder = window.BlobBuilder ||
|
||||
window.WebKitBlobBuilder ||
|
||||
window.MozBlobBuilder ||
|
||||
window.MSBlobBuilder;
|
||||
|
||||
if (e.name == 'TypeError' && window.BlobBuilder) {
|
||||
var bb = new BlobBuilder();
|
||||
bb.append(data);
|
||||
out = bb.getBlob(datatype);
|
||||
console.debug("case 2");
|
||||
} else if (e.name == "InvalidStateError") {
|
||||
// InvalidStateError (tested on FF13 WinXP)
|
||||
out = new Blob([data], {
|
||||
type: datatype
|
||||
});
|
||||
console.debug("case 3");
|
||||
} else {
|
||||
// We're screwed, blob constructor unsupported entirely
|
||||
console.debug("Errore");
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
var blob;
|
||||
|
||||
blob = new NewBlob(ew, 'text/plain;charset=utf-8');
|
||||
|
||||
|
||||
this.notifications.success('Backup created', 'Encrypted backup file saved');
|
||||
|
||||
// otherwise lean on the browser implementation
|
||||
saveAs(blob, filename);
|
||||
};
|
||||
|
||||
BackupService.prototype.walletEncrypted = function(wallet) {
|
||||
return wallet.exportEncrypted(this.$rootScope.iden.password);
|
||||
}
|
||||
|
||||
BackupService.prototype.walletDownload = function(wallet) {
|
||||
var ew = this.walletEncrypted(wallet);
|
||||
var walletName = wallet.getName();
|
||||
var copayerName = this.getCopayer(wallet);
|
||||
var filename = (copayerName ? copayerName + '-' : '') + walletName + '-keybackup.json.aes';
|
||||
this._download(ew, walletName, filename)
|
||||
};
|
||||
|
||||
BackupService.prototype.profileEncrypted = function(iden) {
|
||||
iden.setBackupNeeded(false);
|
||||
return iden.exportEncryptedWithWalletInfo(iden.password);
|
||||
}
|
||||
|
||||
BackupService.prototype.profileDownload = function(iden) {
|
||||
var ew = this.profileEncrypted(iden);
|
||||
var name = iden.fullName;
|
||||
var filename = name + '-profile.json';
|
||||
this._download(ew, name, filename)
|
||||
};
|
||||
|
||||
angular.module('copayApp.services').service('backupService', BackupService);
|
|
@ -1,13 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('Compatibility', function($rootScope) {
|
||||
var root = {};
|
||||
|
||||
root.check = function (scope) {
|
||||
copay.Compatibility.listWalletsPre8(function(wallets) {
|
||||
scope.anyWallet = wallets.length > 0 ? true : false;
|
||||
scope.oldWallets = wallets;
|
||||
});
|
||||
};
|
||||
return root;
|
||||
});
|
|
@ -1,36 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('configService', function($timeout, localstorageService, gettextCatalog, defaults) {
|
||||
var root = {};
|
||||
|
||||
root.set = function(opts, cb) {
|
||||
|
||||
// Options that have runtime effects
|
||||
if (opts.logLevel)
|
||||
copay.logger.setLevel(opts.logLevel);
|
||||
|
||||
// Set current version
|
||||
opts.version = copay.version;
|
||||
|
||||
localstorageService.getItem('config', function(err, oldOpsStr) {
|
||||
var oldOpts = {};
|
||||
try {
|
||||
oldOpts = JSON.parse(oldOpsStr);
|
||||
} catch (e) {};
|
||||
|
||||
var newOpts = {};
|
||||
_.extend(newOpts, copay.defaultConfig, oldOpts, opts);
|
||||
|
||||
// TODO remove this global variable.
|
||||
config = newOpts;
|
||||
localstorageService.setItem('config', JSON.stringify(newOpts), cb);
|
||||
});
|
||||
};
|
||||
|
||||
root.reset = function(cb) {
|
||||
config = defaults;
|
||||
localstorageService.removeItem('config', cb);
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
|
@ -1,363 +0,0 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.services')
|
||||
.factory('identityService', function($rootScope, $location, $timeout, $filter, pluginManager, notification, pendingTxsService, balanceService, applicationService, go) {
|
||||
|
||||
// TODO:
|
||||
// * remove iden from rootScope
|
||||
// * remove wallet from rootScope
|
||||
// * create walletService
|
||||
|
||||
var root = {};
|
||||
root.check = function(scope) {
|
||||
copay.Identity.checkIfExistsAny({
|
||||
pluginManager: pluginManager.getInstance(config),
|
||||
}, function(anyProfile) {
|
||||
copay.Wallet.checkIfExistsAny({
|
||||
pluginManager: pluginManager.getInstance(config),
|
||||
}, function(anyWallet) {
|
||||
scope.loading = false;
|
||||
scope.anyProfile = anyProfile ? true : false;
|
||||
scope.anyWallet = anyWallet ? true : false;
|
||||
|
||||
if (!scope.anyProfile) {
|
||||
$location.path('/createProfile');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
root.create = function(email, password, cb) {
|
||||
copay.Identity.create({
|
||||
email: email,
|
||||
password: password,
|
||||
pluginManager: pluginManager.getInstance(config),
|
||||
network: config.network,
|
||||
networkName: config.networkName,
|
||||
walletDefaults: config.wallet,
|
||||
passphraseConfig: config.passphraseConfig,
|
||||
failIfExists: true,
|
||||
}, function(err, iden) {
|
||||
if (err) return cb(err);
|
||||
preconditions.checkState(iden);
|
||||
root.bind(iden);
|
||||
|
||||
return cb(null);
|
||||
});
|
||||
};
|
||||
|
||||
root.createDefaultWallet = function(cb) {
|
||||
var iden = $rootScope.iden;
|
||||
|
||||
var walletOptions = {
|
||||
nickname: iden.fullName,
|
||||
networkName: config.networkName,
|
||||
requiredCopayers: 1,
|
||||
totalCopayers: 1,
|
||||
password: iden.password,
|
||||
name: 'My wallet',
|
||||
};
|
||||
iden.createWallet(walletOptions, function(err, wallet) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
root.resendVerificationEmail = function(cb) {
|
||||
var iden = $rootScope.iden;
|
||||
iden.resendVerificationEmail(cb);
|
||||
};
|
||||
|
||||
root.setServerStatus = function(headers) {
|
||||
if (!headers)
|
||||
return;
|
||||
|
||||
var customHeaders = {};
|
||||
_.each(_.keys(headers), function(headerKey) {
|
||||
var hk = headerKey.toLowerCase();
|
||||
if (hk.indexOf('x-') === 0) {
|
||||
customHeaders[hk] = headers[headerKey];
|
||||
}
|
||||
});
|
||||
|
||||
if (customHeaders['x-email-needs-validation'])
|
||||
$rootScope.needsEmailConfirmation = true;
|
||||
else
|
||||
$rootScope.needsEmailConfirmation = null;
|
||||
|
||||
if (customHeaders['x-quota-per-item'])
|
||||
$rootScope.quotaPerItem = parseInt(customHeaders['x-quota-per-item']);
|
||||
|
||||
if (customHeaders['x-quota-items-limit'])
|
||||
$rootScope.quotaItems = parseInt(customHeaders['x-quota-items-limit']);
|
||||
};
|
||||
|
||||
root.open = function(email, password, cb) {
|
||||
var opts = {
|
||||
email: email,
|
||||
password: password,
|
||||
pluginManager: pluginManager.getInstance(config),
|
||||
network: config.network,
|
||||
networkName: config.networkName,
|
||||
walletDefaults: config.wallet,
|
||||
passphraseConfig: config.passphraseConfig,
|
||||
};
|
||||
|
||||
copay.Identity.open(opts, function(err, iden, headers) {
|
||||
if (err) return cb(err);
|
||||
root.setServerStatus(headers);
|
||||
root.bind(iden);
|
||||
return cb(null, iden);
|
||||
});
|
||||
};
|
||||
|
||||
root.deleteProfile = function(cb) {
|
||||
$rootScope.iden.remove(null, cb);
|
||||
};
|
||||
|
||||
root.deleteWallet = function(w, cb) {
|
||||
$rootScope.iden.deleteWallet(w.getId(), cb);
|
||||
};
|
||||
|
||||
root.isFocused = function(wid) {
|
||||
return $rootScope.wallet && wid === $rootScope.wallet.getId();
|
||||
};
|
||||
|
||||
root.setupGlobalVariables = function(iden) {
|
||||
$rootScope.reconnecting = false;
|
||||
$rootScope.iden = iden;
|
||||
};
|
||||
|
||||
|
||||
root.noFocusedWallet = function() {
|
||||
$rootScope.wallet = null;
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
})
|
||||
};
|
||||
|
||||
root.setFocusedWallet = function(w, dontUpdateIt) {
|
||||
if (!_.isObject(w))
|
||||
w = $rootScope.iden.getWalletById(w);
|
||||
preconditions.checkState(w && _.isObject(w));
|
||||
|
||||
copay.logger.debug('Set focus:', w.getName());
|
||||
$rootScope.wallet = w;
|
||||
|
||||
if (!dontUpdateIt)
|
||||
$rootScope.iden.updateFocusedTimestamp(w.getId());
|
||||
|
||||
pendingTxsService.update();
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
root.notifyTxProposalEvent = function(w, e) {
|
||||
if (e.cId == w.getMyCopayerId())
|
||||
return;
|
||||
|
||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||
var name = w.getName();
|
||||
switch (e.type) {
|
||||
case copay.Wallet.TX_NEW:
|
||||
notification.info('[' + name + '] New Transaction',
|
||||
$filter('translate')('You received a transaction proposal from') + ' ' + user);
|
||||
break;
|
||||
case copay.Wallet.TX_SIGNED:
|
||||
notification.success('[' + name + '] Transaction Signed',
|
||||
$filter('translate')('A transaction was signed by') + ' ' + user);
|
||||
break;
|
||||
case copay.Wallet.TX_BROADCASTED:
|
||||
notification.success('[' + name + '] Transaction Approved',
|
||||
$filter('translate')('A transaction was broadcasted by') + ' ' + user);
|
||||
break;
|
||||
case copay.Wallet.TX_REJECTED:
|
||||
notification.warning('[' + name + '] Transaction Rejected',
|
||||
$filter('translate')('A transaction was rejected by') + ' ' + user);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
root.installWalletHandlers = function(w) {
|
||||
var wid = w.getId();
|
||||
w.on('connectionError', function() {
|
||||
if (root.isFocused(wid)) {
|
||||
var message = "Could not connect to the Insight server. Check your settings and network configuration";
|
||||
notification.error('Networking Error', message);
|
||||
}
|
||||
});
|
||||
|
||||
w.on('corrupt', function(peerId) {
|
||||
copay.logger.warn('Received corrupt message from ' + peerId);
|
||||
});
|
||||
|
||||
w.on('publicKeyRingUpdated', function() {
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
w.on('ready', function() {
|
||||
var isFocused = root.isFocused(wid);
|
||||
copay.logger.debug('Wallet:' + w.getName() +
|
||||
' is ready. Focused:', isFocused);
|
||||
|
||||
balanceService.update(w, function() {
|
||||
$rootScope.$digest();
|
||||
}, isFocused);
|
||||
});
|
||||
|
||||
w.on('tx', function(address, isChange) {
|
||||
if (!isChange) {
|
||||
notification.funds('Funds received on ' + w.getName(), address);
|
||||
}
|
||||
balanceService.update(w, function() {
|
||||
$rootScope.$digest();
|
||||
}, root.isFocused(wid));
|
||||
});
|
||||
|
||||
w.on('balanceUpdated', function() {
|
||||
balanceService.update(w, function() {
|
||||
$rootScope.$digest();
|
||||
}, root.isFocused(wid));
|
||||
});
|
||||
|
||||
w.on('insightReconnected', function() {
|
||||
$rootScope.reconnecting = false;
|
||||
balanceService.update(w, function() {
|
||||
$rootScope.$digest();
|
||||
}, root.isFocused(wid));
|
||||
});
|
||||
|
||||
w.on('insightError', function() {
|
||||
if (root.isFocused(wid)) {
|
||||
$rootScope.reconnecting = true;
|
||||
$rootScope.$digest();
|
||||
}
|
||||
});
|
||||
w.on('newAddresses', function() {
|
||||
// Nothing yet
|
||||
});
|
||||
|
||||
// Disabled for now, does not seens to have much value for the user
|
||||
// w.on('paymentACK', function(memo) {
|
||||
// notification.success('Payment Acknowledged', memo);
|
||||
// });
|
||||
|
||||
w.on('txProposalEvent', function(ev) {
|
||||
|
||||
if (root.isFocused(wid)) {
|
||||
pendingTxsService.update();
|
||||
}
|
||||
|
||||
// TODO aqui lo unico que cambia son los locked
|
||||
// se puede optimizar
|
||||
balanceService.update(w, function() {
|
||||
$rootScope.$digest();
|
||||
}, root.isFocused(wid));
|
||||
|
||||
root.notifyTxProposalEvent(w, ev);
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
});
|
||||
});
|
||||
|
||||
w.on('addressBookUpdated', function(dontDigest) {
|
||||
if (root.isFocused(wid)) {
|
||||
if (!dontDigest) {
|
||||
$rootScope.$digest();
|
||||
}
|
||||
}
|
||||
});
|
||||
w.on('connect', function(peerID) {
|
||||
$rootScope.$digest();
|
||||
});
|
||||
// TODO?
|
||||
// w.on('close', );
|
||||
// w.on('locked',);
|
||||
};
|
||||
|
||||
root.bind = function(iden) {
|
||||
preconditions.checkArgument(_.isObject(iden));
|
||||
copay.logger.debug('Binding profile...');
|
||||
|
||||
var self = this;
|
||||
root.setupGlobalVariables(iden);
|
||||
iden.on('newWallet', function(wid) {
|
||||
var w = iden.getWalletById(wid);
|
||||
copay.logger.debug('newWallet:',
|
||||
w.getName(), wid, iden.getLastFocusedWalletId());
|
||||
root.installWalletHandlers(w);
|
||||
if (wid == iden.getLastFocusedWalletId()) {
|
||||
copay.logger.debug('GOT Focused wallet:', w.getName());
|
||||
root.setFocusedWallet(w, true);
|
||||
go.walletHome();
|
||||
}
|
||||
|
||||
// At the end (after all handlers are in place)...start the wallet.
|
||||
w.netStart();
|
||||
});
|
||||
|
||||
iden.on('noWallets', function() {
|
||||
notification.warning('No Wallets', 'Your profile has no wallets. Create one here');
|
||||
$rootScope.starting = false;
|
||||
$location.path('/add');
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
});
|
||||
|
||||
iden.on('walletDeleted', function(wid) {
|
||||
// do nothing. this is handled 'on sync' on controller.
|
||||
});
|
||||
|
||||
iden.on('walletStorageError', function(wid, message) {
|
||||
notification.error('Error storing wallet', message);
|
||||
});
|
||||
|
||||
iden.on('closed', function() {
|
||||
delete $rootScope['wallet'];
|
||||
delete $rootScope['iden'];
|
||||
applicationService.restart();
|
||||
});
|
||||
};
|
||||
|
||||
root.signout = function() {
|
||||
if ($rootScope.iden) {
|
||||
$rootScope.signingOut = true;
|
||||
$rootScope.iden.close(function() { // Will trigger 'closed'
|
||||
$timeout(function() {
|
||||
$rootScope.signingOut = null;
|
||||
}, 100);
|
||||
}); // Will trigger 'closed'
|
||||
}
|
||||
};
|
||||
|
||||
root.createWallet = function(opts, cb) {
|
||||
$rootScope.iden.createWallet(opts, cb);
|
||||
};
|
||||
|
||||
root.importWallet = function(encryptedObj, pass, opts, cb) {
|
||||
copay.Compatibility.importEncryptedWallet($rootScope.iden, encryptedObj, pass, opts, cb);
|
||||
};
|
||||
|
||||
root.joinWallet = function(opts, cb) {
|
||||
$rootScope.iden.joinWallet(opts, function(err, w) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
root.importProfile = function(str, password, cb) {
|
||||
copay.Identity.importFromEncryptedFullJson(str, password, {
|
||||
pluginManager: pluginManager.getInstance(config),
|
||||
network: config.network,
|
||||
networkName: config.networkName,
|
||||
walletDefaults: config.wallet,
|
||||
passphraseConfig: config.passphraseConfig,
|
||||
}, function(err, iden, walletObjs) {
|
||||
if (err) return cb(err);
|
||||
root.bind(iden);
|
||||
iden.importMultipleWalletsFromObj(walletObjs);
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
angular.module('copayApp.services')
|
||||
.factory('localstorageService', function($rootScope) {
|
||||
var LS = require('../js/plugins/LocalStorage');
|
||||
var ls = new LS();
|
||||
return ls;
|
||||
});
|
|
@ -1,51 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services')
|
||||
.factory('pendingTxsService', function($rootScope, $filter, rateService) {
|
||||
var root = {};
|
||||
|
||||
root.setAlternativeAmount = function(w, tx, cb) {
|
||||
var alternativeIsoCode = w.settings.alternativeIsoCode;
|
||||
rateService.whenAvailable(function() {
|
||||
_.each(tx.outs, function(out) {
|
||||
var valueSat = out.valueSat * w.settings.unitToSatoshi;
|
||||
out.alternativeAmount = $filter('noFractionNumber')(
|
||||
rateService.toFiat(valueSat, alternativeIsoCode), 2);
|
||||
out.alternativeIsoCode = alternativeIsoCode;
|
||||
});
|
||||
if (cb) return cb(tx);
|
||||
});
|
||||
};
|
||||
|
||||
root.getDecoratedTxProposals = function(w) {
|
||||
var txps = w.getPendingTxProposals();
|
||||
|
||||
_.each(txps, function(tx) {
|
||||
root.setAlternativeAmount(w, tx);
|
||||
if (tx.outs) {
|
||||
_.each(tx.outs, function(out) {
|
||||
out.valueSat = out.value;
|
||||
out.value = $filter('noFractionNumber')(out.value);
|
||||
});
|
||||
}
|
||||
});
|
||||
return txps;
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc adds 2 fields to wallet: pendingTxProposalsCountForUs, pendingTxProposals.
|
||||
*
|
||||
* @param w wallet
|
||||
*/
|
||||
root.update = function(w) {
|
||||
var w = $rootScope.wallet;
|
||||
if (!w) return;
|
||||
|
||||
//pendingTxCount
|
||||
var ret = w.getPendingTxProposalsCount();
|
||||
w.pendingTxProposalsCountForUs = ret.pendingForUs;
|
||||
w.pendingTxProposals = root.getDecoratedTxProposals(w);
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
|
@ -1,115 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services')
|
||||
.factory('pinService', function($rootScope, $timeout, localstorageService) {
|
||||
|
||||
var KEY = 'pinDATA';
|
||||
var SALT = '4gllotIKguqi0EkIslC0';
|
||||
var ITER = 5000;
|
||||
|
||||
var ls = localstorageService;
|
||||
var root = {};
|
||||
|
||||
var _firstpin;
|
||||
|
||||
root.check = function(cb) {
|
||||
ls.getItem(KEY, function(err, value) {
|
||||
return cb(err, value ? true : false);
|
||||
});
|
||||
};
|
||||
|
||||
root.get = function(pin, cb) {
|
||||
ls.getItem(KEY, function(err, value) {
|
||||
if (!value) return cb(null);
|
||||
var enc = value;
|
||||
var data = copay.crypto.decrypt('' + parseInt(pin), enc);
|
||||
var err = new Error('Could not decrypt');
|
||||
if (data) {
|
||||
var obj;
|
||||
try {
|
||||
obj = JSON.parse(data);
|
||||
err = null;
|
||||
} catch (e) {};
|
||||
}
|
||||
return cb(err, obj);
|
||||
});
|
||||
};
|
||||
|
||||
root.save = function(pin, email, password, cb) {
|
||||
var credentials = {
|
||||
email: email,
|
||||
password: password,
|
||||
};
|
||||
var enc = copay.crypto.encrypt('' + parseInt(pin), credentials, SALT, ITER);
|
||||
ls.setItem(KEY, enc, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
root.clear = function(cb) {
|
||||
ls.removeItem(KEY, cb);
|
||||
};
|
||||
|
||||
root.clearPin = function(scope) {
|
||||
scope.digits = [];
|
||||
scope.defined = [];
|
||||
};
|
||||
|
||||
root.pressPin = function(scope, digit, skipOpenWithPin) {
|
||||
scope.error = null;
|
||||
scope.digits.push(digit);
|
||||
scope.defined.push(true);
|
||||
if (scope.digits.length == 4) {
|
||||
var pin = scope.digits.join('');
|
||||
if (!$rootScope.hasPin) {
|
||||
if (!_firstpin) {
|
||||
_firstpin = pin;
|
||||
scope.askForPin = 2;
|
||||
$timeout(function() {
|
||||
scope.clear();
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (pin === _firstpin) {
|
||||
_firstpin = null;
|
||||
scope.askForPin = null;
|
||||
scope.createPin(pin);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
_firstpin = null;
|
||||
scope.askForPin = 1;
|
||||
$timeout(function() {
|
||||
scope.clear();
|
||||
scope.error = 'Entered PINs were not equal. Try again';
|
||||
$timeout(function() {
|
||||
scope.error = null;
|
||||
}, 2000);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skipOpenWithPin) {
|
||||
scope.openWithPin(pin);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
root.skipPin = function(scope, creatingProfile) {
|
||||
if (!$rootScope.hasPin) {
|
||||
if (!creatingProfile) {
|
||||
scope.openWallets();
|
||||
}
|
||||
else {
|
||||
scope.createDefaultWallet()
|
||||
}
|
||||
}
|
||||
else {
|
||||
scope.pinLogout();
|
||||
}
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('rateService', function(request) {
|
||||
var cfg = _.extend(config.rates, {
|
||||
request: request
|
||||
});
|
||||
return copay.RateService.singleton(cfg);
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('request', function() {
|
||||
return require('request');
|
||||
});
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('txStatus', function($modal) {
|
||||
var root = {};
|
||||
|
||||
root.notify = function(status) {
|
||||
var msg;
|
||||
if (status == copay.Wallet.TX_BROADCASTED)
|
||||
msg = 'Transaction broadcasted';
|
||||
else if (status == copay.Wallet.TX_PROPOSAL_SENT)
|
||||
msg = 'Transaction proposal created';
|
||||
else if (status == copay.Wallet.TX_SIGNED)
|
||||
msg = 'Transaction proposal signed';
|
||||
else if (status == copay.Wallet.TX_REJECTED)
|
||||
msg = 'Transaction was rejected';
|
||||
|
||||
if (msg)
|
||||
root.openModal(msg);
|
||||
};
|
||||
|
||||
root.openModal = function(statusStr) {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.statusStr = statusStr;
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/tx-status.html',
|
||||
windowClass: 'tiny',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
};
|
||||
|
||||
return root;
|
||||
});
|
122
js/shell.js
122
js/shell.js
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
** copay-shell integration
|
||||
*/
|
||||
(function() {
|
||||
/*
|
||||
** This is a monkey patch for when Copay is running from
|
||||
** within Copay-Shell (atom-shell). Since the renderer (the frontend)
|
||||
** receives context from Node.js, we get a `module.exports` contruct
|
||||
** available to us. Because of this, some libs (specifically Moment.js)
|
||||
** attempt to assume their CommonJS form and bind to this. This causes
|
||||
** there to be no references in the window to these libs, so let's trick
|
||||
** the renderer into thinking that we are _not_ in a CommonJS environment.
|
||||
*/
|
||||
if (typeof module !== 'undefined') module = {
|
||||
exports: false
|
||||
};
|
||||
|
||||
// are we running in copay shell?
|
||||
if (window.process && process.type === 'renderer') {
|
||||
window.cshell = initCopayShellBindings();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
function controller(name) {
|
||||
return angular.element(
|
||||
document.querySelectorAll(
|
||||
'[ng-controller="' + name + '"], [data-ng-controller="' + name + '"]'
|
||||
)
|
||||
).scope();
|
||||
};
|
||||
|
||||
function needsWalletLogin(ipc) {
|
||||
ipc.send('alert', 'info', 'Please select a wallet.');
|
||||
};
|
||||
|
||||
function initCopayShellBindings() {
|
||||
|
||||
var ipc = require('ipc');
|
||||
var clipb = require('clipboard');
|
||||
|
||||
// atom shell forces to implement the clipboard (on osx) on our own - thanks obama.
|
||||
|
||||
Mousetrap.stopCallback = function() {
|
||||
return false
|
||||
};
|
||||
|
||||
Mousetrap.bind('command+c', function(e) {
|
||||
clipb.writeText(window.getSelection().toString());
|
||||
});
|
||||
|
||||
Mousetrap.bind('command+v', function(e) {
|
||||
if (document.activeElement) {
|
||||
document.activeElement.value = clipb.readText();
|
||||
document.activeElement.dispatchEvent(new Event('change'));
|
||||
}
|
||||
});
|
||||
|
||||
// handle messages
|
||||
ipc.on('address:create', function(data) {
|
||||
location.href = '#/addresses';
|
||||
var ctrl = controller('AddressesController');
|
||||
if (!ctrl) return needsWalletLogin(ipc);
|
||||
ctrl.newAddr();
|
||||
});
|
||||
|
||||
ipc.on('transactions:send', function(data) {
|
||||
location.href = '#/send';
|
||||
var ctrl = controller('SendController');
|
||||
if (!ctrl) return needsWalletLogin(ipc);
|
||||
});
|
||||
|
||||
ipc.on('transactions:all', function(data) {
|
||||
location.href = '#/transactions';
|
||||
var ctrl = controller('TransactionsController');
|
||||
if (!ctrl) return needsWalletLogin(ipc);
|
||||
ctrl.show();
|
||||
});
|
||||
|
||||
ipc.on('transactions:pending', function(data) {
|
||||
location.href = '#/transactions';
|
||||
var ctrl = controller('TransactionsController');
|
||||
if (!ctrl) return needsWalletLogin(ipc);
|
||||
ctrl.show(true);
|
||||
});
|
||||
|
||||
ipc.on('backup:download', function(data) {
|
||||
location.href = '#/backup';
|
||||
var ctrl = controller('BackupController');
|
||||
if (!ctrl) return needsWalletLogin(ipc);
|
||||
ctrl.download();
|
||||
});
|
||||
|
||||
ipc.on('backup:email', function(data) {
|
||||
location.href = '#/backup';
|
||||
var ctrl = controller('BackupController');
|
||||
if (!ctrl) return needsWalletLogin(ipc);
|
||||
ctrl.openModal();
|
||||
});
|
||||
|
||||
ipc.on('backup:import:data', function(data) {
|
||||
location.href = '#/import';
|
||||
var ctrl = controller('ImportController');
|
||||
if (!ctrl) return;
|
||||
ctrl.backupText = data;
|
||||
ctrl.openPasteArea();
|
||||
ctrl.$apply();
|
||||
});
|
||||
|
||||
ipc.on('backup:import', function(data) {
|
||||
location.href = '#/import';
|
||||
});
|
||||
|
||||
// let the shell know when an error occurs
|
||||
window.onerror = function(err) {
|
||||
ipc.send('error', err);
|
||||
};
|
||||
|
||||
return ipc;
|
||||
};
|
||||
|
||||
})();
|
|
@ -1,79 +0,0 @@
|
|||
var preconditions = require('preconditions').singleton();
|
||||
|
||||
module.exports = {
|
||||
request: function(options, callback) {
|
||||
preconditions.checkArgument(_.isObject(options));
|
||||
|
||||
options.method = options.method || 'GET';
|
||||
options.headers = options.headers || {};
|
||||
var ret = {
|
||||
success: function(cb) {
|
||||
this._success = cb;
|
||||
return this;
|
||||
},
|
||||
error: function(cb) {
|
||||
this._error = cb;
|
||||
return this;
|
||||
},
|
||||
_success: function() {;
|
||||
},
|
||||
_error: function(_, err) {
|
||||
console.trace(err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
var method = (options.method || 'GET').toUpperCase();
|
||||
var url = options.url;
|
||||
var req = options;
|
||||
|
||||
req.headers = req.headers || {};
|
||||
req.body = req.body || req.data || '';
|
||||
|
||||
var xhr = options.xhr || new XMLHttpRequest();
|
||||
xhr.open(method, url, true);
|
||||
|
||||
Object.keys(req.headers).forEach(function(key) {
|
||||
var val = req.headers[key];
|
||||
if (key === 'Content-Length') return;
|
||||
if (key === 'Content-Transfer-Encoding') return;
|
||||
xhr.setRequestHeader(key, val);
|
||||
});
|
||||
|
||||
if (req.responseType) {
|
||||
xhr.responseType = req.responseType;
|
||||
}
|
||||
|
||||
xhr.onload = function(event) {
|
||||
var response = xhr.response;
|
||||
var buf = new Uint8Array(response);
|
||||
var headers = {};
|
||||
(xhr.getAllResponseHeaders() || '').replace(
|
||||
/(?:\r?\n|^)([^:\r\n]+): *([^\r\n]+)/g,
|
||||
function($0, $1, $2) {
|
||||
headers[$1.toLowerCase()] = $2;
|
||||
}
|
||||
);
|
||||
|
||||
return ret._success(buf, xhr.status, headers, options);
|
||||
};
|
||||
|
||||
xhr.onerror = function(event) {
|
||||
var status;
|
||||
if (xhr.status === 0 || !xhr.statusText) {
|
||||
status = 'HTTP Request Error: This endpoint likely does not support cross-origin requests.';
|
||||
} else {
|
||||
status = xhr.statusText;
|
||||
}
|
||||
return ret._error(null, status, null, options);
|
||||
};
|
||||
|
||||
if (req.body) {
|
||||
xhr.send(req.body);
|
||||
} else {
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
};
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* Small module for some helpers that wrap sjcl with some good practices.
|
||||
*/
|
||||
var sjcl = require('sjcl');
|
||||
var _ = require('lodash');
|
||||
|
||||
var log = require('../util/log.js');
|
||||
var config = require('../../config');
|
||||
|
||||
var defaultSalt = (config && config.passphraseConfig && config.passphraseConfig.storageSalt) || 'mjuBtGybi/4=';
|
||||
var defaultIterations = (config && config.passphraseConfig && config.passphraseConfig.iterations) || 1000;
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* @param {string} password
|
||||
* @param {string} salt - base64 encoded, defaults to 'mjuBtGybi/4='
|
||||
* @param {number} iterations - defaults to 100
|
||||
* @param {number} length - bits, defaults to 512 bits
|
||||
* @returns {string} base64 encoded pbkdf2 derivation using sha1 for hmac
|
||||
*/
|
||||
kdf: function(password, salt, iterations, length) {
|
||||
return sjcl.codec.base64.fromBits(
|
||||
this.kdfbinary(password, salt, iterations, length)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string} data
|
||||
* @return {string} base64 encoded hmac
|
||||
*/
|
||||
hmac: function(key, data) {
|
||||
return sjcl.codec.base64.fromBits(
|
||||
new sjcl.misc.hmac(key, sjcl.hash.sha256).encrypt(data)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} password
|
||||
* @param {string} salt - base64 encoded, defaults to 'mjuBtGybi/4='
|
||||
* @param {number} iterations - defaults to 100
|
||||
* @param {number} length - bits, defaults to 512 bits
|
||||
* @returns {string} base64 encoded pbkdf2 derivation using sha1 for hmac
|
||||
*/
|
||||
kdfbinary: function(password, salt, iterations, length) {
|
||||
iterations = iterations || defaultIterations;
|
||||
length = length || 512;
|
||||
salt = sjcl.codec.base64.toBits(salt || defaultSalt);
|
||||
|
||||
var hash = sjcl.hash.sha256.hash(sjcl.hash.sha256.hash(password));
|
||||
var prff = function(key) {
|
||||
return new sjcl.misc.hmac(hash, sjcl.hash.sha1);
|
||||
};
|
||||
|
||||
return sjcl.misc.pbkdf2(hash, salt, iterations, length, prff);
|
||||
},
|
||||
|
||||
/**
|
||||
* Encrypts symmetrically using a passphrase
|
||||
*/
|
||||
encrypt: function(key, message, salt, iter) {
|
||||
if (!_.isString(message)) {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
sjcl.json.defaults.salt = salt || defaultSalt;
|
||||
sjcl.json.defaults.iter = iter || defaultIterations;
|
||||
return sjcl.encrypt(key, message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Decrypts symmetrically using a passphrase
|
||||
*/
|
||||
decrypt: function(key, sjclEncryptedJson) {
|
||||
var output = {};
|
||||
try {
|
||||
return sjcl.decrypt(key, sjclEncryptedJson);
|
||||
} catch (e) {
|
||||
log.debug('Decryption failed due to error: ' + e.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
/**
|
||||
* Small module for exporting data to CSV.
|
||||
*/
|
||||
var _ = require('lodash');
|
||||
var preconditions = require('preconditions').singleton();
|
||||
var moment = require('moment');
|
||||
|
||||
var logger = require('../util/log.js');
|
||||
var config = require('../../config');
|
||||
|
||||
|
||||
var COL_DELIMITER = ',';
|
||||
var ROW_DELIMITER = '\r\n';
|
||||
|
||||
function getValue(obj, property) {
|
||||
if (_.isFunction(property)) {
|
||||
try {
|
||||
return property(obj);
|
||||
} catch (err) {
|
||||
if (_.isString(err)) return err;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (!_.isObject(obj)) return undefined;
|
||||
return obj.hasOwnProperty(property) ? obj[property] : undefined;
|
||||
};
|
||||
|
||||
function formatValue(value, type, format) {
|
||||
if (_.isUndefined(value) || _.isNull(value)) return '';
|
||||
|
||||
var r;
|
||||
switch (type) {
|
||||
default:
|
||||
case 'string':
|
||||
r = value.toString();
|
||||
r.replace('"', '\\"');
|
||||
break;
|
||||
case 'date':
|
||||
r = moment(value).format(format);
|
||||
break;
|
||||
case 'number':
|
||||
r = value.toString();
|
||||
break;
|
||||
}
|
||||
|
||||
// escape when commas in values
|
||||
if (r.indexOf(',') !== -1) {
|
||||
r = '"' + r + '"';
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
function getHeader(descriptor) {
|
||||
return _.map(descriptor.columns, function (col) {
|
||||
return col.label || (_.isString(col.property) ? col.property : '') || '';
|
||||
});
|
||||
};
|
||||
|
||||
function processDataRow(data, descriptor) {
|
||||
return _.map(descriptor.columns, function (col) {
|
||||
var value = getValue(data, col.property);
|
||||
var formatted = formatValue(value, col.type, col.format);
|
||||
return formatted;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Convert json object to csv based on a descriptor
|
||||
*
|
||||
* @param {array} data - the array of json objects to convert to csv
|
||||
* @param {object} descriptor - an object that parameterizes the conversion
|
||||
* @param {function} cb - called with the resulting csv
|
||||
*/
|
||||
module.exports.toCsv = function(data, descriptor, cb) {
|
||||
preconditions.shouldBeArray(data);
|
||||
preconditions.shouldBeObject(descriptor);
|
||||
preconditions.shouldBeArray(descriptor.columns);
|
||||
preconditions.shouldBeFunction(cb);
|
||||
|
||||
var colDelimiter = descriptor.colDelimiter || COL_DELIMITER;
|
||||
var rowDelimiter = descriptor.rowDelimiter || ROW_DELIMITER;
|
||||
|
||||
var rows = _.map(data, function (dataRow) {
|
||||
return processDataRow(dataRow, descriptor);
|
||||
});
|
||||
|
||||
var header = getHeader(descriptor);
|
||||
rows.unshift(header);
|
||||
|
||||
var csv = _.reduce(rows, function (memo, row) {
|
||||
return memo + row.join(colDelimiter) + rowDelimiter;
|
||||
}, '');
|
||||
|
||||
return cb(null, csv);
|
||||
};
|
150
js/util/log.js
150
js/util/log.js
|
@ -1,150 +0,0 @@
|
|||
var config = config || require('../../config');
|
||||
var _ = require('lodash');
|
||||
var ls;
|
||||
|
||||
try {
|
||||
var LS = require('../js/plugins/LocalStorage');
|
||||
ls = new LS();
|
||||
} catch (e) {};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* A simple logger that wraps the <tt>console.log</tt> methods when available.
|
||||
*
|
||||
* Usage:
|
||||
* <pre>
|
||||
* log = new Logger('copay');
|
||||
* log.setLevel('info');
|
||||
* log.debug('Message!'); // won't show
|
||||
* log.setLevel('debug');
|
||||
* log.debug('Message!', 1); // will show '[debug] copay: Message!, 1'
|
||||
* </pre>
|
||||
*
|
||||
* @param {string} name - a name for the logger. This will show up on every log call
|
||||
* @constructor
|
||||
*/
|
||||
var Logger = function(name) {
|
||||
this.name = name || 'log';
|
||||
this.level = 2;
|
||||
};
|
||||
|
||||
Logger.prototype.getLevels = function() {
|
||||
return levels;
|
||||
};
|
||||
|
||||
|
||||
var levels = {
|
||||
'debug': 0,
|
||||
'info': 1,
|
||||
'log': 2,
|
||||
'warn': 3,
|
||||
'error': 4,
|
||||
'fatal': 5
|
||||
};
|
||||
|
||||
_.each(levels, function(level, levelName) {
|
||||
Logger.prototype[levelName] = function() {
|
||||
if (level >= levels[this.level]) {
|
||||
|
||||
if (Error.stackTraceLimit && this.level == 'debug') {
|
||||
var old = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 2;
|
||||
var stack;
|
||||
|
||||
// this hack is to be compatible with IE11
|
||||
try {
|
||||
anerror();
|
||||
} catch (e) {
|
||||
stack = e.stack;
|
||||
}
|
||||
var lines = stack.split('\n');
|
||||
var caller = lines[2];
|
||||
caller = ':' + caller.substr(6);
|
||||
Error.stackTraceLimit = old;
|
||||
}
|
||||
|
||||
var str = '[' + levelName + (caller || '') + '] ' + arguments[0],
|
||||
extraArgs,
|
||||
extraArgs = [].slice.call(arguments, 1);
|
||||
if (console[levelName]) {
|
||||
extraArgs.unshift(str);
|
||||
console[levelName].apply(console, extraArgs);
|
||||
} else {
|
||||
if (extraArgs.length) {
|
||||
str += JSON.stringify(extraArgs);
|
||||
}
|
||||
console.log(str);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Sets the level of a logger. A level can be any bewteen: 'debug', 'info', 'log',
|
||||
* 'warn', 'error', and 'fatal'. That order matters: if a logger's level is set to
|
||||
* 'warn', calling <tt>level.debug</tt> won't have any effect.
|
||||
*
|
||||
* @param {number} level - the name of the logging level
|
||||
*/
|
||||
Logger.prototype.setLevel = function(level) {
|
||||
this.level = level;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Logger
|
||||
* @method debug
|
||||
* @desc Log messages at the debug level.
|
||||
* @param {*} args - the arguments to be logged.
|
||||
*/
|
||||
/**
|
||||
* @class Logger
|
||||
* @method info
|
||||
* @desc Log messages at the info level.
|
||||
* @param {*} args - the arguments to be logged.
|
||||
*/
|
||||
/**
|
||||
* @class Logger
|
||||
* @method log
|
||||
* @desc Log messages at an intermediary level called 'log'.
|
||||
* @param {*} args - the arguments to be logged.
|
||||
*/
|
||||
/**
|
||||
* @class Logger
|
||||
* @method warn
|
||||
* @desc Log messages at the warn level.
|
||||
* @param {*} args - the arguments to be logged.
|
||||
*/
|
||||
/**
|
||||
* @class Logger
|
||||
* @method error
|
||||
* @desc Log messages at the error level.
|
||||
* @param {*} args - the arguments to be logged.
|
||||
*/
|
||||
/**
|
||||
* @class Logger
|
||||
* @method fatal
|
||||
* @desc Log messages at the fatal level.
|
||||
* @param {*} args - the arguments to be logged.
|
||||
*/
|
||||
|
||||
var logger = new Logger('copay');
|
||||
var error = new Error();
|
||||
|
||||
var logLevel = config.logLevel || 'info';
|
||||
|
||||
|
||||
|
||||
if (ls && ls.getItem) {
|
||||
ls.getItem("config", function(err, value) {
|
||||
if (err) return;
|
||||
var localConfig = JSON.parse(value);
|
||||
if (localConfig && localConfig.logLevel)
|
||||
logLevel = localConfig.logLevel;
|
||||
logger.setLevel(logLevel);
|
||||
});
|
||||
} else {
|
||||
logger.setLevel(logLevel);
|
||||
}
|
||||
|
||||
module.exports = logger;
|
118
karma.conf.js
118
karma.conf.js
|
@ -1,118 +0,0 @@
|
|||
// Karma configuration
|
||||
// Generated on Fri Mar 21 2014 17:52:41 GMT-0300 (ART)
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['mocha', 'chai'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
//Configs
|
||||
'config.js',
|
||||
|
||||
'lib/angular/angular.min.js',
|
||||
'lib/angular-mocks/angular-mocks.js',
|
||||
'lib/moment/moment.js',
|
||||
'lib/ng-idle/angular-idle.min.js',
|
||||
'lib/angular-moment/angular-moment.js',
|
||||
'lib/qrcode-generator/js/qrcode.js',
|
||||
'lib/angular-qrcode/qrcode.js',
|
||||
'lib/angular-route/angular-route.min.js',
|
||||
'lib/angular-foundation/mm-foundation.min.js',
|
||||
'lib/angular-foundation/mm-foundation-tpls.min.js',
|
||||
'lib/angular-load/angular-load.min.js',
|
||||
'lib/angular-gravatar/build/md5.min.js',
|
||||
'lib/angular-gravatar/build/angular-gravatar.min.js',
|
||||
'lib/angular-touch/angular-touch.min.js',
|
||||
'lib/angular-gettext/dist/angular-gettext.min.js',
|
||||
'lib/inherits/inherits.js',
|
||||
'lib/lodash/dist/lodash.js',
|
||||
'lib/file-saver/FileSaver.js',
|
||||
'lib/socket.io-client/socket.io.js',
|
||||
'lib/sjcl.js',
|
||||
'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js',
|
||||
|
||||
'lib/bitcore.js',
|
||||
'js/copayBundle.js',
|
||||
|
||||
//App-specific Code
|
||||
'js/app.js',
|
||||
'js/routes.js',
|
||||
'js/services/*.js',
|
||||
'js/directives.js',
|
||||
'js/filters.js',
|
||||
'js/controllers/*.js',
|
||||
'js/translations.js',
|
||||
'js/init.js',
|
||||
|
||||
'test/mocks/FakeBlockchainSocket.js',
|
||||
|
||||
'test/mocha.conf.js',
|
||||
|
||||
//test files
|
||||
'setup/karma.js',
|
||||
'test/unit/**/*.js',
|
||||
'test/*.js',
|
||||
],
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [],
|
||||
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
'js/controllers/*.js': ['coverage']
|
||||
},
|
||||
|
||||
coverageReporter: {
|
||||
type: 'html',
|
||||
dir: 'coverage/'
|
||||
},
|
||||
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress', 'coverage'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: true,
|
||||
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: false,
|
||||
|
||||
// if browser doesn't capture output in given timeout (ms), kill it
|
||||
captureTimeout: 60000
|
||||
});
|
||||
};
|
34
launch.js
34
launch.js
|
@ -1,34 +0,0 @@
|
|||
#! /usr/bin/node
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
|
||||
var sys = require('sys')
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
function puts(error, stdout, stderr) {
|
||||
sys.puts(stdout)
|
||||
}
|
||||
|
||||
function isNumber(n) {
|
||||
return !isNaN(parseInt(n)) && isFinite(n);
|
||||
}
|
||||
|
||||
var args = process.argv.slice(2);
|
||||
var n_str = args[0];
|
||||
if (!isNumber(n_str)) {
|
||||
console.log('Program requires one numeric argument for the amount of copayers');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var N = parseInt(n_str);
|
||||
var DEFAULT_PORT = process.env.DEFAULT_PORT || 3000;
|
||||
|
||||
|
||||
for (var i = 0; i < N; i++) {
|
||||
var port = (i + DEFAULT_PORT);
|
||||
console.log('Simulating copayer #' + (i + 1) + ' at http://localhost:' + port);
|
||||
var command = 'PORT=' + port + ' npm start &'
|
||||
exec(command, puts);
|
||||
}
|
636
lib/bitcore.js
636
lib/bitcore.js
File diff suppressed because one or more lines are too long
58
lib/sjcl.js
58
lib/sjcl.js
|
@ -1,58 +0,0 @@
|
|||
"use strict";function q(a){throw a;}var r=void 0,s=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
|
||||
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});
|
||||
sjcl.cipher.aes=function(a){this.m[0][0][0]||this.H();var b,c,d,e,f=this.m[0][4],g=this.m[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c&
|
||||
255]]};
|
||||
sjcl.cipher.aes.prototype={encrypt:function(a){return v(this,a,0)},decrypt:function(a){return v(this,a,1)},m:[[[],[],[],[],[]],[[],[],[],[],[]]],H:function(){var a=this.m[0],b=this.m[1],c=a[4],d=b[4],e,f,g,h=[],k=[],l,n,m,p;for(e=0;0x100>e;e++)k[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=l||1,g=k[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[l=h[f]]];p=0x1010101*n^0x10001*e^0x101*l^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e=
|
||||
0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}};
|
||||
function v(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,k,l,n=d.length/4-2,m,p=4,u=[0,0,0,0];h=a.m[c];a=h[0];var t=h[1],x=h[2],y=h[3],z=h[4];for(m=0;m<n;m++)h=a[e>>>24]^t[f>>16&255]^x[g>>8&255]^y[b&255]^d[p],k=a[f>>>24]^t[g>>16&255]^x[b>>8&255]^y[e&255]^d[p+1],l=a[g>>>24]^t[b>>16&255]^x[e>>8&255]^y[f&255]^d[p+2],b=a[b>>>24]^t[e>>16&255]^x[f>>8&255]^y[g&255]^d[p+3],p+=4,e=h,f=k,g=l;for(m=0;4>
|
||||
m;m++)u[c?3&-m:m]=z[e>>>24]<<24^z[f>>16&255]<<16^z[g>>8&255]<<8^z[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return u}
|
||||
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.Q(a.slice(b/32),32-(b&31)).slice(1);return c===r?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.Q(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
|
||||
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b&=31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return s;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return 0===
|
||||
c},Q:function(a,b,c,d){var e;e=0;for(d===r&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32<b+a?c:d.pop(),1));return d},n:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}};
|
||||
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
|
||||
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
|
||||
sjcl.codec.base32={r:"0123456789abcdefghjkmnpqrstvwxyz",BITS:32,BASE:5,REMAINING:27,fromBits:function(a){var b=sjcl.codec.base32.BASE,c=sjcl.codec.base32.REMAINING,d="",e,f=0,g=sjcl.codec.base32.r,h=0,k=sjcl.bitArray.bitLength(a);for(e=0;d.length*b<=k;)d+=g.charAt((h^a[e]>>>f)>>>c),f<b?(h=a[e]<<b-f,f+=c,e++):(h<<=b,f-=b);return d},toBits:function(a){var b=sjcl.codec.base32.BITS,c=sjcl.codec.base32.BASE,d=sjcl.codec.base32.REMAINING,e=[],f,g=0,h=sjcl.codec.base32.r,k=0,l;for(f=0;f<a.length;f++)l=h.indexOf(a.charAt(f)),
|
||||
0>l&&q(new sjcl.exception.invalid("this isn't base32!")),g>d?(g-=d,e.push(k^l>>>g),k=l<<b-g):(g+=c,k^=l<<b-g);g&56&&e.push(sjcl.bitArray.partial(g&56,k,1));return e}};
|
||||
sjcl.codec.base64={r:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.r,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.r,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;d<a.length;d++)h=f.indexOf(a.charAt(d)),
|
||||
0>h&&q(new sjcl.exception.invalid("this isn't base64!")),26<e?(e-=26,c.push(g^h>>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.H();a?(this.e=a.e.slice(0),this.d=a.d.slice(0),this.c=a.c):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
|
||||
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.e=this.u.slice(0);this.d=[];this.c=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.d=sjcl.bitArray.concat(this.d,a);b=this.c;a=this.c=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.q(c.splice(0,16));return this},finalize:function(){var a,b=this.d,c=this.e,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.c/
|
||||
4294967296));for(b.push(this.c|0);b.length;)this.q(b.splice(0,16));this.reset();return c},u:[],b:[],H:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.u[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}},q:function(a){var b,c,d=a.slice(0),e=this.e,f=this.b,g=e[0],h=e[1],k=e[2],l=e[3],n=e[4],m=e[5],p=e[6],u=e[7];for(a=0;64>a;a++)16>a?b=d[a]:(b=d[a+1&15],c=d[a+14&15],b=d[a&15]=(b>>>7^b>>>18^b>>>3^
|
||||
b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0),b=b+u+(n>>>6^n>>>11^n>>>25^n<<26^n<<21^n<<7)+(p^n&(m^p))+f[a],u=p,p=m,m=n,n=l+b|0,l=k,k=h,h=g,g=b+(h&k^l&(h^k))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0;e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+k|0;e[3]=e[3]+l|0;e[4]=e[4]+n|0;e[5]=e[5]+m|0;e[6]=e[6]+p|0;e[7]=e[7]+u|0}};sjcl.hash.sha1=function(a){a?(this.e=a.e.slice(0),this.d=a.d.slice(0),this.c=a.c):this.reset()};sjcl.hash.sha1.hash=function(a){return(new sjcl.hash.sha1).update(a).finalize()};
|
||||
sjcl.hash.sha1.prototype={blockSize:512,reset:function(){this.e=this.u.slice(0);this.d=[];this.c=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.d=sjcl.bitArray.concat(this.d,a);b=this.c;a=this.c=b+sjcl.bitArray.bitLength(a);for(b=this.blockSize+b&-this.blockSize;b<=a;b+=this.blockSize)this.q(c.splice(0,16));return this},finalize:function(){var a,b=this.d,c=this.e,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);
|
||||
b.push(Math.floor(this.c/0x100000000));for(b.push(this.c|0);b.length;)this.q(b.splice(0,16));this.reset();return c},u:[1732584193,4023233417,2562383102,271733878,3285377520],b:[1518500249,1859775393,2400959708,3395469782],q:function(a){var b,c,d,e,f,g,h=a.slice(0),k=this.e;c=k[0];d=k[1];e=k[2];f=k[3];g=k[4];for(a=0;79>=a;a++)16<=a&&(h[a]=(h[a-3]^h[a-8]^h[a-14]^h[a-16])<<1|(h[a-3]^h[a-8]^h[a-14]^h[a-16])>>>31),b=19>=a?d&e|~d&f:39>=a?d^e^f:59>=a?d&e|d&f|e&f:79>=a?d^e^f:r,b=(c<<5|c>>>27)+b+g+h[a]+this.b[Math.floor(a/
|
||||
20)]|0,g=f,f=e,e=d<<30|d>>>2,d=c,c=b;k[0]=k[0]+c|0;k[1]=k[1]+d|0;k[2]=k[2]+e|0;k[3]=k[3]+f|0;k[4]=k[4]+g|0}};
|
||||
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,k=h.bitLength(c)/8,l=h.bitLength(g)/8;e=e||64;d=d||[];7>k&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&l>>>8*f;f++);f<15-k&&(f=15-k);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.N(a,b,c,d,e,f);g=sjcl.mode.ccm.s(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),k=f.clamp(b,h-e),l=f.bitSlice(b,
|
||||
h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));k=sjcl.mode.ccm.s(a,k,c,l,e,b);a=sjcl.mode.ccm.N(a,k.data,c,d,e,b);f.equal(k.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return k.data},N:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,k=h.n;e/=8;(e%2||4>e||16<e)&&q(new sjcl.exception.invalid("ccm: invalid tag length"));(0xffffffff<d.length||0xffffffff<b.length)&&q(new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"));
|
||||
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(k(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(k(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,8*e)},s:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.n;var k=b.length,l=h.bitLength(b);c=h.concat([h.partial(8,
|
||||
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!k)return{tag:d,data:[]};for(g=0;g<k;g+=4)c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,l)}}};
|
||||
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var g,h=sjcl.mode.ocb2.K,k=sjcl.bitArray,l=k.n,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4)m=b.slice(g,g+4),n=l(n,m),p=p.concat(l(c,a.encrypt(l(c,m)))),c=h(c);m=b.slice(g);b=k.bitLength(m);g=a.encrypt(l(c,[0,0,0,b]));m=k.clamp(l(m.concat([0,0,0]),g),b);n=l(n,l(m.concat([0,0,0]),g));n=a.encrypt(l(n,l(c,h(c))));d.length&&
|
||||
(n=l(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(k.concat(m,k.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));e=e||64;var g=sjcl.mode.ocb2.K,h=sjcl.bitArray,k=h.n,l=[0,0,0,0],n=g(a.encrypt(c)),m,p,u=sjcl.bitArray.bitLength(b)-e,t=[];d=d||[];for(c=0;c+4<u/32;c+=4)m=k(n,a.decrypt(k(n,b.slice(c,c+4)))),l=k(l,m),t=t.concat(m),n=g(n);p=u-32*c;m=a.encrypt(k(n,[0,0,0,p]));m=k(m,h.clamp(b.slice(c),p).concat([0,0,0]));
|
||||
l=k(l,m);l=a.encrypt(k(l,k(n,g(n))));d.length&&(l=k(l,f?d:sjcl.mode.ocb2.pmac(a,d)));h.equal(h.clamp(l,e),h.bitSlice(b,u))||q(new sjcl.exception.corrupt("ocb: tag doesn't match"));return t.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.K,e=sjcl.bitArray,f=e.n,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h,
|
||||
d(h))),g))},K:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}};
|
||||
sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.s(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.s(s,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},$:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.n;e=[0,0,0,0];f=b.slice(0);
|
||||
for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0<d;d--)f[d]=f[d]>>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},j:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.$(b,a);return b},s:function(a,b,c,d,e,f){var g,h,k,l,n,m,p,u,t=sjcl.bitArray;m=c.length;p=t.bitLength(c);u=t.bitLength(d);h=t.bitLength(e);g=b.encrypt([0,
|
||||
0,0,0]);96===h?(e=e.slice(0),e=t.concat(e,[1])):(e=sjcl.mode.gcm.j(g,[0,0,0,0],e),e=sjcl.mode.gcm.j(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.j(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.j(g,h,c));for(l=0;l<m;l+=4)n[3]++,k=b.encrypt(n),c[l]^=k[0],c[l+1]^=k[1],c[l+2]^=k[2],c[l+3]^=k[3];c=t.clamp(c,p);a&&(d=sjcl.mode.gcm.j(g,h,c));a=[Math.floor(u/0x100000000),u&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.j(g,d,a);k=b.encrypt(e);d[0]^=k[0];
|
||||
d[1]^=k[1];d[2]^=k[2];d[3]^=k[3];return{tag:t.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.O=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.p=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.p[0].update(c[0]);this.p[1].update(c[1]);this.J=new b(this.p[0])};
|
||||
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){this.R&&q(new sjcl.exception.invalid("encrypt on already updated hmac called!"));this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.J=new this.O(this.p[0]);this.R=s};sjcl.misc.hmac.prototype.update=function(a){this.R=!0;this.J.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.J.finalize(),a=(new this.O(this.p[1])).update(a).finalize();this.reset();return a};
|
||||
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;(0>d||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,k,l=[],n=sjcl.bitArray;for(k=1;32*l.length<(d||1);k++){e=f=a.encrypt(n.concat(b,[k]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}l=l.concat(e)}d&&(l=n.clamp(l,d));return l};
|
||||
sjcl.prng=function(a){this.f=[new sjcl.hash.sha256];this.k=[0];this.I=0;this.w={};this.G=0;this.M={};this.P=this.g=this.l=this.X=0;this.b=[0,0,0,0,0,0,0,0];this.i=[0,0,0,0];this.D=r;this.F=a;this.t=s;this.C={progress:{},seeded:{}};this.o=this.W=0;this.A=1;this.B=2;this.T=0x10000;this.L=[0,48,64,96,128,192,0x100,384,512,768,1024];this.U=3E4;this.S=80};
|
||||
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;d===this.o&&q(new sjcl.exception.notReady("generator isn't seeded"));if(d&this.B){d=!(d&this.A);e=[];var f=0,g;this.P=e[0]=(new Date).valueOf()+this.U;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.f.length&&!(e=e.concat(this.f[g].finalize()),f+=this.k[g],this.k[g]=0,!d&&this.I&1<<g);g++);this.I>=1<<this.f.length&&(this.f.push(new sjcl.hash.sha256),this.k.push(0));this.g-=f;f>this.l&&(this.l=f);this.I++;
|
||||
this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.D=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.i[d]=this.i[d]+1|0,this.i[d]);d++);}for(d=0;d<a;d+=4)0===(d+1)%this.T&&w(this),e=A(this),c.push(e[0],e[1],e[2],e[3]);w(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b&&q("Setting paranoia=0 will ruin your security; use it only for testing");this.F=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),
|
||||
g=this.w[c],h=this.isReady(),k=0;d=this.M[c];d===r&&(d=this.M[c]=this.X++);g===r&&(g=this.w[c]=0);this.w[c]=(this.w[c]+1)%this.f.length;switch(typeof a){case "number":b===r&&(b=1);this.f[g].update([d,this.G++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{"[object Array]"!==c&&(k=1);for(c=0;c<a.length&&!k;c++)"number"!==typeof a[c]&&(k=1)}if(!k){if(b===r)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,
|
||||
e>>>=1;this.f[g].update([d,this.G++,2,b,f,a.length].concat(a))}break;case "string":b===r&&(b=a.length);this.f[g].update([d,this.G++,3,b,f,a.length]);this.f[g].update(a);break;default:k=1}k&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.k[g]+=b;this.g+=b;h===this.o&&(this.isReady()!==this.o&&B("seeded",Math.max(this.l,this.g)),B("progress",this.getProgress()))},isReady:function(a){a=this.L[a!==r?a:this.F];return this.l&&this.l>=a?this.k[0]>this.S&&
|
||||
(new Date).valueOf()>this.P?this.B|this.A:this.A:this.g>=a?this.B|this.o:this.o},getProgress:function(a){a=this.L[a?a:this.F];return this.l>=a?1:this.g>a?1:this.g/a},startCollectors:function(){this.t||(this.a={loadTimeCollector:C(this,this.ba),mouseCollector:C(this,this.ca),keyboardCollector:C(this,this.aa),accelerometerCollector:C(this,this.V),touchCollector:C(this,this.ea)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,s),window.addEventListener("mousemove",this.a.mouseCollector,
|
||||
s),window.addEventListener("keypress",this.a.keyboardCollector,s),window.addEventListener("devicemotion",this.a.accelerometerCollector,s),window.addEventListener("touchmove",this.a.touchCollector,s)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.t=!0)},stopCollectors:function(){this.t&&(window.removeEventListener?
|
||||
(window.removeEventListener("load",this.a.loadTimeCollector,s),window.removeEventListener("mousemove",this.a.mouseCollector,s),window.removeEventListener("keypress",this.a.keyboardCollector,s),window.removeEventListener("devicemotion",this.a.accelerometerCollector,s),window.removeEventListener("touchmove",this.a.touchCollector,s)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress",
|
||||
this.a.keyboardCollector)),this.t=s)},addEventListener:function(a,b){this.C[a][this.W++]=b},removeEventListener:function(a,b){var c,d,e=this.C[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;c<f.length;c++)d=f[c],delete e[d]},aa:function(){D(1)},ca:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&sjcl.random.addEntropy([b,c],2,"mouse");D(0)},ea:function(a){a=a.touches[0]||a.changedTouches[0];sjcl.random.addEntropy([a.pageX||
|
||||
a.clientX,a.pageY||a.clientY],1,"touch");D(0)},ba:function(){D(2)},V:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&sjcl.random.addEntropy(b,1,"accelerometer")}a&&sjcl.random.addEntropy(a,2,"accelerometer");D(0)}};function B(a,b){var c,d=sjcl.random.C[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}
|
||||
function D(a){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?sjcl.random.addEntropy(window.performance.now(),a,"loadtime"):sjcl.random.addEntropy((new Date).valueOf(),a,"loadtime")}function w(a){a.b=A(a).concat(A(a));a.D=new sjcl.cipher.aes(a.b)}function A(a){for(var b=0;4>b&&!(a.i[b]=a.i[b]+1|0,a.i[b]);b++);return a.D.encrypt(a.i)}function C(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6);
|
||||
a:try{var E,F,G,H;if(H="undefined"!==typeof module){var I;if(I=module.exports){var J;try{J=require("crypto")}catch(K){J=null}I=(F=J)&&F.randomBytes}H=I}if(H)E=F.randomBytes(128),E=new Uint32Array((new Uint8Array(E)).buffer),sjcl.random.addEntropy(E,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){G=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(G);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(G);
|
||||
else break a;sjcl.random.addEntropy(G,1024,"crypto['getRandomValues']")}}catch(L){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(L))}
|
||||
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},Z:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.h({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.h(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||4<
|
||||
f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.h(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a,
|
||||
b,c,d){var e=sjcl.json,f=e.Z.apply(e,arguments);return e.encode(f)},Y:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.h(e.h(e.h({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4<b.iv.length)&&q(new sjcl.exception.invalid("json decrypt: invalid parameters"));
|
||||
"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,b),a=g.key.slice(0,b.ks/32),b.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof f&&(f=sjcl.codec.utf8String.toBits(f));g=new sjcl.cipher[b.cipher](a);f=sjcl.mode[b.mode].decrypt(g,b.ct,b.iv,f,b.ts);e.h(d,b);d.key=a;return 1===c.raw?f:sjcl.codec.utf8String.fromBits(f)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.Y(a,e.decode(b),c,d)},encode:function(a){var b,
|
||||
c="{",d="";for(b in a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||q(new sjcl.exception.invalid("json encode: invalid property name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:q(new sjcl.exception.bug("json encode: unsupported type"))}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^\{.*\}$/)||q(new sjcl.exception.invalid("json decode: this isn't json!"));
|
||||
a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++)(d=a[c].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i))||q(new sjcl.exception.invalid("json decode: this isn't json!")),d[3]?b[d[2]]=parseInt(d[3],10):d[4]?b[d[2]]=d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]):d[5]&&(b[d[2]]="true"===d[5]);return b},h:function(a,b,c){a===r&&(a={});if(b===r)return a;for(var d in b)b.hasOwnProperty(d)&&(c&&(a[d]!==
|
||||
r&&a[d]!==b[d])&&q(new sjcl.exception.invalid("required parameter overridden")),a[d]=b[d]);return a},ga:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},fa:function(a,b){var c={},d;for(d=0;d<b.length;d++)a[b[d]]!==r&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.da={};
|
||||
sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.da,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===r?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
|
86
package.json
86
package.json
|
@ -7,7 +7,8 @@
|
|||
"wallet",
|
||||
"copay",
|
||||
"multisignature",
|
||||
"bitcoin"
|
||||
"bitcoin",
|
||||
"bitcore"
|
||||
],
|
||||
"main": "app.js",
|
||||
"id": "jid1-x7bV5evAaI1P9Q",
|
||||
|
@ -21,73 +22,34 @@
|
|||
"url": "https://github.com/bitpay/copay/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"browser-request": "git://github.com/matiu/browser-request.git#67200dd4ec133457fb7dc69f005540f92b543f0a",
|
||||
"cordova": "^4.1.2",
|
||||
"inherits": "^2.0.1",
|
||||
"lodash": "^2.4.1",
|
||||
"moment": "2.6.0",
|
||||
"optimist": "^0.6.1",
|
||||
"preconditions": "^1.0.7",
|
||||
"querystring": "^0.2.0",
|
||||
"request": "^2.40.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"coverage": "./node_modules/.bin/istanbul cover -x lib/sjcl.js ./node_modules/.bin/_mocha -- --reporter spec test",
|
||||
"test": "sh test/run.sh",
|
||||
"dist": "node shell/scripts/dist.js",
|
||||
"shell": "node shell/scripts/launch.js",
|
||||
"setup-shell": "node shell/scripts/download-atom-shell.js",
|
||||
"postinstall": "./node_modules/.bin/grunt"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-gravatar": "*",
|
||||
"async": "^0.9.0",
|
||||
"bitcore": "git://github.com/bitpay/bitcore.git#e20df2144926dbc20bd468d671e73f8e52bb70c1",
|
||||
"blanket": "^1.1.6",
|
||||
"browser-pack": "^2.0.1",
|
||||
"browserify": "^3.32.1",
|
||||
"buffertools": "^2.0.1",
|
||||
"chai": "^1.9.1",
|
||||
"cli-color": "^0.3.2",
|
||||
"commander": "^2.1.0",
|
||||
"coveralls": "^2.10.0",
|
||||
"express": "^4.10.0",
|
||||
"github-releases": "^0.2.0",
|
||||
"express": "^4.11.2",
|
||||
"fs": "0.0.2",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-angular-gettext": "^0.2.15",
|
||||
"grunt-browserify": "^2.0.8",
|
||||
"grunt-cli": "^0.1.13",
|
||||
"grunt-contrib-concat": "^0.5.0",
|
||||
"grunt-contrib-copy": "^0.7.0",
|
||||
"grunt-contrib-cssmin": "^0.10.0",
|
||||
"grunt-contrib-uglify": "^0.5.1",
|
||||
"grunt-contrib-concat": "^0.5.1",
|
||||
"grunt-contrib-copy": "^0.8.0",
|
||||
"grunt-contrib-cssmin": "^0.12.2",
|
||||
"grunt-contrib-uglify": "^0.8.0",
|
||||
"grunt-contrib-watch": "^0.5.3",
|
||||
"grunt-exec": "*",
|
||||
"grunt-jsdoc": "^0.5.7",
|
||||
"grunt-exec": "^0.4.6",
|
||||
"grunt-markdown": "^0.5.0",
|
||||
"grunt-mocha-test": "^0.8.2",
|
||||
"grunt-release": "^0.7.0",
|
||||
"istanbul": "^0.2.10",
|
||||
"karma": "^0.12.9",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^0.1.3",
|
||||
"shelljs": "^0.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"test": "./node_modules/.bin/grunt test-coveralls"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular": "^1.3.14",
|
||||
"angular-mocks": "^1.3.14",
|
||||
"grunt-karma": "^0.10.1",
|
||||
"grunt-karma-coveralls": "^2.5.3",
|
||||
"karma": "^0.12.31",
|
||||
"karma-cli": "0.0.4",
|
||||
"karma-coverage": "^0.2.7",
|
||||
"karma-firefox-launcher": "^0.1",
|
||||
"karma-mocha": "^0.1.9",
|
||||
"karma-phantomjs-launcher": "^0.1.4",
|
||||
"karma-sinon": "^1.0.3",
|
||||
"load-grunt-tasks": "^0.6.0",
|
||||
"mocha": "^1.18.2",
|
||||
"mocha-lcov-reporter": "^0.0.1",
|
||||
"mock-fs": "^2.3.1",
|
||||
"node-cryptojs-aes": "^0.4.0",
|
||||
"request": "^2.40.0",
|
||||
"shelljs": "^0.3.0",
|
||||
"sinon": "^1.10.3",
|
||||
"sjcl": "*",
|
||||
"socket.io-client": "^1.0.6",
|
||||
"travis-cov": "^0.2.5",
|
||||
"uglifyify": "^1.2.3"
|
||||
"karma-jasmine": "^0.3.5",
|
||||
"karma-phantomjs-launcher": "^0.1.4"
|
||||
}
|
||||
}
|
||||
|
|
32
popup.html
32
popup.html
|
@ -1,32 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Copay</title>
|
||||
<style>
|
||||
body { background-color: #2C3E50; }
|
||||
a {
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: #008cba;
|
||||
color: #fff;
|
||||
display: block;
|
||||
font-family: "Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;
|
||||
margin: 20px auto;
|
||||
opacity: .8;
|
||||
outline: none;
|
||||
padding: 10px;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
width: 200px;
|
||||
}
|
||||
a:hover { opacity: 1; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="index.html" target="_blank">Open wallet</a>
|
||||
<a href="index.html#!/settings" target="_blank">Settings</a>
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue