diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c2cdfb8a --- /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/.gitignore b/.gitignore index bb726e29..e322bd9f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ results npm-debug.log node_modules +.nodemonignore .DS_Store public/lib/* diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..bd532c2d --- /dev/null +++ b/.jshintrc @@ -0,0 +1,40 @@ +{ + "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. + "browser": true, // Standard browser globals e.g. `window`, `document`. + "esnext": true, // Allow ES.next specific features such as `const` and `let`. + "bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.). + "camelcase": false, // Permit only camelcase for `var` and `object indexes`. + "curly": false, // Require {} for every new block or scope. + "eqeqeq": true, // Require triple equals i.e. `===`. + "immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` + "latedef": true, // Prohibit variable use before definition. + "newcap": true, // Require capitalization of all constructor functions e.g. `new F()`. + "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`. + "quotmark": "single", // Define quotes to string values. + "regexp": true, // Prohibit `.` and `[^...]` in regular expressions. + "undef": true, // Require all non-global variables be declared before they are used. + "unused": true, // Warn unused variables. + "strict": true, // Require `use strict` pragma in every file. + "trailing": true, // Prohibit trailing whitespaces. + "smarttabs": false, // Suppresses warnings about mixed tabs and spaces + "globals": { // Globals variables. + "angular": true + }, + "predef": [ // Extra globals. + "define", + "require", + "exports", + "module", + "describe", + "before", + "beforeEach", + "after", + "afterEach", + "it", + "inject", + "expect" + ], + "indent": 2, // Specify indentation spacing + "devel": true, // Allow development statements e.g. `console.log();`. + "noempty": true // Prohibit use of empty blocks. +} diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..743d9845 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,94 @@ +'use strict'; + +module.exports = function(grunt) { + // Project Configuration + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + watch: { + jade: { + files: ['app/views/**'], + options: { + livereload: true, + }, + }, + js: { + files: ['Gruntfile.js', 'server.js', 'app/**/*.js', 'public/js/**'], + tasks: ['jshint'], + options: { + livereload: true, + }, + }, + html: { + files: ['public/views/**'], + options: { + livereload: true, + }, + }, + css: { + files: ['public/css/**'], + options: { + livereload: true + } + } + }, + jshint: { + all: { + src: ['Gruntfile.js', 'server.js', 'app/**/*.js', 'public/js/**'], + options: { + jshintrc: true + } + } + }, + nodemon: { + dev: { + options: { + file: 'server.js', + args: [], + ignoredFiles: ['public/**'], + watchedExtensions: ['js'], + nodeArgs: ['--debug'], + delayTime: 1, + env: { + PORT: 3000 + }, + cwd: __dirname + } + } + }, + concurrent: { + tasks: ['nodemon', 'watch'], + options: { + logConcurrentOutput: true + } + }, + mochaTest: { + options: { + reporter: 'spec', + require: 'server.js' + }, + src: ['test/*.js'] + }, + env: { + test: { + NODE_ENV: 'test' + } + } + }); + + //Load NPM tasks + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-mocha-test'); + grunt.loadNpmTasks('grunt-nodemon'); + grunt.loadNpmTasks('grunt-concurrent'); + grunt.loadNpmTasks('grunt-env'); + + //Making grunt default to force in order not to break the project. + grunt.option('force', true); + + //Default task(s). + grunt.registerTask('default', ['jshint', 'concurrent']); + + //Test task. + grunt.registerTask('test', ['env:test', 'mochaTest']); +}; diff --git a/README.md b/README.md index 69bc9c9f..35ff00c2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ mystery ======= +## Install + +- sudo npm -g i grunt-cli +- npm i +- grunt + + ## bitcoind configuration There is a bitcoind configuration sample at: diff --git a/app/views/includes/foot.jade b/app/views/includes/foot.jade index b2a61b6e..f7cc3e62 100755 --- a/app/views/includes/foot.jade +++ b/app/views/includes/foot.jade @@ -2,5 +2,30 @@ .container p.text-muted Place sticky footer content here. -script(type='text/javascript', src='/lib/jquery/jquery.min.js') -script(type='text/javascript', src='/lib/bootstrap/dist/js/bootstrap.min.js') +//script(type='text/javascript', src='/lib/jquery/jquery.min.js') +//script(type='text/javascript', src='/lib/bootstrap/dist/js/bootstrap.min.js') + +//AngularJS +script(type='text/javascript', src='/lib/angular/angular.js') +script(type='text/javascript', src='/lib/angular-cookies/angular-cookies.js') +script(type='text/javascript', src='/lib/angular-resource/angular-resource.js') +script(type='text/javascript', src='/lib/angular-route/angular-route.js') + +//Angular UI +script(type='text/javascript', src='/lib/angular-bootstrap/ui-bootstrap.js') +script(type='text/javascript', src='/lib/angular-bootstrap/ui-bootstrap-tpls.js') +script(type='text/javascript', src='/lib/angular-ui-utils/ui-utils.js') + +//Application Init +script(type='text/javascript', src='/js/app.js') +script(type='text/javascript', src='/js/config.js') +script(type='text/javascript', src='/js/directives.js') +script(type='text/javascript', src='/js/filters.js') + +//Application Services +script(type='text/javascript', src='/js/services/global.js') + +//Application Controllers +script(type='text/javascript', src='/js/controllers/index.js') +script(type='text/javascript', src='/js/controllers/header.js') +script(type='text/javascript', src='/js/init.js') diff --git a/app/views/includes/navbar.jade b/app/views/includes/navbar.jade index eabfbb97..a1b2b1c8 100644 --- a/app/views/includes/navbar.jade +++ b/app/views/includes/navbar.jade @@ -1,17 +1 @@ -.navbar.navbar-default.navbar-fixed-top(role='navigation') - .container - .navbar-header - button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse') - span.sr-only Toggle navigation - span.icon-bar - span.icon-bar - span.icon-bar - a.navbar-brand(href='#') Mystery - .collapse.navbar-collapse - ul.nav.navbar-nav - li.active - a(href='#') Home - li - a(href='#about') About - li - a(href='#contact') Contact +.navbar.navbar-default.navbar-fixed-top(data-ng-include="'views/header.html'", role='navigation') diff --git a/app/views/index.jade b/app/views/index.jade index cd0eb696..7f337180 100755 --- a/app/views/index.jade +++ b/app/views/index.jade @@ -1,24 +1,25 @@ extends layouts/default block content - .page-header - h1 Hello BitPay! - p ˈmɪst(ə)ri/' - | noun - audio(src="https://ssl.gstatic.com/dictionary/static/sounds/de/0/mystery.mp3",preload="auto",data-dobid="aud",id="aud") - button(onclick="document.getElementById('aud').play()") Play - - ol - li - strong something that is difficult or impossible to understand or explain. - p "the mysteries of outer space" - | synonyms: puzzle, enigma, conundrum, riddle, secret, unsolved problem, problem, question, question mark, closed book; secrecy or obscurity. - p "much of her past is shrouded in mystery" - | synonyms: secrecy, darkness, obscurity, ambiguity, ambiguousness, uncertainty, impenetrability, vagueness, nebulousness; More - li - strong a person or thing whose identity or nature is puzzling or unknown. - p "‘He's a bit of a mystery,’ said Nina" - li - strong a novel, play, or film dealing with a puzzling crime, especially a murder. - p "the 1920s murder mystery, The Ghost Train" - | synonyms: thriller, detective story/novel, murder story; More + section.container(data-ng-view) + + section.container + p ˈmɪst(ə)ri/' + | noun + audio(src="https://ssl.gstatic.com/dictionary/static/sounds/de/0/mystery.mp3",preload="auto",data-dobid="aud",id="aud") + button(onclick="document.getElementById('aud').play()") Play + + ol + li + strong something that is difficult or impossible to understand or explain. + p "the mysteries of outer space" + | synonyms: puzzle, enigma, conundrum, riddle, secret, unsolved problem, problem, question, question mark, closed book; secrecy or obscurity. + p "much of her past is shrouded in mystery" + | synonyms: secrecy, darkness, obscurity, ambiguity, ambiguousness, uncertainty, impenetrability, vagueness, nebulousness; More + li + strong a person or thing whose identity or nature is puzzling or unknown. + p "‘He's a bit of a mystery,’ said Nina" + li + strong a novel, play, or film dealing with a puzzling crime, especially a murder. + p "the 1920s murder mystery, The Ghost Train" + | synonyms: thriller, detective story/novel, murder story; More diff --git a/app/views/layouts/default.jade b/app/views/layouts/default.jade index 186b81c6..64e2713b 100755 --- a/app/views/layouts/default.jade +++ b/app/views/layouts/default.jade @@ -4,6 +4,5 @@ html(lang='en', xmlns='http://www.w3.org/1999/xhtml') body #wrap include ../includes/navbar - section.container - block content + block content include ../includes/foot diff --git a/bower.json b/bower.json index 62688af2..46be4c59 100644 --- a/bower.json +++ b/bower.json @@ -2,6 +2,13 @@ "name": "Mystery", "version": "0.0.1", "dependencies": { - "bootstrap": "3.0.3" + "angular": "latest", + "angular-resource": "latest", + "angular-cookies": "latest", + "angular-mocks": "latest", + "angular-route": "latest", + "bootstrap": "3.0.3", + "angular-bootstrap": "0.9.0", + "angular-ui-utils": "0.1.0" } } \ No newline at end of file diff --git a/package.json b/package.json index b235128d..0fc2f268 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,12 @@ "email": "ryan@bitpay.com" }, "repository": "git://github.com/bitpay/mystery.git", - "contributors": [{ - "name": "Matias Alejo Garcia", - "email": "ematiu@gmail.com" - }], + "contributors": [ + { + "name": "Matias Alejo Garcia", + "email": "ematiu@gmail.com" + } + ], "bugs": { "url": "https://github.com/bitpay/mystery/issues" }, @@ -29,6 +31,7 @@ "node": "*" }, "scripts": { + "start": "node node_modules/grunt-cli/bin/grunt", "postinstall": "node node_modules/bower/bin/bower install" }, "dependencies": { @@ -39,6 +42,23 @@ "lodash": "~2.4.1", "bower": "~1.2.8", "bitcore": "*", - "buffertools": "*" + "buffertools": "*", + "grunt": "~0.4.2", + "grunt-cli": "~0.1.11", + "grunt-env": "~0.4.1", + "grunt-contrib-jshint": "~0.8.0", + "grunt-contrib-watch": "~0.5.3", + "grunt-concurrent": "~0.4.2", + "grunt-nodemon": "~0.1.2", + "grunt-mocha-test": "~0.8.1", + "should": "~2.1.1" + }, + "devDependencies": { + "grunt-contrib-watch": "latest", + "grunt-contrib-jshint": "latest", + "grunt-nodemon": "latest", + "grunt-concurrent": "latest", + "grunt-mocha-test": "latest", + "should": "latest" } } diff --git a/public/js/app.js b/public/js/app.js new file mode 100755 index 00000000..77b64cba --- /dev/null +++ b/public/js/app.js @@ -0,0 +1,5 @@ +'use strict'; + +angular.module('mystery', ['ngCookies', 'ngResource', 'ngRoute', 'ui.bootstrap', 'ui.route', 'mystery.system']); + +angular.module('mystery.system', []); \ No newline at end of file diff --git a/public/js/config.js b/public/js/config.js new file mode 100755 index 00000000..fe680064 --- /dev/null +++ b/public/js/config.js @@ -0,0 +1,21 @@ +'use strict'; + +//Setting up route +angular.module('mystery').config(['$routeProvider', + function($routeProvider) { + $routeProvider. + when('/', { + templateUrl: 'views/index.html' + }). + otherwise({ + redirectTo: '/' + }); + } +]); + +//Setting HTML5 Location Mode +angular.module('mystery').config(['$locationProvider', + function($locationProvider) { + $locationProvider.hashPrefix('!'); + } +]); diff --git a/public/js/controllers/header.js b/public/js/controllers/header.js new file mode 100755 index 00000000..039a5ac2 --- /dev/null +++ b/public/js/controllers/header.js @@ -0,0 +1,15 @@ +'use strict'; + +angular.module('mystery.system').controller('HeaderController', ['$scope', 'Global', function ($scope, Global) { + $scope.global = Global; + + $scope.menu = [{ + 'title': 'Articles', + 'link': 'articles' + }, { + 'title': 'Create New Article', + 'link': 'articles/create' + }]; + + $scope.isCollapsed = false; +}]); diff --git a/public/js/controllers/index.js b/public/js/controllers/index.js new file mode 100755 index 00000000..4cb2b6fd --- /dev/null +++ b/public/js/controllers/index.js @@ -0,0 +1,5 @@ +'use strict'; + +angular.module('mystery.system').controller('IndexController', ['$scope', 'Global', function ($scope, Global) { + $scope.global = Global; +}]); \ No newline at end of file diff --git a/public/js/directives.js b/public/js/directives.js new file mode 100755 index 00000000..a726efc4 --- /dev/null +++ b/public/js/directives.js @@ -0,0 +1 @@ +'use strict'; \ No newline at end of file diff --git a/public/js/filters.js b/public/js/filters.js new file mode 100755 index 00000000..a726efc4 --- /dev/null +++ b/public/js/filters.js @@ -0,0 +1 @@ +'use strict'; \ No newline at end of file diff --git a/public/js/init.js b/public/js/init.js new file mode 100755 index 00000000..b226e6fd --- /dev/null +++ b/public/js/init.js @@ -0,0 +1,9 @@ +'use strict'; + +angular.element(document).ready(function() { + //Fixing facebook bug with redirect + if (window.location.hash === '#_=_') window.location.hash = '#!'; + + //Then init the app + angular.bootstrap(document, ['mystery']); +}); diff --git a/public/js/services/global.js b/public/js/services/global.js new file mode 100755 index 00000000..79006526 --- /dev/null +++ b/public/js/services/global.js @@ -0,0 +1,14 @@ +'use strict'; + +//Global service for global variables +angular.module('mystery.system').factory('Global', [ + function() { + var _this = this; + _this._data = { + user: window.user, + authenticated: !! window.user + }; + + return _this._data; + } +]); diff --git a/public/views/header.html b/public/views/header.html new file mode 100755 index 00000000..70189e79 --- /dev/null +++ b/public/views/header.html @@ -0,0 +1,18 @@ +
+ + +
diff --git a/public/views/index.html b/public/views/index.html new file mode 100644 index 00000000..b92fe022 --- /dev/null +++ b/public/views/index.html @@ -0,0 +1,5 @@ +
+ +
diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000..74590dc4 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,4 @@ +--require should +-R spec +--ui bdd + diff --git a/test/test.js b/test/test.js new file mode 100644 index 00000000..bab25b64 --- /dev/null +++ b/test/test.js @@ -0,0 +1,9 @@ +var assert = require("assert") +describe('Array', function(){ + describe('#indexOf()', function(){ + it('should return -1 when the value is not present', function(){ + assert.equal(-1, [1,2,3].indexOf(5)); + assert.equal(-1, [1,2,3].indexOf(0)); + }) + }) +}) diff --git a/util/sync.js b/util/sync.js index 9fe5a5b2..2cb5d5ee 100755 --- a/util/sync.js +++ b/util/sync.js @@ -11,15 +11,19 @@ var config = require('../config/config'); var mongoose = require('mongoose'); var networkName = process.argv[2] || 'testnet'; -var genesisBlockHash = networks.testnet.genesisBlock.hash.reverse().toString('hex'); +var network = networkName == 'testnet' ? networks.testnet : networks.livenet; -function syncBlocks(blockHash) { +function getNextBlock(blockHash,cb) { + + if ( !blockHash ) { + console.log("done"); + return cb(); + } rpc.getBlock(blockHash, function(err, blockInfo) { if (err) { - console.log(err); - throw(err); + return cb(err); } if ( ! ( blockInfo.result.height % 1000) ) @@ -27,25 +31,39 @@ function syncBlocks(blockHash) { Block.create( blockInfo.result, function(err, inBlock) { - if (err && err.toString().match(/E11000/)) { -// console.log("\twas there. Skipping"); - return syncBlocks(blockInfo.result.nextblockhash); + // E11000 => already exists + if (err && ! err.toString().match(/E11000/)) { + return cb(err); } - if (err) throw(err); - - if (inBlock.nextblockhash && ! inBlock.nextblockhash.match(/^0+$/) ) { - syncBlocks(inBlock.nextblockhash) - } - else { - mongoose.connection.close(); - } + return getNextBlock(blockInfo.result.nextblockhash); }); }); } +function syncBlocks(network, cb) { + + Block.findOne({}, {}, { sort: { 'height' : -1 } }, function(err, block) { + if (err) { + return cb(err); + } + + + + var nextHash = + block && block.hash + ? block.hash + : network.genesisBlock.hash.reverse().toString('hex') + ; + + + console.log('Starting at hash' + nextHash); + getNextBlock(nextHash, cb); + }); +} + mongoose.connect(config.db); @@ -55,9 +73,12 @@ var rpc = new RpcClient(config.bitcoind); db.on('error', console.error.bind(console, 'connection error:')); db.once('open', function callback () { - - syncBlocks(genesisBlockHash); - + syncBlocks(network, function(err) { + if (err) { + console.log(err); + } + mongoose.connection.close(); + }); });