diff --git a/.bowerrc b/.bowerrc
new file mode 100644
index 0000000..69fad35
--- /dev/null
+++ b/.bowerrc
@@ -0,0 +1,3 @@
+{
+ "directory": "bower_components"
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..c2cdfb8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 2
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..2125666
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1bd7226..0256151 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
node_modules
-*.swp
+.tmp
+.sass-cache
+dist
+bower_components
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..f750969
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,23 @@
+{
+ "node": true,
+ "browser": true,
+ "esnext": true,
+ "bitwise": true,
+ "camelcase": true,
+ "curly": true,
+ "eqeqeq": true,
+ "immed": true,
+ "indent": 2,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "quotmark": "single",
+ "undef": true,
+ "unused": true,
+ "strict": true,
+ "trailing": true,
+ "smarttabs": true,
+ "globals": {
+ "angular": false
+ }
+}
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..6fb6850
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,9 @@
+sudo: false
+language: node_js
+node_js:
+ - 'iojs'
+ - '0.12'
+ - '0.10'
+before_script:
+ - 'npm install -g bower grunt-cli'
+ - 'bower install'
diff --git a/.yo-rc.json b/.yo-rc.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/.yo-rc.json
@@ -0,0 +1 @@
+{}
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..c60862c
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,464 @@
+// Generated on 2015-12-17 using generator-angular 0.11.1
+'use strict';
+
+// # Globbing
+// for performance reasons we're only matching one level down:
+// 'test/spec/{,*/}*.js'
+// use this if you want to recursively match all subfolders:
+// 'test/spec/**/*.js'
+
+module.exports = function (grunt) {
+
+ // Load grunt tasks automatically
+ require('load-grunt-tasks')(grunt);
+
+ // Time how long tasks take. Can help when optimizing build times
+ require('time-grunt')(grunt);
+
+ // Configurable paths for the application
+ var appConfig = {
+ app: require('./bower.json').appPath || 'app',
+ dist: 'dist'
+ };
+
+ // Define the configuration for all the tasks
+ grunt.initConfig({
+
+ // Project settings
+ yeoman: appConfig,
+
+ // Watches files for changes and runs tasks based on the changed files
+ watch: {
+ bower: {
+ files: ['bower.json'],
+ tasks: ['wiredep']
+ },
+ js: {
+ files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
+ // tasks: ['newer:jshint:all'],
+ options: {
+ livereload: '<%= connect.options.livereload %>'
+ }
+ },
+ jsTest: {
+ files: ['test/spec/{,*/}*.js'],
+ tasks: ['newer:jshint:test', 'karma']
+ },
+ styles: {
+ files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
+ tasks: ['newer:copy:styles', 'autoprefixer']
+ },
+ gruntfile: {
+ files: ['Gruntfile.js']
+ },
+ livereload: {
+ options: {
+ livereload: '<%= connect.options.livereload %>'
+ },
+ files: [
+ '<%= yeoman.app %>/{,*/}*.html',
+ '.tmp/styles/{,*/}*.css',
+ '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
+ ]
+ }
+ },
+ buildcontrol: {
+ options: {
+ dir: 'dist',
+ commit: true,
+ push: true,
+ message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
+ },
+ heroku: {
+ options: {
+ remote: 'https://stark-thicket-8728.herokuapp.com/',
+ branch: 'master'
+ }
+ }
+ },
+
+ // The actual grunt server settings
+ connect: {
+ options: {
+ port: 9000,
+ // Change this to '0.0.0.0' to access the server from outside.
+ hostname: 'localhost',
+ livereload: 35729
+ },
+ livereload: {
+ options: {
+ open: true,
+ middleware: function (connect) {
+ return [
+ connect.static('.tmp'),
+ connect().use(
+ '/bower_components',
+ connect.static('./bower_components')
+ ),
+ connect().use(
+ '/app/styles',
+ connect.static('./app/styles')
+ ),
+ connect.static(appConfig.app)
+ ];
+ }
+ }
+ },
+ test: {
+ options: {
+ port: 9001,
+ middleware: function (connect) {
+ return [
+ connect.static('.tmp'),
+ connect.static('test'),
+ connect().use(
+ '/bower_components',
+ connect.static('./bower_components')
+ ),
+ connect.static(appConfig.app)
+ ];
+ }
+ }
+ },
+ dist: {
+ options: {
+ open: true,
+ base: '<%= yeoman.dist %>'
+ }
+ }
+ },
+
+ // Make sure code styles are up to par and there are no obvious mistakes
+ jshint: {
+ options: {
+ jshintrc: '.jshintrc',
+ reporter: require('jshint-stylish')
+ },
+ all: {
+ src: [
+ 'Gruntfile.js',
+ '<%= yeoman.app %>/scripts/{,*/}*.js'
+ ]
+ },
+ test: {
+ options: {
+ jshintrc: 'test/.jshintrc'
+ },
+ src: ['test/spec/{,*/}*.js']
+ }
+ },
+
+ // Empties folders to start fresh
+ clean: {
+ dist: {
+ files: [{
+ dot: true,
+ src: [
+ '.tmp',
+ '<%= yeoman.dist %>/*',
+ '!<%= yeoman.dist %>/.git{,*/}*',
+ '!<%= yeoman.dist %>/Procfile',
+ '!<%= yeoman.dist %>/package.json',
+ '!<%= yeoman.dist %>/web.js',
+ '!<%= yeoman.dist %>/node_modules'
+ ]
+ }]
+ },
+ server: '.tmp'
+ },
+
+ // Add vendor prefixed styles
+ autoprefixer: {
+ options: {
+ browsers: ['last 1 version']
+ },
+ server: {
+ options: {
+ map: true,
+ },
+ files: [{
+ expand: true,
+ cwd: '.tmp/styles/',
+ src: '{,*/}*.css',
+ dest: '.tmp/styles/'
+ }]
+ },
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '.tmp/styles/',
+ src: '{,*/}*.css',
+ dest: '.tmp/styles/'
+ }]
+ }
+ },
+
+ // Automatically inject Bower components into the app
+ wiredep: {
+ app: {
+ src: ['<%= yeoman.app %>/index.html'],
+ ignorePath: /\.\.\//
+ },
+ test: {
+ devDependencies: true,
+ src: '<%= karma.unit.configFile %>',
+ ignorePath: /\.\.\//,
+ fileTypes:{
+ js: {
+ block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi,
+ detect: {
+ js: /'(.*\.js)'/gi
+ },
+ replace: {
+ js: '\'{{filePath}}\','
+ }
+ }
+ }
+ }
+ },
+
+ // Renames files for browser caching purposes
+ filerev: {
+ dist: {
+ src: [
+ '<%= yeoman.dist %>/scripts/{,*/}*.js',
+ '<%= yeoman.dist %>/styles/{,*/}*.css',
+ '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
+ '<%= yeoman.dist %>/styles/fonts/*'
+ ]
+ }
+ },
+
+ // Reads HTML for usemin blocks to enable smart builds that automatically
+ // concat, minify and revision files. Creates configurations in memory so
+ // additional tasks can operate on them
+ useminPrepare: {
+ html: '<%= yeoman.app %>/index.html',
+ options: {
+ dest: '<%= yeoman.dist %>',
+ flow: {
+ html: {
+ steps: {
+ js: ['concat', 'uglifyjs'],
+ css: ['cssmin']
+ },
+ post: {}
+ }
+ }
+ }
+ },
+
+ // Performs rewrites based on filerev and the useminPrepare configuration
+ usemin: {
+ html: ['<%= yeoman.dist %>/{,*/}*.html'],
+ css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
+ options: {
+ assetsDirs: [
+ '<%= yeoman.dist %>',
+ '<%= yeoman.dist %>/images',
+ '<%= yeoman.dist %>/styles'
+ ]
+ }
+ },
+
+ // The following *-min tasks will produce minified files in the dist folder
+ // By default, your `index.html`'s will take care of
+ // minification. These next options are pre-configured if you do not wish
+ // to use the Usemin blocks.
+ // cssmin: {
+ // dist: {
+ // files: {
+ // '<%= yeoman.dist %>/styles/main.css': [
+ // '.tmp/styles/{,*/}*.css'
+ // ]
+ // }
+ // }
+ // },
+ // uglify: {
+ // dist: {
+ // files: {
+ // '<%= yeoman.dist %>/scripts/scripts.js': [
+ // '<%= yeoman.dist %>/scripts/scripts.js'
+ // ]
+ // }
+ // }
+ // },
+ // concat: {
+ // dist: {}
+ // },
+
+ imagemin: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '<%= yeoman.app %>/images',
+ src: '{,*/}*.{png,jpg,jpeg,gif}',
+ dest: '<%= yeoman.dist %>/images'
+ }]
+ }
+ },
+
+ svgmin: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '<%= yeoman.app %>/images',
+ src: '{,*/}*.svg',
+ dest: '<%= yeoman.dist %>/images'
+ }]
+ }
+ },
+
+ htmlmin: {
+ dist: {
+ options: {
+ collapseWhitespace: true,
+ conservativeCollapse: true,
+ collapseBooleanAttributes: true,
+ removeCommentsFromCDATA: true,
+ removeOptionalTags: true
+ },
+ files: [{
+ expand: true,
+ cwd: '<%= yeoman.dist %>',
+ src: ['*.html', 'views/{,*/}*.html'],
+ dest: '<%= yeoman.dist %>'
+ }]
+ }
+ },
+
+ // ng-annotate tries to make the code safe for minification automatically
+ // by using the Angular long form for dependency injection.
+ ngAnnotate: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '.tmp/concat/scripts',
+ src: '*.js',
+ dest: '.tmp/concat/scripts'
+ }]
+ }
+ },
+
+ // Replace Google CDN references
+ cdnify: {
+ dist: {
+ html: ['<%= yeoman.dist %>/*.html']
+ }
+ },
+
+ // Copies remaining files to places other tasks can use
+ copy: {
+ dist: {
+ files: [{
+ expand: true,
+ dot: true,
+ cwd: '<%= yeoman.app %>',
+ dest: '<%= yeoman.dist %>',
+ src: [
+ '*.{ico,png,txt}',
+ '.htaccess',
+ '*.html',
+ 'views/{,*/}*.html',
+ 'images/{,*/}*.{webp}',
+ 'styles/fonts/{,*/}*.*'
+ ]
+ }, {
+ expand: true,
+ cwd: '.tmp/images',
+ dest: '<%= yeoman.dist %>/images',
+ src: ['generated/*']
+ }, {
+ expand: true,
+ cwd: 'bower_components/bootstrap/dist',
+ src: 'fonts/*',
+ dest: '<%= yeoman.dist %>'
+ }]
+ },
+ styles: {
+ expand: true,
+ cwd: '<%= yeoman.app %>/styles',
+ dest: '.tmp/styles/',
+ src: '{,*/}*.css'
+ }
+ },
+
+ // Run some tasks in parallel to speed up the build process
+ concurrent: {
+ server: [
+ 'copy:styles'
+ ],
+ test: [
+ 'copy:styles'
+ ],
+ dist: [
+ 'copy:styles',
+ 'imagemin',
+ 'svgmin'
+ ]
+ },
+
+ // Test settings
+ karma: {
+ unit: {
+ configFile: 'test/karma.conf.js',
+ singleRun: true
+ }
+ }
+ });
+
+
+ grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
+ if (target === 'dist') {
+ return grunt.task.run(['build', 'connect:dist:keepalive']);
+ }
+
+ grunt.task.run([
+ 'clean:server',
+ 'wiredep',
+ 'concurrent:server',
+ 'autoprefixer:server',
+ 'connect:livereload',
+ 'watch'
+ ]);
+ });
+
+ grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
+ grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
+ grunt.task.run(['serve:' + target]);
+ });
+
+ grunt.registerTask('deploy', ['buildcontrol']);
+
+ grunt.registerTask('test', [
+ 'clean:server',
+ 'wiredep',
+ 'concurrent:test',
+ 'autoprefixer',
+ 'connect:test',
+ 'karma'
+ ]);
+
+ grunt.registerTask('build', [
+ 'clean:dist',
+ 'wiredep',
+ 'useminPrepare',
+ 'concurrent:dist',
+ 'autoprefixer',
+ 'concat',
+ 'ngAnnotate',
+ 'copy:dist',
+ 'cdnify',
+ 'cssmin',
+ 'uglify',
+ 'filerev',
+ 'usemin',
+ 'htmlmin'
+ ]);
+
+ grunt.registerTask('default', [
+ 'newer:jshint',
+ 'test',
+ 'build'
+ ]);
+};
diff --git a/Procfile b/Procfile
new file mode 100644
index 0000000..5ec9cc2
--- /dev/null
+++ b/Procfile
@@ -0,0 +1 @@
+web: node index.js
\ No newline at end of file
diff --git a/README.md b/README.md
index a04032f..ae71621 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,18 @@
# Ether Signal
Contract on Testnet:
-http://testnet.etherscan.io/address/0x851a78f09511bf510ad27036f5ff7c8901fdd2e2#code
+http://testnet.etherscan.io/address/0x9e75993a7a9b9f92a1978bcc15c30cbcb967bc81#code
+
+## Build & development
+
+Run `grunt` for building and `grunt serve` for preview.
+
+## Testing
+
+Running `grunt test` will run the unit tests with karma.
+
+To test with Protractor:
+webdriver-manager start
+protractor conf.js
+
+geth --rpc --rpccorsdomain="*" --rpcapi="db,eth,net,web3,personal" --testnet
\ No newline at end of file
diff --git a/app/.buildignore b/app/.buildignore
new file mode 100644
index 0000000..fc98b8e
--- /dev/null
+++ b/app/.buildignore
@@ -0,0 +1 @@
+*.coffee
\ No newline at end of file
diff --git a/app/.htaccess b/app/.htaccess
new file mode 100644
index 0000000..cb84cb9
--- /dev/null
+++ b/app/.htaccess
@@ -0,0 +1,543 @@
+# Apache Configuration File
+
+# (!) Using `.htaccess` files slows down Apache, therefore, if you have access
+# to the main server config file (usually called `httpd.conf`), you should add
+# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
+
+# ##############################################################################
+# # CROSS-ORIGIN RESOURCE SHARING (CORS) #
+# ##############################################################################
+
+# ------------------------------------------------------------------------------
+# | Cross-domain AJAX requests |
+# ------------------------------------------------------------------------------
+
+# Enable cross-origin AJAX requests.
+# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
+# http://enable-cors.org/
+
+#
+# Header set Access-Control-Allow-Origin "*"
+#
+
+# ------------------------------------------------------------------------------
+# | CORS-enabled images |
+# ------------------------------------------------------------------------------
+
+# Send the CORS header for images when browsers request it.
+# https://developer.mozilla.org/en/CORS_Enabled_Image
+# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
+# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
+
+
+
+
+ SetEnvIf Origin ":" IS_CORS
+ Header set Access-Control-Allow-Origin "*" env=IS_CORS
+
+
+
+
+# ------------------------------------------------------------------------------
+# | Web fonts access |
+# ------------------------------------------------------------------------------
+
+# Allow access from all domains for web fonts
+
+
+
+ Header set Access-Control-Allow-Origin "*"
+
+
+
+
+# ##############################################################################
+# # ERRORS #
+# ##############################################################################
+
+# ------------------------------------------------------------------------------
+# | 404 error prevention for non-existing redirected folders |
+# ------------------------------------------------------------------------------
+
+# Prevent Apache from returning a 404 error for a rewrite if a directory
+# with the same name does not exist.
+# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
+# http://www.webmasterworld.com/apache/3808792.htm
+
+Options -MultiViews
+
+# ------------------------------------------------------------------------------
+# | Custom error messages / pages |
+# ------------------------------------------------------------------------------
+
+# You can customize what Apache returns to the client in case of an error (see
+# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
+
+ErrorDocument 404 /404.html
+
+
+# ##############################################################################
+# # INTERNET EXPLORER #
+# ##############################################################################
+
+# ------------------------------------------------------------------------------
+# | Better website experience |
+# ------------------------------------------------------------------------------
+
+# Force IE to render pages in the highest available mode in the various
+# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
+
+
+ Header set X-UA-Compatible "IE=edge"
+ # `mod_headers` can't match based on the content-type, however, we only
+ # want to send this header for HTML pages and not for the other resources
+
+ Header unset X-UA-Compatible
+
+
+
+# ------------------------------------------------------------------------------
+# | Cookie setting from iframes |
+# ------------------------------------------------------------------------------
+
+# Allow cookies to be set from iframes in IE.
+
+#
+# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
+#
+
+# ------------------------------------------------------------------------------
+# | Screen flicker |
+# ------------------------------------------------------------------------------
+
+# Stop screen flicker in IE on CSS rollovers (this only works in
+# combination with the `ExpiresByType` directives for images from below).
+
+# BrowserMatch "MSIE" brokenvary=1
+# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
+# BrowserMatch "Opera" !brokenvary
+# SetEnvIf brokenvary 1 force-no-vary
+
+
+# ##############################################################################
+# # MIME TYPES AND ENCODING #
+# ##############################################################################
+
+# ------------------------------------------------------------------------------
+# | Proper MIME types for all files |
+# ------------------------------------------------------------------------------
+
+
+
+ # Audio
+ AddType audio/mp4 m4a f4a f4b
+ AddType audio/ogg oga ogg
+
+ # JavaScript
+ # Normalize to standard type (it's sniffed in IE anyways):
+ # http://tools.ietf.org/html/rfc4329#section-7.2
+ AddType application/javascript js jsonp
+ AddType application/json json
+
+ # Video
+ AddType video/mp4 mp4 m4v f4v f4p
+ AddType video/ogg ogv
+ AddType video/webm webm
+ AddType video/x-flv flv
+
+ # Web fonts
+ AddType application/font-woff woff
+ AddType application/vnd.ms-fontobject eot
+
+ # Browsers usually ignore the font MIME types and sniff the content,
+ # however, Chrome shows a warning if other MIME types are used for the
+ # following fonts.
+ AddType application/x-font-ttf ttc ttf
+ AddType font/opentype otf
+
+ # Make SVGZ fonts work on iPad:
+ # https://twitter.com/FontSquirrel/status/14855840545
+ AddType image/svg+xml svg svgz
+ AddEncoding gzip svgz
+
+ # Other
+ AddType application/octet-stream safariextz
+ AddType application/x-chrome-extension crx
+ AddType application/x-opera-extension oex
+ AddType application/x-shockwave-flash swf
+ AddType application/x-web-app-manifest+json webapp
+ AddType application/x-xpinstall xpi
+ AddType application/xml atom rdf rss xml
+ AddType image/webp webp
+ AddType image/x-icon ico
+ AddType text/cache-manifest appcache manifest
+ AddType text/vtt vtt
+ AddType text/x-component htc
+ AddType text/x-vcard vcf
+
+
+
+# ------------------------------------------------------------------------------
+# | UTF-8 encoding |
+# ------------------------------------------------------------------------------
+
+# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
+AddDefaultCharset utf-8
+
+# Force UTF-8 for certain file formats.
+
+ AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
+
+
+
+# ##############################################################################
+# # URL REWRITES #
+# ##############################################################################
+
+# ------------------------------------------------------------------------------
+# | Rewrite engine |
+# ------------------------------------------------------------------------------
+
+# Turning on the rewrite engine and enabling the `FollowSymLinks` option is
+# necessary for the following directives to work.
+
+# If your web host doesn't allow the `FollowSymlinks` option, you may need to
+# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
+# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
+
+# Also, some cloud hosting services require `RewriteBase` to be set:
+# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
+
+
+ Options +FollowSymlinks
+ # Options +SymLinksIfOwnerMatch
+ RewriteEngine On
+ # RewriteBase /
+
+
+# ------------------------------------------------------------------------------
+# | Suppressing / Forcing the "www." at the beginning of URLs |
+# ------------------------------------------------------------------------------
+
+# The same content should never be available under two different URLs especially
+# not with and without "www." at the beginning. This can cause SEO problems
+# (duplicate content), therefore, you should choose one of the alternatives and
+# redirect the other one.
+
+# By default option 1 (no "www.") is activated:
+# http://no-www.org/faq.php?q=class_b
+
+# If you'd prefer to use option 2, just comment out all the lines from option 1
+# and uncomment the ones from option 2.
+
+# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+# Option 1: rewrite www.example.com → example.com
+
+
+ RewriteCond %{HTTPS} !=on
+ RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+ RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+# Option 2: rewrite example.com → www.example.com
+
+# Be aware that the following might not be a good idea if you use "real"
+# subdomains for certain parts of your website.
+
+#
+# RewriteCond %{HTTPS} !=on
+# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
+# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
+#
+
+
+# ##############################################################################
+# # SECURITY #
+# ##############################################################################
+
+# ------------------------------------------------------------------------------
+# | Content Security Policy (CSP) |
+# ------------------------------------------------------------------------------
+
+# You can mitigate the risk of cross-site scripting and other content-injection
+# attacks by setting a Content Security Policy which whitelists trusted sources
+# of content for your site.
+
+# The example header below allows ONLY scripts that are loaded from the current
+# site's origin (no inline scripts, no CDN, etc). This almost certainly won't
+# work as-is for your site!
+
+# To get all the details you'll need to craft a reasonable policy for your site,
+# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
+# see the specification: http://w3.org/TR/CSP).
+
+#
+# Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
+#
+# Header unset Content-Security-Policy
+#
+#
+
+# ------------------------------------------------------------------------------
+# | File access |
+# ------------------------------------------------------------------------------
+
+# Block access to directories without a default document.
+# Usually you should leave this uncommented because you shouldn't allow anyone
+# to surf through every directory on your server (which may includes rather
+# private places like the CMS's directories).
+
+
+ Options -Indexes
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+# Block access to hidden files and directories.
+# This includes directories used by version control systems such as Git and SVN.
+
+
+ RewriteCond %{SCRIPT_FILENAME} -d [OR]
+ RewriteCond %{SCRIPT_FILENAME} -f
+ RewriteRule "(^|/)\." - [F]
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+# Block access to backup and source files.
+# These files may be left by some text editors and can pose a great security
+# danger when anyone has access to them.
+
+
+ Order allow,deny
+ Deny from all
+ Satisfy All
+
+
+# ------------------------------------------------------------------------------
+# | Secure Sockets Layer (SSL) |
+# ------------------------------------------------------------------------------
+
+# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
+# prevent `https://www.example.com` when your certificate only allows
+# `https://secure.example.com`.
+
+#
+# RewriteCond %{SERVER_PORT} !^443
+# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
+#
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+# Force client-side SSL redirection.
+
+# If a user types "example.com" in his browser, the above rule will redirect him
+# to the secure version of the site. That still leaves a window of opportunity
+# (the initial HTTP connection) for an attacker to downgrade or redirect the
+# request. The following header ensures that browser will ONLY connect to your
+# server via HTTPS, regardless of what the users type in the address bar.
+# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
+
+#
+# Header set Strict-Transport-Security max-age=16070400;
+#
+
+# ------------------------------------------------------------------------------
+# | Server software information |
+# ------------------------------------------------------------------------------
+
+# Avoid displaying the exact Apache version number, the description of the
+# generic OS-type and the information about Apache's compiled-in modules.
+
+# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
+
+# ServerTokens Prod
+
+
+# ##############################################################################
+# # WEB PERFORMANCE #
+# ##############################################################################
+
+# ------------------------------------------------------------------------------
+# | Compression |
+# ------------------------------------------------------------------------------
+
+
+
+ # Force compression for mangled headers.
+ # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
+
+
+ SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
+ RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
+
+
+
+ # Compress all output labeled with one of the following MIME-types
+ # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
+ # and can remove the `` and ` ` lines
+ # as `AddOutputFilterByType` is still in the core directives).
+
+ AddOutputFilterByType DEFLATE application/atom+xml \
+ application/javascript \
+ application/json \
+ application/rss+xml \
+ application/vnd.ms-fontobject \
+ application/x-font-ttf \
+ application/x-web-app-manifest+json \
+ application/xhtml+xml \
+ application/xml \
+ font/opentype \
+ image/svg+xml \
+ image/x-icon \
+ text/css \
+ text/html \
+ text/plain \
+ text/x-component \
+ text/xml
+
+
+
+
+# ------------------------------------------------------------------------------
+# | Content transformations |
+# ------------------------------------------------------------------------------
+
+# Prevent some of the mobile network providers from modifying the content of
+# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
+
+#
+# Header set Cache-Control "no-transform"
+#
+
+# ------------------------------------------------------------------------------
+# | ETag removal |
+# ------------------------------------------------------------------------------
+
+# Since we're sending far-future expires headers (see below), ETags can
+# be removed: http://developer.yahoo.com/performance/rules.html#etags.
+
+# `FileETag None` is not enough for every server.
+
+ Header unset ETag
+
+
+FileETag None
+
+# ------------------------------------------------------------------------------
+# | Expires headers (for better cache control) |
+# ------------------------------------------------------------------------------
+
+# The following expires headers are set pretty far in the future. If you don't
+# control versioning with filename-based cache busting, consider lowering the
+# cache time for resources like CSS and JS to something like 1 week.
+
+
+
+ ExpiresActive on
+ ExpiresDefault "access plus 1 month"
+
+ # CSS
+ ExpiresByType text/css "access plus 1 year"
+
+ # Data interchange
+ ExpiresByType application/json "access plus 0 seconds"
+ ExpiresByType application/xml "access plus 0 seconds"
+ ExpiresByType text/xml "access plus 0 seconds"
+
+ # Favicon (cannot be renamed!)
+ ExpiresByType image/x-icon "access plus 1 week"
+
+ # HTML components (HTCs)
+ ExpiresByType text/x-component "access plus 1 month"
+
+ # HTML
+ ExpiresByType text/html "access plus 0 seconds"
+
+ # JavaScript
+ ExpiresByType application/javascript "access plus 1 year"
+
+ # Manifest files
+ ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
+ ExpiresByType text/cache-manifest "access plus 0 seconds"
+
+ # Media
+ ExpiresByType audio/ogg "access plus 1 month"
+ ExpiresByType image/gif "access plus 1 month"
+ ExpiresByType image/jpeg "access plus 1 month"
+ ExpiresByType image/png "access plus 1 month"
+ ExpiresByType video/mp4 "access plus 1 month"
+ ExpiresByType video/ogg "access plus 1 month"
+ ExpiresByType video/webm "access plus 1 month"
+
+ # Web feeds
+ ExpiresByType application/atom+xml "access plus 1 hour"
+ ExpiresByType application/rss+xml "access plus 1 hour"
+
+ # Web fonts
+ ExpiresByType application/font-woff "access plus 1 month"
+ ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
+ ExpiresByType application/x-font-ttf "access plus 1 month"
+ ExpiresByType font/opentype "access plus 1 month"
+ ExpiresByType image/svg+xml "access plus 1 month"
+
+
+
+# ------------------------------------------------------------------------------
+# | Filename-based cache busting |
+# ------------------------------------------------------------------------------
+
+# If you're not using a build process to manage your filename version revving,
+# you might want to consider enabling the following directives to route all
+# requests such as `/css/style.12345.css` to `/css/style.css`.
+
+# To understand why this is important and a better idea than `*.css?v231`, read:
+# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
+
+#
+# RewriteCond %{REQUEST_FILENAME} !-f
+# RewriteCond %{REQUEST_FILENAME} !-d
+# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
+#
+
+# ------------------------------------------------------------------------------
+# | File concatenation |
+# ------------------------------------------------------------------------------
+
+# Allow concatenation from within specific CSS and JS files, e.g.:
+# Inside of `script.combined.js` you could have
+#
+#
+# and they would be included into this single file.
+
+#
+#
+# Options +Includes
+# AddOutputFilterByType INCLUDES application/javascript application/json
+# SetOutputFilter INCLUDES
+#
+#
+# Options +Includes
+# AddOutputFilterByType INCLUDES text/css
+# SetOutputFilter INCLUDES
+#
+#
+
+# ------------------------------------------------------------------------------
+# | Persistent connections |
+# ------------------------------------------------------------------------------
+
+# Allow multiple requests to be sent over the same TCP connection:
+# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
+
+# Enable if you serve a lot of static content but, be aware of the
+# possible disadvantages!
+
+#
+# Header set Connection Keep-Alive
+#
diff --git a/app/404.html b/app/404.html
new file mode 100644
index 0000000..f5c3819
--- /dev/null
+++ b/app/404.html
@@ -0,0 +1,157 @@
+
+
+
+
+ Page Not Found :(
+
+
+
+
+
Not found :(
+
Sorry, but the page you were trying to view does not exist.
+
It looks like this was the result of either:
+
+ a mistyped address
+ an out-of-date link
+
+
+
+
+
+
diff --git a/app/favicon.ico b/app/favicon.ico
new file mode 100644
index 0000000..6527905
Binary files /dev/null and b/app/favicon.ico differ
diff --git a/app/images/ajax.gif b/app/images/ajax.gif
new file mode 100644
index 0000000..1455a25
Binary files /dev/null and b/app/images/ajax.gif differ
diff --git a/app/images/ethersignal-logo.svg b/app/images/ethersignal-logo.svg
new file mode 100644
index 0000000..b28e6c4
--- /dev/null
+++ b/app/images/ethersignal-logo.svg
@@ -0,0 +1,14 @@
+
+
+
+ Trial 2
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/index.html b/app/index.html
new file mode 100644
index 0000000..6eb3d07
--- /dev/null
+++ b/app/index.html
@@ -0,0 +1,206 @@
+
+
+
+
+ EtherSignal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/package.json b/app/package.json
new file mode 100644
index 0000000..48f0bda
--- /dev/null
+++ b/app/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "EtherSignal",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "express": "^4.13.3",
+ "gzippo": "^0.2.0"
+ }
+}
diff --git a/app/robots.txt b/app/robots.txt
new file mode 100644
index 0000000..9417495
--- /dev/null
+++ b/app/robots.txt
@@ -0,0 +1,3 @@
+# robotstxt.org
+
+User-agent: *
diff --git a/app/scripts/app.js b/app/scripts/app.js
new file mode 100644
index 0000000..66a6945
--- /dev/null
+++ b/app/scripts/app.js
@@ -0,0 +1,41 @@
+'use strict';
+
+/**
+ * @ngdoc overview
+ * @name nohoApp
+ * @description
+ * # nohoApp
+ *
+ * Main module of the application.
+ */
+
+
+
+var app = angular.module('nohoApp', [
+ 'ngAnimate',
+ 'ngCookies',
+ 'ngResource',
+ 'ngRoute',
+ 'ngSanitize',
+ 'ngTouch',
+ 'ui.bootstrap',
+ 'angular-scroll-animate'
+ ]);
+app.config(function ($routeProvider) {
+ $routeProvider
+ .when('/', {
+ templateUrl: 'views/main.html',
+ controller: 'MainCtrl'
+ })
+ .when('/about', {
+ templateUrl: 'views/about.html',
+ controller: 'AboutCtrl'
+ })
+ .when('/cliquickstart', {
+ templateUrl: 'views/cliquickstart.html',
+ controller: 'AboutCtrl'
+ })
+ .otherwise({
+ redirectTo: '/'
+ });
+});
diff --git a/app/scripts/controllers/about.js b/app/scripts/controllers/about.js
new file mode 100644
index 0000000..2a11da2
--- /dev/null
+++ b/app/scripts/controllers/about.js
@@ -0,0 +1,11 @@
+'use strict';
+
+/**
+ * @ngdoc function
+ * @name nohoApp.controller:AboutCtrl
+ * @description
+ * # AboutCtrl
+ * Controller of the nohoApp
+ */
+app.controller('AboutCtrl', function ($scope) {
+});
diff --git a/app/scripts/controllers/main.js b/app/scripts/controllers/main.js
new file mode 100644
index 0000000..5cb5bb6
--- /dev/null
+++ b/app/scripts/controllers/main.js
@@ -0,0 +1,20 @@
+'use strict';
+
+/**
+ * @ngdoc function
+ * @name nohoApp.controller:MainCtrl
+ * @description
+ * # MainCtrl
+ * Controller of the nohoApp
+ */
+
+
+app.controller('MainCtrl', function ($scope, $rootScope) {
+
+ $scope.title = "EtherSignal"
+ $rootScope.alerts = [];
+ $scope.closeAlert = function(index) {
+ $scope.alerts.splice(index, 1);
+ };
+
+});
\ No newline at end of file
diff --git a/app/scripts/newether.js b/app/scripts/newether.js
new file mode 100644
index 0000000..76e42dd
--- /dev/null
+++ b/app/scripts/newether.js
@@ -0,0 +1,396 @@
+var isMist = typeof web3 !== 'undefined';
+var obj = obj || "";
+var web3;
+if (typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {
+ // If there's a web3 library loaded, then make your own web3
+ web3 = new Web3(web3.currentProvider);
+} else if (typeof Web3 !== 'undefined') {
+ // If there isn't then set a provider
+ web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
+ if(!web3.isConnected()){
+ var Web3 = require('web3');
+ web3 = new Web3(new Web3.providers.HttpProvider("https://signal.ether.ai/proxy"));
+ }
+}
+
+var ethersignalContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"pro","type":"bool"}],"name":"setSignal","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"endSignal","outputs":[],"type":"function"},{"inputs":[{"name":"rAddr","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pro","type":"bool"},{"indexed":false,"name":"addr","type":"address"}],"name":"LogSignal","type":"event"},{"anonymous":false,"inputs":[],"name":"EndSignal","type":"event"}]);
+
+var positionregistryContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"title","type":"string"},{"name":"text","type":"string"}],"name":"registerPosition","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"regAddr","type":"address"},{"indexed":true,"name":"sigAddr","type":"address"},{"indexed":false,"name":"title","type":"string"},{"indexed":false,"name":"text","type":"string"}],"name":"LogPosition","type":"event"}]);
+var to = '0x9e75993a7a9b9f92a1978bcc15c30cbcb967bc81';
+var positionregistry = positionregistryContract.at(to);
+
+
+var connected = false;
+
+
+app.directive('networkStats', ['ethereum', '$interval','$rootScope', function(ethereum, $interval, $rootScope) {
+ return {
+ restrict: 'E',
+ templateUrl: 'new-networkstats.html',
+ link: function(scope) {
+ $interval(function() {
+
+ if ($rootScope.pending)
+ return;
+ else if (scope.sinceLastBlock <= 20)
+ scope.syncState = 'good';
+ else if (scope.sinceLastBlock > 20 && scope.sinceLastBlock < 60)
+ scope.syncState = 'warning';
+ else
+ scope.syncState = 'bad';
+ scope.sinceLastBlock += 1;
+ }, 1000);
+
+ }
+ }
+}]);
+
+app.directive('accountSelector', ['ethereum','ethSignalContract','$rootScope', function(ethereum, ethSignalContract, $rootScope) {
+ return {
+ restrict: 'E',
+ templateUrl: 'new-accountselector.html',
+ link: function(scope) {
+ var contract = ethSignalContract;
+ scope.accounts = ethereum.accounts;
+ scope.user = {defaultAccount:'coinbase'};
+ scope.$watch('user.defaultAccount', function(newVal, oldVal) {
+ //console.log(contract);
+ if(oldVal == newVal)
+ return;
+ //console.log("Setting defaultAccount to: ", newVal);
+ ethereum.web3.eth.defaultAccount = newVal;
+ });
+
+ scope.newProposal = function() {
+ console.log("newProposal");
+ $rootScope.newProposals = []
+ $('#submitPositionModal').modal('show')
+ $rootScope.newProposals.push({name:"", description:""});
+ };
+ }
+ }
+}]);
+
+app.directive('proposalsList', ['proposalService','ethereum','$uibModal','$rootScope', function(proposalService, ethereum, $uibModal, $rootScope) {
+
+ return {
+ restrict: 'E',
+ templateUrl: 'new-proposalslist.html',
+ link: function(scope) {
+ scope.proposals = proposalService.proposals;
+ scope.percentage = function(a, b){
+ return a + b;
+ }
+ scope.cancel = function() {
+ $('#submitPositionModal').modal('hide')
+ // $rootScope.newProposals = [];
+ }
+ scope.vote = function(proposalId, position) {
+ if(angular.isUndefined(ethereum.web3.eth.defaultAccount)){
+ $rootScope.alerts = [{ type: 'danger', msg: 'Please select an account' },];
+ // alert("Please select an account to from the \"Select Account\" dropdown.");
+ return
+ }
+ proposalService.vote(proposalId, position);
+ };
+
+ scope.createProposal = function(proposal) {
+ if(angular.isUndefined(ethereum.web3.eth.defaultAccount)){
+ $rootScope.alerts = [{ type: 'danger', msg: 'Cannot find an account.' },];
+ return
+ }
+ if (proposal.name == ""){
+ scope.invalidForm = true;
+ return
+ }
+
+ scope.invalidForm = false;
+ proposalService.newProposal(proposal);
+ };
+ }
+ }
+}]);
+
+app.directive('showErrors', function() {
+ return {
+ restrict: 'A',
+ link: function(scope, el) {
+ el.bind('blur', function() {
+ var valid = // is valid logic
+ el.toggleClass('has-error', valid);
+ });
+ }
+ }
+});
+
+app.service('ethereum', function($rootScope, $interval, $timeout) {
+ // TODO graceful connection handling
+ $rootScope.isMist = isMist;
+ // convert block timestamps into human readable strings
+ function utcSecondsToString(timestamp) {
+ var date = new Date(0);
+ date.setUTCSeconds(timestamp);
+ return date.toString();
+ }
+ function getCurrentNetwork(networkId) {
+ if (networkId == 1)
+ return 'Main-Net';
+ if (networkId == 2)
+ return 'Test-Net';
+ }
+ function getConnectionStatus(connected) {
+ if (connected)
+ return 'connected';
+ return 'disconnected';
+ }
+
+ function getEtherscanUrl(networkId) {
+ if(networkId == 1)
+ return 'https://etherscan.io/tx';
+ if (networkId == 2)
+ return 'https://testnet.etherscan.io/tx';
+ }
+ // get blockchain stats to display to the user
+ function watchNetworkStats() {
+ var latest = web3.eth.filter('latest');
+ latest.watch(function(err,blockHash){
+ web3.eth.getBlock(blockHash, false, function(err, block) {
+ $rootScope.pending = false;
+ $rootScope.currentBlock = block.number;
+ $rootScope.currentBlockTime = utcSecondsToString(block.timestamp);
+ $rootScope.sinceLastBlock = -1;
+ });
+ });
+ }
+
+ $rootScope.pending = true;
+ $rootScope.syncState = 'warning';
+ $rootScope.currentBlock = 'SYNCING';
+ $rootScope.currentBlockTime = 'SYNCING';
+ $rootScope.sinceLastBlock = 0;
+ $rootScope.minDeposit = 0;
+ $interval(function() {
+ var newState = web3.isConnected();
+ if (newState != connected){
+ $rootScope.$emit('connectionStateChanged', newState);
+ if(newState){
+ if(!$rootScope.isMist) watchNetworkStats();
+ web3.eth.defaultAccount = web3.eth.accounts[0];
+ $rootScope.ethereumNetwork = getCurrentNetwork(web3.version.network);
+ $rootScope.etherscanUrl = getEtherscanUrl(web3.version.network);
+ }
+ $rootScope.connectionStateDisplay = getConnectionStatus(newState);
+ $rootScope.connectionState = newState;
+ connected = newState;
+ }
+ }, 1000);
+
+ var accounts = null;
+ return {
+ //make web3 available as a property of this service
+ web3: web3,
+ accounts: accounts
+ };
+});
+
+
+app.service('ethSignalContract',['ethereum', function(ethereum) {
+ var web3 = ethereum.web3;
+ var abi = [ { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "proposals", "outputs": [ { "name": "name", "type": "string", "value": "Do you support the hard fork?" }, { "name": "description", "type": "string", "value": "\"Decentralization without decentralized social responsibility is terrifying\"\\n-Vlad Zamfir" }, { "name": "creator", "type": "address", "value": "0xb2445f120a5a4fe73c4ca9b3a73f415371b5656b" }, { "name": "active", "type": "bool", "value": true } ], "type": "function" }, { "constant": true, "inputs": [], "name": "numberOfProposals", "outputs": [ { "name": "_numberOfProposals", "type": "uint256" } ], "type": "function" }, { "constant": false, "inputs": [ { "name": "proposalID", "type": "uint256" }, { "name": "position", "type": "bool" } ], "name": "vote", "outputs": [], "type": "function" }, { "constant": false, "inputs": [ { "name": "proposalName", "type": "string" }, { "name": "proposalDescription", "type": "string" } ], "name": "newProposal", "outputs": [], "type": "function" }, { "inputs": [], "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "proposalID", "type": "uint256" }, { "indexed": false, "name": "position", "type": "bool" }, { "indexed": false, "name": "voter", "type": "address" } ], "name": "userVotedEvent", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "proposalName", "type": "string" }, { "indexed": false, "name": "proposalDescription", "type": "string" }, { "indexed": false, "name": "creator", "type": "address" } ], "name": "proposalCreatedEvent", "type": "event" } ];
+ return web3.eth.contract(abi).at(to);
+}]);
+
+
+app.service('proposalService', ['ethSignalContract', '$q','ethereum','$rootScope', function(ethSignalContract, $q, ethereum, $rootScope) {
+ // get all the questions
+
+ $rootScope.animateElementIn = function($el) {
+ $el.removeClass('not-visible');
+ $el.addClass('animated fadeIn'); // this example leverages animate.css classes
+ }
+ $rootScope.animateElementOut = function($el) {
+ // $el.addClass('not-visible');
+ // $el.removeClass('fadeIn');
+ }
+ var positions = [];
+ $rootScope.newProposals = [];
+ $rootScope.$on('connectionStateChanged', function(evt, connected){
+ if(connected) getPositions();
+ })
+
+ $rootScope.minDepositChanged = function(minDeposit) {
+ if (typeof minDeposit !== undefined)
+ {
+ $rootScope.minDeposit = minDeposit;
+ if(connected) getPositions();
+ } else {
+ $rootScope.minDeposit = 0;
+ }
+ }
+
+ function getPositions() {
+ while (positions.pop());
+ positionregistry.LogPosition({}, {fromBlock:1200000}).get(function(err,evt) {
+ if (err) console.warn()
+
+ var obj;
+ var dep;
+ for (obj in evt) {
+ var block = web3.eth.getBlock(evt[obj].blockNumber);
+ dep = Number(web3.fromWei(web3.eth.getBalance(evt[obj].args.sigAddr), "finney"));
+ if (dep >= $rootScope.minDeposit) {
+ //console.log(evt[obj]);
+ getSigList(evt[obj], dep, block)
+ }
+ }
+ });
+ }
+
+ function getSigList(input, dep, block){
+ var address = input.args.sigAddr
+ // console.log("getSigList", address);
+ var etherSig = ethersignalContract.at(address)
+ etherSig.LogSignal({}, {fromBlock:input.blockNumber}).get(function(err,evt) {
+ if (err) console.warn("warning")
+
+ var proMap = {};
+ var antiMap = {};
+
+ var obj;
+ for (obj in evt){
+ if (evt[obj].args.pro) {
+ proMap[evt[obj].args.addr] = 1;
+ antiMap[evt[obj].args.addr] = 0;
+ } else {
+ proMap[evt[obj].args.addr] = 0;
+ antiMap[evt[obj].args.addr] = 1;
+ }
+ }
+ CalcSignal(proMap, antiMap, input, dep, block)
+ })
+ }
+
+ function calcPercent(A, B){
+ var res = []
+ res[0] = Math.round(A * 100.0 / (A + B))
+ res[1] = Math.round(B * 100.0 / (A + B))
+ return res
+ }
+
+ function CalcSignal(proMap, antiMap, input, dep, block) {
+ var totalPro = 0;
+ var totalAgainst = 0;
+ var isMine = false;
+ var iHaveSignaled = false;
+ // call getBalance just once per address
+ Object.keys(proMap).map(function(a) {
+ var bal = web3.fromWei(web3.eth.getBalance(a));
+ proMap[a] = proMap[a] * bal;
+ antiMap[a] = antiMap[a] * bal;
+
+ var idx;
+ for (idx in web3.eth.accounts) {
+ if (web3.eth.accounts[idx] === a) { iHaveSignaled = true; }
+ }
+ });
+ var idx;
+ for (idx in web3.eth.accounts) {
+ if (web3.eth.accounts[idx] === input.args.regAddr) { isMine = true; }
+ }
+
+ // sum the pro and anti account values
+ Object.keys(proMap).map(function(a) { totalPro += parseFloat(proMap[a]); });
+ Object.keys(antiMap).map(function(a) { totalAgainst += parseFloat(antiMap[a]); });
+
+ // console.log(totalPro);
+ // console.log(totalAgainst);
+ var percent = calcPercent( totalPro, totalAgainst );
+
+ positions.push({title: input.args.title, desc: input.args.text, regAddr: input.args.regAddr, pro: Math.round(totalPro), against: Math.round(totalAgainst), percent: percent, sigAddr: input.args.sigAddr, deposit: dep, time: block.timestamp, iHaveSignaled: iHaveSignaled, isMine: isMine})
+ console.log(positions);
+ }
+
+ return {
+ proposals: positions,
+ // votes: votes,
+ percentage: function(inP){
+ //console.log(out);
+ },
+ vote: function(posSigAddr, proBool) {
+ //console.log(posSigAddr, proBool);
+ var etherSig = ethersignalContract.at(posSigAddr)
+ for (idx in web3.eth.accounts)
+ {
+ var from = web3.eth.accounts[idx];
+ try {
+ $rootScope.lastTx = etherSig.setSignal(proBool, {from: from});
+ }
+ catch(e) {
+ console.log("Error submitting signal");
+ console.log(e);
+ $rootScope.alerts.push({ type: 'danger', msg: 'Error sending signal' });
+ }
+ $rootScope.alerts.push({ type: 'success', msg: 'Signal sent!' });
+ }
+ },
+ newProposal: function(proposal) {
+ console.log("newProposal proposalService");
+
+ //console.log(ethereum.web3.eth.defaultAccount);
+ from = ethereum.web3.eth.defaultAccount;
+ // var data = ethSignalContract.newProposal.getData(proposal.name, proposal.description);
+ var data = positionregistry.registerPosition.getData(proposal.name, proposal.description);
+ //console.log("data ", data);
+ var gas = ethereum.web3.eth.estimateGas({from:from, to:to, data:data});
+
+ //console.log("gas: ", gas);
+ try {
+ $rootScope.lastTx = positionregistry.registerPosition.sendTransaction(proposal.name, proposal.description, {from:from, to:to, gas:gas});
+ }
+ catch(e) {
+ console.log("Error submitting position");
+ console.log(e);
+ $rootScope.alerts.push({ type: 'danger', msg: 'Error sending position' });
+ }
+ $rootScope.alerts.push({ type: 'success', msg: 'Position sent!' });
+ $('#submitPositionModal').modal('hide')
+ $rootScope.newProposals = [];
+ }
+ }
+
+}]);
+
+
+// Global search filter
+app.filter('searchFilter',function($filter) {
+ return function(items,searchfilter) {
+ var isSearchFilterEmpty = true;
+ angular.forEach(searchfilter, function(searchstring) {
+ if(searchstring !=null && searchstring !=""){
+ isSearchFilterEmpty= false;
+ }
+ });
+ if(!isSearchFilterEmpty){
+ var result = [];
+ angular.forEach(items, function(item) {
+ var isFound = false;
+ angular.forEach(item, function(term,key) {
+ if(term != null && !isFound){
+ term = term.toString();
+ term = term.toLowerCase();
+ angular.forEach(searchfilter, function(searchstring) {
+ searchstring = searchstring.toLowerCase();
+ if(searchstring !="" && term.indexOf(searchstring) !=-1 && !isFound){
+ result.push(item);
+ isFound = true;
+ }
+ });
+ }
+ });
+ });
+ return result;
+ }else{
+ return items;
+ }
+ }
+});
diff --git a/app/styles/main.css b/app/styles/main.css
new file mode 100644
index 0000000..ee24168
--- /dev/null
+++ b/app/styles/main.css
@@ -0,0 +1,160 @@
+/* Sticky footer styles
+-------------------------------------------------- */
+html {
+ position: relative;
+ min-height: 100%;
+}
+body {
+ /* Margin bottom by footer height */
+ margin-bottom: 60px;
+}
+.footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ /* Set the fixed height of the footer here */
+ height: 60px;
+ background-color: #f5f5f5;
+}
+
+
+
+
+.browsehappy {
+ margin: 0.2em 0;
+ background: #ccc;
+ color: #000;
+ padding: 0.2em 0;
+}
+
+body {
+ padding: 0;
+}
+
+/* Everything but the jumbotron gets side spacing for mobile first views */
+.header,
+.marketing,
+.footer {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+/* Custom page header */
+.header {
+ /*border-bottom: 1px solid #e5e5e5;*/
+ margin-bottom: 10px;
+}
+/* Make the masthead heading the same height as the navigation */
+.header h3 {
+ margin-top: 0;
+ margin-bottom: 0;
+ line-height: 40px;
+ padding-bottom: 19px;
+}
+
+/* Custom page footer */
+.footer {
+ padding-top: 19px;
+ color: #777;
+ border-top: 1px solid #e5e5e5;
+}
+
+.container-narrow > hr {
+ margin: 30px 0;
+}
+
+/* Main marketing message and sign up button */
+.jumbotron {
+ text-align: center;
+ border-bottom: 1px solid #e5e5e5;
+}
+.jumbotron .btn {
+ font-size: 21px;
+ padding: 14px 24px;
+}
+
+/* Supporting marketing content */
+.marketing {
+ margin: 40px 0;
+}
+.marketing p + h4 {
+ margin-top: 28px;
+}
+
+/* Responsive: Portrait tablets and up */
+@media screen and (min-width: 768px) {
+ .container {
+ max-width: 730px;
+ }
+
+ /* Remove the padding we set earlier */
+ .header,
+ .marketing,
+ .footer {
+ padding-left: 0;
+ padding-right: 0;
+ }
+ /* Space out the masthead */
+ .header {
+ margin-bottom: 30px;
+ }
+ /* Remove the bottom border on the jumbotron for visual effect */
+ .jumbotron {
+ border-bottom: 0;
+ }
+}
+
+
+.question {
+ font-size: 1.5em;
+ padding:0 0 0 1em;
+}
+
+.light {
+ color: #999;
+}
+
+.navbar-brand>img.logoImg {
+ max-width: 17px;
+ display: inline-block;
+}
+
+#voteButtons .btn span {
+ font-family: sans-serif;
+}
+.stats {
+ color:#666;
+ font-size: .8em;
+}
+
+
+.modal {
+ text-align: center;
+ padding: 0!important;
+}
+
+.modal:before {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle;
+ margin-right: -4px;
+}
+
+.modal-dialog {
+ display: inline-block;
+ text-align: left;
+ vertical-align: middle;
+}
+
+.not-visible {
+ visibility: hidden;
+}
+
+
+
+
+
+
+
+
diff --git a/app/views/about.html b/app/views/about.html
new file mode 100644
index 0000000..430f2cc
--- /dev/null
+++ b/app/views/about.html
@@ -0,0 +1,16 @@
+
+
+
+
About
+
+ EtherSignal intends to allow the Ethereum community to signal on their
+ interests, negatively or positively in positions.
+
+
+ Practically it allows for the creation and discovery of positions, along
+ with the monitoring of and participating in signaling on said
+ positions.
+
+
+
+
diff --git a/app/views/cliquickstart.html b/app/views/cliquickstart.html
new file mode 100644
index 0000000..91fbb00
--- /dev/null
+++ b/app/views/cliquickstart.html
@@ -0,0 +1,103 @@
+
+
+
+
+
EtherSignal CLI
+
+
Quick Start.
+
+
+
1. Launch a geth node if it is not already running.
+
geth
+
+
2. Attach via the geth command line client
+
geth attach
+
+
+
3. Load the ethersignal script.
+
> loadScript("ethersignal2.js")
+true
+
+
+
Now you can either signal on a position, tally the current signal levels
+for a position, list the registered positions, or register a position:
+
+
To list the registered positions run the following:
+
> ListPositions()
+[Positions: cut & paste the CalcSignal(); portion to see current signal levels]
+
+
+Position CalcSignal("0x953521cfe06b48d65b64ae864abb4c808312885e", 1290010);
+registered by 0x8c2741b9bebd3c27feb7bb3356f7b04652977b78
+eth deposit: 0
+Title: will this work
+Text: will this contract factory work
+
+Position CalcSignal("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f", 1290020);
+registered by 0x8c2741b9bebd3c27feb7bb3356f7b04652977b78
+eth deposit: 0
+Title: title
+Text: text
+Positions filtered for being under the minDeposit of 0: 0
+true
+
+
+
+
+
If you would like to filter based on the position desposit, pass a
+parameter to ListPositions()
as follows:
+
> ListPositions(1)
+[Positions: cut & paste the CalcSignal(); portion to see current signal levels]
+Positions filtered for being under the minDeposit of 1: 2
+true
+
+
+
As you can see above, in order to get the current signal levels for a position
+you can simply cut and paste the CalcSignal();
portion of the output from:
+
ListPositions():
+> CalcSignal("0x953521cfe06b48d65b64ae864abb4c808312885e", 1290010);
+{
+against: 0,
+pro: 167.12471268213704
+}
+
+
+
In order to register a position you can use the following contract method:
+
> positionregistry.registerPosition("title", "text", {from: web3.eth.accounts[0], gas: 300000});
+
+
+
+
If you would like to optionally submit a deposit into your position
+in order to distinguish it from others you can do the following (note
+your deposit will be returned when you withdraw the position):
+
> web3.eth.sendTransaction({from: web3.eth.accounts[0], to:"0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f", value: web3.toWei(0.1, "ether")})
+
+
+
+
You may withdraw you position and reclaim your deposit as follows:
+
> WithdrawPosition("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f");
+
+
+
+
In order to vote on a position, you will need to use the positions
+signal address. Take the following signal as an example:
+
Position CalcSignal("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f", 1290020);
+registered by 0x8c2741b9bebd3c27feb7bb3356f7b04652977b78
+eth deposit: 0
+Title: title
+Text: text
+
+
+
+
The signal address is what is within CalcSignal();
so above it is
+"0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f"
. To vote simply run the
+following command, where true means to vote for the signal, and false
+would mean to vote against the signal:
+
> SetSignal("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f", true);
+
+
+
+
Enjoy.
+
+
+
diff --git a/app/views/main.html b/app/views/main.html
new file mode 100644
index 0000000..fbaea74
--- /dev/null
+++ b/app/views/main.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
EtherSignal
+
This is a tool to signal on positions.
+
+
+
+ Submit a position
+
+
+
+
+
+
+
+
+
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..130da98
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,24 @@
+{
+ "name": "EtherSignal",
+ "version": "0.0.0",
+ "dependencies": {
+ "angular": "^1.3.0",
+ "bootstrap": "3.3.4",
+ "angular-animate": "^1.3.0",
+ "angular-cookies": "^1.3.0",
+ "angular-resource": "^1.3.0",
+ "angular-route": "^1.3.0",
+ "angular-sanitize": "^1.3.0",
+ "angular-touch": "^1.3.0",
+ "web3": "~0.16.0",
+ "angular-bootstrap": "~1.3.3",
+ "blanket": "~1.1.7",
+ "angular-scroll-animate": "~0.9.9",
+ "animate.css": "~3.5.2"
+ },
+ "devDependencies": {
+ "angular-mocks": "^1.3.0"
+ },
+ "appPath": "app",
+ "moduleName": "nohoApp"
+}
diff --git a/cli/ethersignal2.js b/cli/ethersignal2.js
index 41a3069..b538d6c 100644
--- a/cli/ethersignal2.js
+++ b/cli/ethersignal2.js
@@ -1,27 +1,75 @@
-var ethersignalContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"pro","type":"bool"}],"name":"setSignal","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"endSignal","outputs":[],"type":"function"},{"inputs":[{"name":"rAddr","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pro","type":"bool"},{"indexed":false,"name":"addr","type":"address"}],"name":"LogSignal","type":"event"},{"anonymous":false,"inputs":[],"name":"EndSignal","type":"event"}]);
+var ethersignalContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"pro","type":"bool"}],"name":"setSignal","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"endSignal","outputs":[],"type":"function"},{"inputs":[{"name":"rAddr","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"pro","type":"bool"},{"indexed":false,"name":"addr","type":"address"}],"name":"LogSignal","type":"event"},{"anonymous":false,"inputs":[],"name":"EndSignal","type":"event"}]);
var positionregistryContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"title","type":"string"},{"name":"text","type":"string"}],"name":"registerPosition","outputs":[],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"regAddr","type":"address"},{"indexed":true,"name":"sigAddr","type":"address"},{"indexed":false,"name":"title","type":"string"},{"indexed":false,"name":"text","type":"string"}],"name":"LogPosition","type":"event"}]);
-var positionregistry = positionregistryContract.at('0x0265a5b822625ca506c474912662617c394bbb66')
+var positionregistry = positionregistryContract.at('0x9e75993a7a9b9f92a1978bcc15c30cbcb967bc81')
-function ListPositions() {
+function WithdrawPosition(sigAddr) {
+ var ethersignal = ethersignalContract.at(sigAddr);
+
+ ethersignal.endSignal();
+
+ return true;
+}
+
+function WithdrawFromPosition(sigAddr, amount) {
+ var ethersignal = ethersignalContract.at(sigAddr);
+ var gas = ethersignal.withdraw.estimateGas(web3.toWei(amount)) * 2;
+
+ ethersignal.withdraw(web3.toWei(amount), {from: web3.eth.accounts[0], gas: gas});
+
+ return true;
+}
+
+function SetSignal(sigAddr, pro) {
+ var ethersignal = ethersignalContract.at(sigAddr);
+ var gas = ethersignal.setSignal.estimateGas(pro);
+
+ ethersignal.setSignal(pro, {from: web3.eth.accounts[0], gas: gas});
+
+ return true;
+}
+
+function ListPositions(minDeposit) {
var posMap = {};
+ var minDeposit = typeof minDeposit !== 'undefined' ? minDeposit : 0;
positionregistry.LogPosition({}, {fromBlock: 1200000}, function(error, result){
if (!error)
{
- posMap[result.args.sigAddr] = [result.args.title, result.args.text, result.args.regAddr];
+ posMap[result.args.sigAddr] = [result.args.title, result.args.text, result.args.regAddr, result.blockNumber];
}
})
- Object.keys(posMap).map(function(k) { console.log(k + ": " + posMap[k]); });
+ console.log("[Positions: cut & paste the CalcSignal(); portion to see current signal levels]");
+
+ var numFiltered = 0;
+ Object.keys(posMap).map(function(k) {
+ var deposit = web3.fromWei(web3.eth.getBalance(k));
+
+ if (deposit >= minDeposit)
+ {
+ console.log("\nPosition CalcSignal(\"" + k + "\"," + posMap[k][3] + ");");
+ console.log(" registered by " + posMap[k][2]);
+ console.log(" eth deposit: " + deposit);
+ console.log("Title: " + posMap[k][0]);
+ console.log("Text: " + posMap[k][1]);
+ } else {
+ numFiltered++;
+ }
+ });
+
+ console.log("Positions filtered for being under the minDeposit of " + minDeposit + ": " + numFiltered);
+
+ return true;
}
-/*
-function CalcSignal(positionHash) {
+function CalcSignal(posAddr, startBlock) {
var proMap = {};
var antiMap = {};
- ethersignal.LogSignal({positionHash: positionHash}, {fromBlock: 1200000}, function(error, result){
+ var ethersignal = ethersignalContract.at(posAddr);
+
+ ethersignal.LogSignal({}, {fromBlock: startBlock}, function(error, result){
if (!error)
{
if (result.args.pro) {
@@ -50,4 +98,3 @@ function CalcSignal(positionHash) {
return {pro: totalPro, against: totalAgainst}
}
-*/
diff --git a/cli/readmev2.txt b/cli/readmev2.txt
new file mode 100644
index 0000000..e7b4fa9
--- /dev/null
+++ b/cli/readmev2.txt
@@ -0,0 +1,77 @@
+EtherSignal CLI
+
+Quick Start.
+
+1) Launch a geth node if it is not already running.
+geth
+
+2) Attach via the geth command line client
+geth attach
+
+3) Load the ethersignal script.
+> loadScript("ethersignal2.js")
+true
+
+Now you can either signal on a position, tally the current signal levels
+for a position, list the registered positions, or register a position:
+
+=== To list the registered positions run the following:
+> ListPositions()
+[Positions: cut & paste the CalcSignal(); portion to see current signal levels]
+
+Position CalcSignal("0x953521cfe06b48d65b64ae864abb4c808312885e", 1297010);
+ registered by 0x8c2741b9bebd3c27feb7bb3356f7b04652977b78
+ eth deposit: 0
+Title: will this work
+Text: will this contract factory work
+
+Position CalcSignal("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f", 1297014);
+ registered by 0x8c2741b9bebd3c27feb7bb3356f7b04652977b78
+ eth deposit: 0
+Title: title
+Text: text
+Positions filtered for being under the minDeposit of 0: 0
+true
+
+=== If you would like to filter based on the position desposit, pass a
+parameter to ListPositions) as follows:
+> ListPositions(1)
+[Positions: cut & paste the CalcSignal(); portion to see current signal levels]
+Positions filtered for being under the minDeposit of 1: 2
+true
+
+=== As you can see above, in order to get the current signal levels for a position
+you can simply cut and paste the CalcSignal(); portion of the output from
+ListPositions():
+> CalcSignal("0x953521cfe06b48d65b64ae864abb4c808312885e", 1297010);
+{
+ against: 0,
+ pro: 167.12471268213704
+}
+
+=== In order to register a position you can use the following contract method:
+> positionregistry.registerPosition("title", "text", {from: web3.eth.accounts[0], gas: 300000});
+
+=== If you would like to optionally submit a deposit into your position
+in order to distinguish it from others you can do the following (note
+your deposit will be returned when you withdraw the position):
+> web3.eth.sendTransaction({from: web3.eth.accounts[0], to:"0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f", value: web3.toWei(0.1, "ether")})
+
+=== You may withdraw you position and reclaim your deposit as follows
+> WithdrawPosition("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f");
+
+=== In order to vote on a position, you will need to use the positions
+signal address. Take the following signal as an example:
+Position CalcSignal("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f");
+ registered by 0x8c2741b9bebd3c27feb7bb3356f7b04652977b78
+ eth deposit: 0
+Title: title
+Text: text
+
+The signal address is what is within CalcSignal(); so above it is
+"0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f". To vote simply run the
+following command, where true means to vote for the signal, and false
+would mean to vote against the signal:
+> SetSignal("0xcdda0a8fe9a7a844c9d8611b2cadfe36b4bb438f", true);
+
+Enjoy.
diff --git a/contracts/ethersignal2.sol b/contracts/ethersignal2.sol
index 61b8325..874b676 100644
--- a/contracts/ethersignal2.sol
+++ b/contracts/ethersignal2.sol
@@ -18,6 +18,12 @@ contract EtherSignal {
EndSignal();
}
+ function withdraw(uint amount) {
+ if (msg.sender != regAddr) { throw; }
+ if (amount > this.balance) { throw; }
+ if (!msg.sender.send(amount)) { throw; }
+ }
+
function () {
if (msg.sender != regAddr) { throw; }
// accept deposit only from the address which registered the position
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..02881a1
--- /dev/null
+++ b/index.js
@@ -0,0 +1,9 @@
+var gzippo = require('gzippo');
+var express = require('express');
+var morgan = require('morgan');
+var app = express();
+
+
+app.use(morgan('dev'));
+app.use(gzippo.staticGzip("" + __dirname + "/dist"));
+app.listen(process.env.PORT || 5000);
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..72a5584
--- /dev/null
+++ b/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "etherSignal",
+ "version": "0.0.0",
+ "private": false,
+ "main": "index.js",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "dependencies": {
+ "express": "^4.13.3",
+ "gzippo": "^0.2.0",
+ "karma": "^0.13.22",
+ "karma-coverage": "^1.1.0"
+ },
+ "repository": {},
+ "devDependencies": {
+ "grunt": "^0.4.5",
+ "grunt-autoprefixer": "^2.0.0",
+ "grunt-build-control": "^0.6.2",
+ "grunt-cli": "1.2.0",
+ "grunt-concurrent": "^1.0.0",
+ "grunt-contrib-clean": "^0.6.0",
+ "grunt-contrib-concat": "^0.5.0",
+ "grunt-contrib-connect": "^0.9.0",
+ "grunt-contrib-copy": "^0.7.0",
+ "grunt-contrib-cssmin": "^0.12.0",
+ "grunt-contrib-htmlmin": "^0.4.0",
+ "grunt-contrib-imagemin": "^0.9.2",
+ "grunt-contrib-jshint": "^0.11.0",
+ "grunt-contrib-uglify": "^0.7.0",
+ "grunt-contrib-watch": "^0.6.1",
+ "grunt-filerev": "^2.1.2",
+ "grunt-google-cdn": "^0.4.3",
+ "grunt-karma": "*",
+ "grunt-newer": "^1.1.0",
+ "grunt-ng-annotate": "^0.9.2",
+ "grunt-svgmin": "^2.0.0",
+ "grunt-usemin": "^3.0.0",
+ "grunt-wiredep": "^2.0.0",
+ "jasmine-core": "^2.4.1",
+ "jshint-stylish": "^1.0.0",
+ "karma-jasmine": "*",
+ "karma-ng-html2js-preprocessor": "^1.0.0",
+ "karma-phantomjs-launcher": "*",
+ "load-grunt-tasks": "^3.1.0",
+ "time-grunt": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+}
diff --git a/publish_site.sh b/publish_site.sh
new file mode 100755
index 0000000..4f6e0bb
--- /dev/null
+++ b/publish_site.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# !!!!!!!!!!
+# !!DANGER!! This script publishes whatever branch you're in to gh-pages
+# !!!!!!!!!!
+
+# if [ "$#" -ne 1 ]
+# then
+# echo "Usage: please specify branch"
+# exit 1
+# fi
+
+BRANCH=`git rev-parse --abbrev-ref HEAD`
+echo "stashing current branch, 'git stash pop' after push to retrieve."
+git stash save -u
+
+npm install
+node_modules/bower/bin/bower install
+node_modules/grunt-cli/bin/grunt --force
+
+git checkout gh-pages -f
+git rm `git ls-tree -r HEAD --name-only | grep -v .git`
+
+mv dist/* .
+rm -rf dist
+
+git add .
+VERSION=`git rev-parse $BRANCH`
+echo "Making commit..."
+git commit -m"From branch $BRANCH commit $VERSION"
diff --git a/test/.jshintrc b/test/.jshintrc
new file mode 100644
index 0000000..e2a25d2
--- /dev/null
+++ b/test/.jshintrc
@@ -0,0 +1,28 @@
+{
+ "node": true,
+ "browser": true,
+ "esnext": true,
+ "bitwise": true,
+ "camelcase": true,
+ "curly": true,
+ "eqeqeq": true,
+ "immed": true,
+ "indent": 2,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "quotmark": "single",
+ "regexp": true,
+ "undef": true,
+ "unused": true,
+ "strict": true,
+ "trailing": true,
+ "smarttabs": true,
+ "jasmine": true,
+ "globals": {
+ "angular": false,
+ "browser": false,
+ "inject": false
+ }
+}
+
diff --git a/test/karma.conf.js b/test/karma.conf.js
new file mode 100644
index 0000000..772bee7
--- /dev/null
+++ b/test/karma.conf.js
@@ -0,0 +1,120 @@
+// Karma configuration
+// http://karma-runner.github.io/0.12/config/configuration-file.html
+// Generated on 2015-12-17 using
+// generator-karma 1.0.1
+
+module.exports = function(config) {
+ 'use strict';
+
+ config.set({
+ preprocessors: {
+ '**/*.html': ['ng-html2js']
+ // 'views/**/*.html': 'html2js'
+ },
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+ // base path, that will be used to resolve files and exclude
+ basePath: '../',
+
+ // testing framework to use (jasmine/mocha/qunit/...)
+ // as well as any additional frameworks (requirejs/chai/sinon/...)
+ frameworks: [
+ "jasmine"
+ ],
+
+ // list of files / patterns to load in the browser
+ files: [
+ // bower:js
+ 'bower_components/jquery/dist/jquery.js',
+ 'bower_components/angular/angular.js',
+ 'bower_components/bootstrap/dist/js/bootstrap.js',
+ 'bower_components/angular-animate/angular-animate.js',
+ 'bower_components/angular-cookies/angular-cookies.js',
+ 'bower_components/angular-resource/angular-resource.js',
+ 'bower_components/angular-route/angular-route.js',
+ 'bower_components/angular-sanitize/angular-sanitize.js',
+ 'bower_components/angular-touch/angular-touch.js',
+ 'bower_components/bignumber.js/bignumber.js',
+ 'bower_components/crypto-js/index.js',
+ 'bower_components/web3/dist/web3.js',
+ 'bower_components/web3/dist/web3.min.js',
+ 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
+ 'bower_components/blanket/dist/qunit/blanket.js',
+ 'bower_components/angular-scroll-animate/dist/angular-scroll-animate.js',
+ 'bower_components/angular-mocks/angular-mocks.js',
+ // endbower
+ "app/scripts/**/*.js",
+ "test/mock/**/*.js",
+ "test/spec/**/*.js",
+ 'app/**/*.html',
+ ],
+
+ // list of files / patterns to exclude
+ exclude: [
+ ],
+
+ // web server port
+ port: 8080,
+
+ // Start these browsers, currently available:
+ // - Chrome
+ // - ChromeCanary
+ // - Firefox
+ // - Opera
+ // - Safari (only Mac)
+ // - PhantomJS
+ // - IE (only Windows)
+ browsers: [
+ "PhantomJS"
+ ],
+
+ // Which plugins to enable
+ plugins: [
+ "karma-phantomjs-launcher",
+ "karma-jasmine",
+ 'karma-ng-html2js-preprocessor'
+ ],
+
+ // Continuous Integration mode
+ // if true, it capture browsers, run tests and exit
+ singleRun: false,
+
+ colors: true,
+
+ // level of logging
+ // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+ // Uncomment the following lines if you are using grunt's server to run the tests
+ // proxies: {
+ // '/': 'http://localhost:9000/'
+ // },
+ // URL root prevent conflicts with the site root
+ // urlRoot: '_karma_'
+ ngHtml2JsPreprocessor: {
+ // strip this from the file path
+ // stripPrefix: 'views/',
+ // stripSuffix: '.ext',
+ // prepend this to the
+ // prependPrefix: 'app/',
+
+ // or define a custom transform function
+ // - cacheId returned is used to load template
+ // module(cacheId) will return template at filepath
+ // moduleName: function (htmlPath, originalPath) {
+ // return htmlPath.split('/')[0];
+ // },
+
+ // - setting this option will create only a single module that contains templates
+ // from all the files, so you can load them all with module('foo')
+ // - you may provide a function(htmlPath, originalPath) instead of a string
+ // if you'd like to generate modules dynamically
+ // htmlPath is a originalPath stripped and/or prepended
+ // with all provided suffixes and prefixes
+ // moduleName: 'foo'
+ // moduleName: "my.templates"
+ }
+ });
+};
diff --git a/test/protractor/conf.js b/test/protractor/conf.js
new file mode 100644
index 0000000..14bea1b
--- /dev/null
+++ b/test/protractor/conf.js
@@ -0,0 +1,6 @@
+// conf.js
+exports.config = {
+ framework: 'jasmine',
+ seleniumAddress: 'http://localhost:4444/wd/hub',
+ specs: ['spec.js']
+}
\ No newline at end of file
diff --git a/test/protractor/spec.js b/test/protractor/spec.js
new file mode 100644
index 0000000..87dc333
--- /dev/null
+++ b/test/protractor/spec.js
@@ -0,0 +1,26 @@
+// spec.js
+describe('EtherSignal main', function() {
+ var herotitle = element(by.id('herotitle'));
+ var submitBtn = element(by.id('submitBtn'));
+
+ beforeEach(function() {
+ browser.get('http://localhost:9000/');
+ browser.waitForAngular();
+ });
+
+ it('should have a title', function() {
+ expect(browser.getTitle()).toEqual('EtherSignal');
+ });
+
+ it('should have a title logo', function() {
+ expect(herotitle.getText()).toEqual('EtherSignal');
+ });
+
+ it('should have a submit button', function() {
+ expect(submitBtn.getText()).toEqual('Submit a position');
+ });
+
+});
+
+// webdriver-manager start
+// protractor conf.js
\ No newline at end of file
diff --git a/test/spec/controllers/about.js b/test/spec/controllers/about.js
new file mode 100644
index 0000000..5aecbf2
--- /dev/null
+++ b/test/spec/controllers/about.js
@@ -0,0 +1,19 @@
+'use strict';
+
+describe('Controller: AboutCtrl', function () {
+
+ // load the controller's module
+ beforeEach(module('nohoApp'));
+
+ var AboutCtrl,
+ scope;
+
+ // Initialize the controller and a mock scope
+ beforeEach(inject(function ($controller, $rootScope) {
+ scope = $rootScope.$new();
+ AboutCtrl = $controller('AboutCtrl', {
+ $scope: scope
+ });
+ }));
+
+});
diff --git a/test/spec/controllers/main.js b/test/spec/controllers/main.js
new file mode 100644
index 0000000..e338729
--- /dev/null
+++ b/test/spec/controllers/main.js
@@ -0,0 +1,58 @@
+'use strict';
+
+describe('Controller: MainCtrl', function () {
+
+ // load the controller's module
+ beforeEach(module('nohoApp'));
+
+ var $controller;
+
+ beforeEach(angular.mock.inject(function(_$controller_){
+ $controller = _$controller_;
+ }));
+
+ describe('Controller: MainCtrl', function() {
+ it('it should have a title', function() {
+ var $scope = {};
+ var controller = $controller('MainCtrl', { $scope: $scope });
+ expect($scope.title).toEqual('EtherSignal');
+ });
+ });
+
+});
+
+
+describe('proposalService test', function(){
+
+ describe('when I call proposalService.proposals', function(){
+ beforeEach(module('nohoApp'));
+ it('is is defined', inject(function(proposalService){ //parameter name = service name
+ expect( proposalService.proposals ).toBeDefined()
+ }))
+ })
+
+ describe('when I call proposalService.vote', function(){
+ beforeEach(module('nohoApp'));
+ it('is is defined', inject(function(proposalService){ //parameter name = service name
+ expect( proposalService.vote ).toBeDefined()
+ }))
+ })
+
+});
+
+describe('ethereum service test', function(){
+
+ describe('when I call ethereum.web3', function(){
+ beforeEach(module('nohoApp'));
+ it('is is defined', inject(function(ethereum){ //parameter name = service name
+ expect( ethereum.web3 ).toBeDefined()
+ }))
+ })
+
+});
+
+
+
+
+
+
diff --git a/ui/README.md b/ui/README.md
deleted file mode 100644
index 08da858..0000000
--- a/ui/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-### Installation
-```
-$ npm install
-```
-
-Load this directory as unpacked chrome extension
diff --git a/ui/browserAction.html b/ui/browserAction.html
deleted file mode 100644
index 72c2ec0..0000000
--- a/ui/browserAction.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/ui/css/style.css b/ui/css/style.css
deleted file mode 100644
index 344fb5e..0000000
--- a/ui/css/style.css
+++ /dev/null
@@ -1,121 +0,0 @@
-body {
- background-color: #162023;
- color: azure;
-}
-
-div.stats {
- text-align: center;
- padding: 10px;
- font-size: 15px;
- margin: 10px;
-}
-.good {
- color: lime;
-}
-.bad {
- color: red;
-}
-.warning {
- color: yellow;
-}
-
-li.proposal {
- padding: 10px;
- min-height: 140px;
- padding-left: 40px;
- margin: 10px;
- border-top: 1px solid white;
-}
-ul {
- list-style-type: none;
- padding: 0px;
- margin: 0;
-}
-
-ul li p {
- margin: 0;
- font-size: 40px;
-}
-ul li p.title {
- font-size: 25px;
-}
-ul li p.description {
- margin-top: 5px;
- font-size: 15px;
-}
-ul li span {
- font-size: 15px;
- margin-left: 10px;
-}
-
-h1 span {
- font-size: 14px;
-}
-h1 span.network {
- color: azure;
- font-size: 20px;
-}
-div.stats div span {
- margin-right: 25px;
-}
-
-.right-align {
- text-align: right;
-}
-select {
- padding: 5px 5px 5px 5px;
- font-size: 16px;
- height: 40px;
-}
-
-option {
- color: black;
-}
-.proposal-action {
- margin-top: 15px;
-}
-button {
- margin: 10px;
- width: 180px;
- font-size: 16px;
- height: 50px;
- background-color: transparent;
- border: 1px solid azure;
- color: azure;
-}
-img {
- vertical-align: middle;
-}
-pre {
- background: brown;
- text-align: center;
- padding: 10px;
- font-size: 15px;
-}
-
-.col-2 {
- display: inline-block;
- width: 49%;
-}
-.proposal-results {
- text-align: right;
- width:200px;
- margin: 0 auto;
- border-left: 4px solid white;
-}
-.proposal-results p span {
- font-size: 40px;
-}
-
-.input {
- margin: 10px;
-}
-input {
- width: 350px;
- height: 30px;
- padding: 10px;
- font-size: 15px;
-}
-a {
- color: aqua;
-}
diff --git a/ui/directives/accountSelector.html b/ui/directives/accountSelector.html
deleted file mode 100644
index d46314e..0000000
--- a/ui/directives/accountSelector.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
- Select Account
- {{ account }}
-
-
-
-
diff --git a/ui/directives/networkStats.html b/ui/directives/networkStats.html
deleted file mode 100644
index 315dc00..0000000
--- a/ui/directives/networkStats.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
etherSignal {{ connectionStateDisplay }} ({{ ethereumNetwork }})
-
- Current Block: {{ currentBlock }}
- Current Block Time: {{ currentBlockTime }}
- Since Last Block: {{ sinceLastBlock }} seconds
-
-
-
diff --git a/ui/directives/proposalsList.html b/ui/directives/proposalsList.html
deleted file mode 100644
index df97ef9..0000000
--- a/ui/directives/proposalsList.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
Create New Proposal
-
-
-
-
-
- Create Proposal
- Cancel
-
-
-
-
-
-
-
{{ proposal[0] }}
-
Submitted by {{proposal[2]}}
-
{{ proposal[1] }}
-
- Yes
- No
-
-
-
-
-
{{ proposal[6] }} Ether
-
{{ proposal[7] }} Ether
-
-
-
-
-
diff --git a/ui/etherSignal.html b/ui/etherSignal.html
deleted file mode 100644
index 3ccba98..0000000
--- a/ui/etherSignal.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
- etherSignal
-
-
-
-
-
- Please start your local geth node
- geth --rpc --rpccorsdomain="chrome-extension://pgfcgpgggeefgnajgbdojefgdddlgnpi" --rpcapi="db,eth,net,web3,personal" --testnet
-
-
-
-
-
-
diff --git a/ui/img/ether128.png b/ui/img/ether128.png
deleted file mode 100644
index 708dfa5..0000000
Binary files a/ui/img/ether128.png and /dev/null differ
diff --git a/ui/img/ether16.png b/ui/img/ether16.png
deleted file mode 100644
index e21ebee..0000000
Binary files a/ui/img/ether16.png and /dev/null differ
diff --git a/ui/img/ether48.png b/ui/img/ether48.png
deleted file mode 100644
index 5a1f406..0000000
Binary files a/ui/img/ether48.png and /dev/null differ
diff --git a/ui/img/lock.png b/ui/img/lock.png
deleted file mode 100644
index 23aad70..0000000
Binary files a/ui/img/lock.png and /dev/null differ
diff --git a/ui/js/browserAction.js b/ui/js/browserAction.js
deleted file mode 100644
index 621ff59..0000000
--- a/ui/js/browserAction.js
+++ /dev/null
@@ -1,16 +0,0 @@
-var Web3 = require('web3');
-var web3 = new Web3();
-
-web3.setProvider(new web3.providers.HttpProvider());
-
-document.addEventListener('DOMContentLoaded', function(){
- document.getElementById('openPane').addEventListener('click', function(evt) {
- chrome.tabs.create({url:chrome.extension.getURL('etherSignal.html')});
- }, false);
- var latest = web3.eth.filter('latest');
- latest.watch(function(err,blockHash){
- web3.eth.getBlock(blockHash, false, function(err, block) {
- console.log(block);
- });
- });
-}, false);
diff --git a/ui/js/etherSignal.js b/ui/js/etherSignal.js
deleted file mode 100644
index ec651aa..0000000
--- a/ui/js/etherSignal.js
+++ /dev/null
@@ -1,267 +0,0 @@
-//TODO move this out of global scope
-var to = '0x3B0C2BA7A03725E0f9aC5a55CB813823053d5eBE';
-var Web3 = require('web3');
-var web3 = new Web3();
-web3.setProvider(new web3.providers.HttpProvider());
-
-var connected = web3.isConnected();
-
-var app = angular.module('ethSignal', []);
-
-app.directive('networkStats', ['ethereum', '$interval','$rootScope', function(ethereum, $interval, $rootScope) {
- return {
- restrict: 'E',
- templateUrl: 'directives/networkStats.html',
- link: function(scope) {
- $interval(function() {
-
- if ($rootScope.pending)
- return;
- else if (scope.sinceLastBlock <= 20)
- scope.syncState = 'good';
- else if (scope.sinceLastBlock > 20 && scope.sinceLastBlock < 60)
- scope.syncState = 'warning';
- else
- scope.syncState = 'bad';
- scope.sinceLastBlock += 1;
- }, 1000);
-
- }
- }
-}]);
-
-app.directive('accountSelector', ['ethereum','ethSignalContract','$rootScope', function(ethereum, ethSignalContract, $rootScope) {
- return {
- restrict: 'E',
- templateUrl: 'directives/accountSelector.html',
- link: function(scope) {
- var contract = ethSignalContract;
- scope.accounts = ethereum.accounts;
- scope.user = {defaultAccount:'coinbase'};
- scope.$watch('user.defaultAccount', function(newVal, oldVal) {
- console.log(contract);
- if(oldVal == newVal)
- return;
- console.log("Setting defaultAccount to: ", newVal);
- ethereum.web3.eth.defaultAccount = newVal;
- });
-
- scope.newProposal = function() {
- if ($rootScope.newProposals.length != 0)
- return;
- $rootScope.newProposals.push({name:"", description:""});
- };
- }
- }
-}]);
-
-app.directive('proposalsList', ['proposalService','ethereum','$rootScope', function(proposalService, ethereum, $rootScope) {
-
- return {
- restrict: 'E',
- templateUrl: 'directives/proposalsList.html',
- link: function(scope) {
- scope.proposals = proposalService.proposals;
- scope.newProposals = $rootScope.newProposals;
- scope.cancel = function() {
- $rootScope.newProposals = [];
- }
- scope.vote = function(proposalId, position) {
- if(angular.isUndefined(ethereum.web3.eth.defaultAccount)){
- alert("Please select an account to from the \"Select Account\" dropdown.");
- return
- }
- proposalService.vote(proposalId, position);
- };
-
- scope.createProposal = function(proposal) {
- if(angular.isUndefined(ethereum.web3.eth.defaultAccount)){
- alert("Please select an account to from the \"Select Account\" dropdown.");
- return
- }
- proposalService.newProposal(proposal);
- };
- }
- }
-}]);
-
-app.service('ethereum', function($rootScope, $interval) {
- // TODO graceful connection handling
-
- // convert block timestamps into human readable strings
- function utcSecondsToString(timestamp) {
- var date = new Date(0);
- date.setUTCSeconds(timestamp);
- return date.toString();
- }
- function getCurrentNetwork(networkId) {
- if (networkId == 1)
- return 'Main-Net';
- if (networkId == 2)
- return 'Test-Net';
- }
- function getConnectionStatus(connected) {
- if (connected)
- return 'connected';
- return 'disconnected';
- }
-
- function getEtherscanUrl(networkId) {
- if(networkId == 1)
- return 'https://etherscan.io/tx';
- if (networkId == 2)
- return 'https://testnet.etherscan.io/tx';
- }
-
- $rootScope.pending = true;
- $rootScope.syncState = 'warning';
- $rootScope.currentBlock = 'SYNCING';
- $rootScope.currentBlockTime = 'SYNCING';
- $rootScope.sinceLastBlock = 0;
- if(connected) {
- $rootScope.ethereumNetwork = getCurrentNetwork(web3.version.network);
- $rootScope.etherscanUrl = getEtherscanUrl(web3.version.network);
- }
- var state = web3.isConnected();
- $rootScope.connectionStateDisplay = getConnectionStatus(state);
- $rootScope.connectionState = state;
- $interval(function() {
- newState = web3.isConnected();
- if (newState != state){
- location.reload();
- }
- }, 7500);
-
- //subscribe to all new blocks from the Ethereum blockchain
- //update global network statistics on $rootScope
- var accounts = null;
- if(connected) {
- (function pollNetworkStats() {
- var latest = web3.eth.filter('latest');
- latest.watch(function(err,blockHash){
- web3.eth.getBlock(blockHash, false, function(err, block) {
- $rootScope.pending = false;
- $rootScope.currentBlock = block.number;
- $rootScope.currentBlockTime = utcSecondsToString(block.timestamp);
- $rootScope.sinceLastBlock = -1;
- });
- });
- })();
-
- accounts = web3.eth.accounts;
- }
- return {
- //make web3 available as a property of this service
- web3: web3,
- accounts: accounts
-
-
- };
-});
-
-app.service('ethSignalContract',['ethereum', function(ethereum) {
- var web3 = ethereum.web3;
- var abi = [ { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "proposals", "outputs": [ { "name": "name", "type": "string", "value": "Do you support the hard fork?" }, { "name": "description", "type": "string", "value": "\"Decentralization without decentralized social responsibility is terrifying\"\\n-Vlad Zamfir" }, { "name": "creator", "type": "address", "value": "0xb2445f120a5a4fe73c4ca9b3a73f415371b5656b" }, { "name": "active", "type": "bool", "value": true } ], "type": "function" }, { "constant": true, "inputs": [], "name": "numberOfProposals", "outputs": [ { "name": "_numberOfProposals", "type": "uint256" } ], "type": "function" }, { "constant": false, "inputs": [ { "name": "proposalID", "type": "uint256" }, { "name": "position", "type": "bool" } ], "name": "vote", "outputs": [], "type": "function" }, { "constant": false, "inputs": [ { "name": "proposalName", "type": "string" }, { "name": "proposalDescription", "type": "string" } ], "name": "newProposal", "outputs": [], "type": "function" }, { "inputs": [], "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "proposalID", "type": "uint256" }, { "indexed": false, "name": "position", "type": "bool" }, { "indexed": false, "name": "voter", "type": "address" } ], "name": "userVotedEvent", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "proposalName", "type": "string" }, { "indexed": false, "name": "proposalDescription", "type": "string" }, { "indexed": false, "name": "creator", "type": "address" } ], "name": "proposalCreatedEvent", "type": "event" } ];
- return web3.eth.contract(abi).at(to);
-}]);
-
-app.service('proposalService', ['ethSignalContract', '$q','ethereum','$rootScope', function(ethSignalContract, $q, ethereum, $rootScope) {
- if(connected) {
- var proposals = [];
- $rootScope.newProposals = [];
- for (var x = 0; x < ethSignalContract.numberOfProposals(); x++){
- var p = ethSignalContract.proposals.call(x);
- // add an aray to keep track of true votes
- p.push([]);
- // and array to keep track of valse votes
- p.push([]);
- // eth balance for
- p.push(0);
- // eth balance against
- p.push(0);
- proposals.push(p);
- }
- var votes;
- // store fromBlock as global
- ethSignalContract.userVotedEvent({}, {fromBlock:1187201,toBlock:'latest'}).get(function(err,evt) {
- console.log(evt);
- votes = evt;
- // store each vote with its associated proposal
- for (var v in votes) {
- var vote = votes[v];
- var proposalId = vote.args.proposalID.c[0];
- // tally Ether of votes
- var balance = parseInt(web3.fromWei(ethereum.web3.eth.getBalance(vote.args.voter).toNumber(), 'ether'), 10);
- if (vote.args.position) {
- proposals[proposalId][4].push(vote);
- proposals[proposalId][6] += balance;
- }
- else{
- proposals[proposalId][5].push(vote);
- proposals[proposalId][7] += balance;
- }
- console.log(proposalId,balance, vote.args.position, vote.args.voter, vote.blockNumber);
- }
- });
-
- function mergeData(proposals, votes) {
- //combine votes and proposals to display results
- }
-
- return {
- proposals: proposals,
- votes: votes,
- vote: function(proposalId, position) {
- // make this async return promise
- var deferred = $q.defer();
- var from = ethereum.web3.eth.defaultAccount;
- try {
- $rootScope.lastTx = ethSignalContract.vote(proposalId, position)
- }
- catch(e) {
- var passphrase = prompt("Please enter your passphrase: ");
- var unlocked = ethereum.web3.personal.unlockAccount(from, passphrase);
- if (unlocked) {
- console.log(from, " unlocked.");
- $rootScope.lastTx = ethSignalContract.vote(proposalId, position)
- console.log($rootScope.lastTx);
- ethereum.web3.personal.lockAccount(from);
- console.log(from, " locked.");
- console.log("Voted ", position, " on proposal: ", proposalId);
- }
- else {
- alert("Incorrect passphrase");
- throw(e);
- }
- }
- },
- newProposal: function(proposal) {
- // do wallet locking and unlocking here
- from = ethereum.web3.eth.defaultAccount;
- var data = ethSignalContract.newProposal.getData(proposal.name, proposal.description);
- var gas = ethereum.web3.eth.estimateGas({from:from, to:to, data:data});
- try {
- $rootScope.lastTx = proposalService.contract.newProposal.sendTransaction(proposal.name, proposal.description, {from:from, to:to, gas:gas});
- }
- catch(e) {
- var passphrase = prompt("Please enter your passphrase: ");
- var unlocked = ethereum.web3.personal.unlockAccount(from, passphrase);
- if (unlocked) {
- console.log(from, " unlocked.");
- $rootScope.lastTx = ethSignalContract.newProposal.sendTransaction(proposal.name, proposal.description, {from:from, to:to, gas:gas});
- console.log($rootScope.lastTx);
- ethereum.web3.personal.lockAccount(from);
- console.log(from, " locked.");
- }
- else {
- alert("Incorrect passphrase");
- throw(e);
- }
- }
- $rootScope.newProposals = [];
- }
- };
- }
- return null;
-}]);
-
diff --git a/ui/manifest.json b/ui/manifest.json
deleted file mode 100644
index 5bc285c..0000000
--- a/ui/manifest.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "manifest_version": 2,
- "key": "jnfcafiflcnhihjokebkadknioapkppb",
- "name": "Chrome Ethereum",
- "description": "Ethereum for Chrome",
- "version": "0.1",
- "icons": { "16": "img/ether16.png",
- "48": "img/ether48.png",
- "128": "img/ether128.png" },
- "background": {
- "scripts": []
- },
- "browser_action": {
- "default_popup": "browserAction.html"
- },
- "commands" : {
- "_execute_browser_action": {
- "suggested_key": {
- "mac": "Alt+J",
- "linux": "Alt+J"
- }
- }
- },
- "permissions": [
- "storage",
- "tabs",
- "https://*/*"
- ]
-}
diff --git a/ui/package.json b/ui/package.json
deleted file mode 100644
index 2d8986b..0000000
--- a/ui/package.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "name": "chrome-ethereum",
- "version": "1.0.0",
- "description": "The most versatile and easy to use Ethereum Wallet.",
- "main": "test.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/thebalaa/chrome-ethereum.git"
- },
- "keywords": [
- "ethereum",
- "geth"
- ],
- "author": "Mo Balaa",
- "license": "ISC",
- "bugs": {
- "url": "https://github.com/thebalaa/chrome-ethereum/issues"
- },
- "homepage": "https://github.com/thebalaa/chrome-ethereum#readme",
- "dependencies": {
- "angular": "^1.5.7",
- "web3": "^0.17.0-alpha"
- }
-}