// Source: public/src/js/app.js var defaultLanguage = localStorage.getItem('insight-language') || 'en'; var defaultCurrency = localStorage.getItem('insight-currency') || 'BTC'; angular.module('insight',[ 'ngAnimate', 'ngResource', 'ngRoute', 'ngProgress', 'ui.bootstrap', 'ui.route', 'monospaced.qrcode', 'gettext', 'angularMoment', 'insight.system', 'insight.socket', 'insight.api', 'insight.blocks', 'insight.transactions', 'insight.address', 'insight.search', 'insight.status', 'insight.connection', 'insight.currency', 'insight.messages' ]); angular.module('insight.system', []); angular.module('insight.socket', []); angular.module('insight.api', []); angular.module('insight.blocks', []); angular.module('insight.transactions', []); angular.module('insight.address', []); angular.module('insight.search', []); angular.module('insight.status', []); angular.module('insight.connection', []); angular.module('insight.currency', []); angular.module('insight.messages', []); // Source: public/src/js/controllers/address.js angular.module('insight.address').controller('AddressController', function($scope, $rootScope, $routeParams, $location, Global, Address, getSocket) { $scope.global = Global; var socket = getSocket($scope); var addrStr = $routeParams.addrStr; var _startSocket = function() { socket.on('bitcoind/addresstxid', function(data) { if (data.address === addrStr) { $rootScope.$broadcast('tx', data.txid); var base = document.querySelector('base'); var beep = new Audio(base.href + '/sound/transaction.mp3'); beep.play(); } }); socket.emit('subscribe', 'bitcoind/addresstxid', [addrStr]); }; var _stopSocket = function () { socket.emit('unsubscribe', 'bitcoind/addresstxid', [addrStr]); }; socket.on('connect', function() { _startSocket(); }); $scope.$on('$destroy', function(){ _stopSocket(); }); $scope.params = $routeParams; $scope.findOne = function() { $rootScope.currentAddr = $routeParams.addrStr; _startSocket(); Address.get({ addrStr: $routeParams.addrStr }, function(address) { $rootScope.titleDetail = address.addrStr.substring(0, 7) + '...'; $rootScope.flashMessage = null; $scope.address = address; }, function(e) { if (e.status === 400) { $rootScope.flashMessage = 'Invalid Address: ' + $routeParams.addrStr; } else if (e.status === 503) { $rootScope.flashMessage = 'Backend Error. ' + e.data; } else { $rootScope.flashMessage = 'Address Not Found'; } $location.path('/'); }); }; }); // Source: public/src/js/controllers/blocks.js angular.module('insight.blocks').controller('BlocksController', function($scope, $rootScope, $routeParams, $location, Global, Block, Blocks, BlockByHeight) { $scope.global = Global; $scope.loading = false; if ($routeParams.blockHeight) { BlockByHeight.get({ blockHeight: $routeParams.blockHeight }, function(hash) { $location.path('/block/' + hash.blockHash); }, function() { $rootScope.flashMessage = 'Bad Request'; $location.path('/'); }); } //Datepicker var _formatTimestamp = function (date) { var yyyy = date.getUTCFullYear().toString(); var mm = (date.getUTCMonth() + 1).toString(); // getMonth() is zero-based var dd = date.getUTCDate().toString(); return yyyy + '-' + (mm[1] ? mm : '0' + mm[0]) + '-' + (dd[1] ? dd : '0' + dd[0]); //padding }; $scope.$watch('dt', function(newValue, oldValue) { if (newValue !== oldValue) { $location.path('/blocks-date/' + _formatTimestamp(newValue)); } }); $scope.openCalendar = function($event) { $event.preventDefault(); $event.stopPropagation(); $scope.opened = true; }; $scope.humanSince = function(time) { var m = moment.unix(time).startOf('day'); var b = moment().startOf('day'); return m.max().from(b); }; $scope.list = function() { $scope.loading = true; if ($routeParams.blockDate) { $scope.detail = 'On ' + $routeParams.blockDate; } if ($routeParams.startTimestamp) { var d=new Date($routeParams.startTimestamp*1000); var m=d.getMinutes(); if (m<10) m = '0' + m; $scope.before = ' before ' + d.getHours() + ':' + m; } $rootScope.titleDetail = $scope.detail; Blocks.get({ blockDate: $routeParams.blockDate, startTimestamp: $routeParams.startTimestamp }, function(res) { $scope.loading = false; $scope.blocks = res.blocks; $scope.pagination = res.pagination; }); }; $scope.findOne = function() { $scope.loading = true; Block.get({ blockHash: $routeParams.blockHash }, function(block) { $rootScope.titleDetail = block.height; $rootScope.flashMessage = null; $scope.loading = false; $scope.block = block; }, function(e) { if (e.status === 400) { $rootScope.flashMessage = 'Invalid Transaction ID: ' + $routeParams.txId; } else if (e.status === 503) { $rootScope.flashMessage = 'Backend Error. ' + e.data; } else { $rootScope.flashMessage = 'Block Not Found'; } $location.path('/'); }); }; $scope.params = $routeParams; }); // Source: public/src/js/controllers/connection.js angular.module('insight.connection').controller('ConnectionController', function($scope, $window, Status, getSocket, PeerSync) { // Set initial values $scope.apiOnline = true; $scope.serverOnline = true; $scope.clienteOnline = true; var socket = getSocket($scope); // Check for the node server connection socket.on('connect', function() { $scope.serverOnline = true; socket.on('disconnect', function() { $scope.serverOnline = false; }); }); // Check for the api connection $scope.getConnStatus = function() { PeerSync.get({}, function(peer) { $scope.apiOnline = peer.connected; $scope.host = peer.host; $scope.port = peer.port; }, function() { $scope.apiOnline = false; }); }; socket.emit('subscribe', 'sync'); socket.on('status', function(sync) { $scope.sync = sync; $scope.apiOnline = (sync.status !== 'aborted' && sync.status !== 'error'); }); // Check for the client conneciton $window.addEventListener('offline', function() { $scope.$apply(function() { $scope.clienteOnline = false; }); }, true); $window.addEventListener('online', function() { $scope.$apply(function() { $scope.clienteOnline = true; }); }, true); }); // Source: public/src/js/controllers/currency.js angular.module('insight.currency').controller('CurrencyController', function($scope, $rootScope, Currency) { $rootScope.currency.symbol = defaultCurrency; var _roundFloat = function(x, n) { if(!parseInt(n, 10) || !parseFloat(x)) n = 0; return Math.round(x * Math.pow(10, n)) / Math.pow(10, n); }; $rootScope.currency.getConvertion = function(value) { value = value * 1; // Convert to number if (!isNaN(value) && typeof value !== 'undefined' && value !== null) { if (value === 0.00000000) return '0 ' + this.symbol; // fix value to show var response; if (this.symbol === 'USD') { response = _roundFloat((value * this.factor), 2); } else if (this.symbol === 'mBTC') { this.factor = 1000; response = _roundFloat((value * this.factor), 5); } else if (this.symbol === 'bits') { this.factor = 1000000; response = _roundFloat((value * this.factor), 2); } else { // assumes symbol is BTC this.factor = 1; response = _roundFloat((value * this.factor), 8); } // prevent sci notation if (response < 1e-7) response=response.toFixed(8); return response + ' ' + this.symbol; } return 'value error'; }; $scope.setCurrency = function(currency) { $rootScope.currency.symbol = currency; localStorage.setItem('insight-currency', currency); if (currency === 'USD') { Currency.get({}, function(res) { $rootScope.currency.factor = $rootScope.currency.bitstamp = res.data.bitstamp; }); } else if (currency === 'mBTC') { $rootScope.currency.factor = 1000; } else if (currency === 'bits') { $rootScope.currency.factor = 1000000; } else { $rootScope.currency.factor = 1; } }; // Get initial value Currency.get({}, function(res) { $rootScope.currency.factor = $rootScope.currency.bitstamp = res.data.bitstamp; }); }); // Source: public/src/js/controllers/footer.js angular.module('insight.system').controller('FooterController', function($scope, $route, $templateCache, gettextCatalog, amMoment, Version) { $scope.defaultLanguage = defaultLanguage; var _getVersion = function() { Version.get({}, function(res) { $scope.version = res.version; }); }; $scope.version = _getVersion(); $scope.availableLanguages = [{ name: 'Deutsch', isoCode: 'de_DE', }, { name: 'English', isoCode: 'en', }, { name: 'Spanish', isoCode: 'es', }, { name: 'Japanese', isoCode: 'ja', }]; $scope.setLanguage = function(isoCode) { gettextCatalog.currentLanguage = $scope.defaultLanguage = defaultLanguage = isoCode; amMoment.changeLocale(isoCode); localStorage.setItem('insight-language', isoCode); var currentPageTemplate = $route.current.templateUrl; $templateCache.remove(currentPageTemplate); $route.reload(); }; }); // Source: public/src/js/controllers/header.js angular.module('insight.system').controller('HeaderController', function($scope, $rootScope, $modal, getSocket, Global, Block) { $scope.global = Global; $rootScope.currency = { factor: 1, bitstamp: 0, symbol: 'BTC' }; $scope.menu = [{ 'title': 'Blocks', 'link': 'blocks' }, { 'title': 'Status', 'link': 'status' }]; $scope.openScannerModal = function() { var modalInstance = $modal.open({ templateUrl: 'scannerModal.html', controller: 'ScannerController' }); }; var _getBlock = function(hash) { Block.get({ blockHash: hash }, function(res) { $scope.totalBlocks = res.height; }); }; var socket = getSocket($scope); socket.on('connect', function() { socket.emit('subscribe', 'inv'); socket.on('block', function(block) { var blockHash = block.toString(); _getBlock(blockHash); }); }); $rootScope.isCollapsed = true; }); // Source: public/src/js/controllers/index.js var TRANSACTION_DISPLAYED = 10; var BLOCKS_DISPLAYED = 5; angular.module('insight.system').controller('IndexController', function($scope, Global, getSocket, Blocks) { $scope.global = Global; var _getBlocks = function() { Blocks.get({ limit: BLOCKS_DISPLAYED }, function(res) { $scope.blocks = res.blocks; $scope.blocksLength = res.length; }); }; var socket = getSocket($scope); var _startSocket = function() { socket.emit('subscribe', 'inv'); socket.on('tx', function(tx) { $scope.txs.unshift(tx); if (parseInt($scope.txs.length, 10) >= parseInt(TRANSACTION_DISPLAYED, 10)) { $scope.txs = $scope.txs.splice(0, TRANSACTION_DISPLAYED); } }); socket.on('block', function() { _getBlocks(); }); }; socket.on('connect', function() { _startSocket(); }); $scope.humanSince = function(time) { var m = moment.unix(time); return m.max().fromNow(); }; $scope.index = function() { _getBlocks(); _startSocket(); }; $scope.txs = []; $scope.blocks = []; }); // Source: public/src/js/controllers/messages.js angular.module('insight.messages').controller('VerifyMessageController', function($scope, $http, Api) { $scope.message = { address: '', signature: '', message: '' }; $scope.verification = { status: 'unverified', // ready|loading|verified|error result: null, error: null, address: '' }; $scope.verifiable = function() { return ($scope.message.address && $scope.message.signature && $scope.message.message); }; $scope.verify = function() { $scope.verification.status = 'loading'; $scope.verification.address = $scope.message.address; $http.post(Api.apiPrefix + '/messages/verify', $scope.message) .success(function(data, status, headers, config) { if(typeof(data.result) != 'boolean') { // API returned 200 but result was not true or false $scope.verification.status = 'error'; $scope.verification.error = null; return; } $scope.verification.status = 'verified'; $scope.verification.result = data.result; }) .error(function(data, status, headers, config) { $scope.verification.status = 'error'; $scope.verification.error = data; }); }; // Hide the verify status message on form change var unverify = function() { $scope.verification.status = 'unverified'; }; $scope.$watch('message.address', unverify); $scope.$watch('message.signature', unverify); $scope.$watch('message.message', unverify); }); // Source: public/src/js/controllers/scanner.js angular.module('insight.system').controller('ScannerController', function($scope, $rootScope, $modalInstance, Global) { $scope.global = Global; // Detect mobile devices var isMobile = { Android: function() { return navigator.userAgent.match(/Android/i); }, BlackBerry: function() { return navigator.userAgent.match(/BlackBerry/i); }, iOS: function() { return navigator.userAgent.match(/iPhone|iPad|iPod/i); }, Opera: function() { return navigator.userAgent.match(/Opera Mini/i); }, Windows: function() { return navigator.userAgent.match(/IEMobile/i); }, any: function() { return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); } }; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; $scope.isMobile = isMobile.any(); $scope.scannerLoading = false; var $searchInput = angular.element(document.getElementById('search')), cameraInput, video, canvas, $video, context, localMediaStream; var _scan = function(evt) { if ($scope.isMobile) { $scope.scannerLoading = true; var files = evt.target.files; if (files.length === 1 && files[0].type.indexOf('image/') === 0) { var file = files[0]; var reader = new FileReader(); reader.onload = (function(theFile) { return function(e) { var mpImg = new MegaPixImage(file); mpImg.render(canvas, { maxWidth: 200, maxHeight: 200, orientation: 6 }); setTimeout(function() { qrcode.width = canvas.width; qrcode.height = canvas.height; qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height); try { //alert(JSON.stringify(qrcode.process(context))); qrcode.decode(); } catch (e) { alert(e); } }, 1500); }; })(file); // Read in the file as a data URL reader.readAsDataURL(file); } } else { if (localMediaStream) { context.drawImage(video, 0, 0, 300, 225); try { qrcode.decode(); } catch(e) { //qrcodeError(e); } } setTimeout(_scan, 500); } }; var _successCallback = function(stream) { video.src = (window.URL && window.URL.createObjectURL(stream)) || stream; localMediaStream = stream; video.play(); setTimeout(_scan, 1000); }; var _scanStop = function() { $scope.scannerLoading = false; $modalInstance.close(); if (!$scope.isMobile) { if (localMediaStream.stop) localMediaStream.stop(); localMediaStream = null; video.src = ''; } }; var _videoError = function(err) { console.log('Video Error: ' + JSON.stringify(err)); _scanStop(); }; qrcode.callback = function(data) { _scanStop(); var str = (data.indexOf('bitcoin:') === 0) ? data.substring(8) : data; console.log('QR code detected: ' + str); $searchInput .val(str) .triggerHandler('change') .triggerHandler('submit'); }; $scope.cancel = function() { _scanStop(); }; $modalInstance.opened.then(function() { $rootScope.isCollapsed = true; // Start the scanner setTimeout(function() { canvas = document.getElementById('qr-canvas'); context = canvas.getContext('2d'); if ($scope.isMobile) { cameraInput = document.getElementById('qrcode-camera'); cameraInput.addEventListener('change', _scan, false); } else { video = document.getElementById('qrcode-scanner-video'); $video = angular.element(video); canvas.width = 300; canvas.height = 225; context.clearRect(0, 0, 300, 225); navigator.getUserMedia({video: true}, _successCallback, _videoError); } }, 500); }); }); // Source: public/src/js/controllers/search.js angular.module('insight.search').controller('SearchController', function($scope, $routeParams, $location, $timeout, Global, Block, Transaction, Address, BlockByHeight) { $scope.global = Global; $scope.loading = false; var _badQuery = function() { $scope.badQuery = true; $timeout(function() { $scope.badQuery = false; }, 2000); }; var _resetSearch = function() { $scope.q = ''; $scope.loading = false; }; $scope.search = function() { var q = $scope.q; $scope.badQuery = false; $scope.loading = true; Block.get({ blockHash: q }, function() { _resetSearch(); $location.path('block/' + q); }, function() { //block not found, search on TX Transaction.get({ txId: q }, function() { _resetSearch(); $location.path('tx/' + q); }, function() { //tx not found, search on Address Address.get({ addrStr: q }, function() { _resetSearch(); $location.path('address/' + q); }, function() { // block by height not found if (isFinite(q)) { // ensure that q is a finite number. A logical height value. BlockByHeight.get({ blockHeight: q }, function(hash) { _resetSearch(); $location.path('/block/' + hash.blockHash); }, function() { //not found, fail :( $scope.loading = false; _badQuery(); }); } else { $scope.loading = false; _badQuery(); } }); }); }); }; }); // Source: public/src/js/controllers/status.js angular.module('insight.status').controller('StatusController', function($scope, $routeParams, $location, Global, Status, Sync, getSocket) { $scope.global = Global; $scope.getStatus = function(q) { Status.get({ q: 'get' + q }, function(d) { $scope.loaded = 1; angular.extend($scope, d); }, function(e) { $scope.error = 'API ERROR: ' + e.data; }); }; $scope.humanSince = function(time) { var m = moment.unix(time / 1000); return m.max().fromNow(); }; var _onSyncUpdate = function(sync) { $scope.sync = sync; }; var _startSocket = function () { socket.emit('subscribe', 'sync'); socket.on('status', function(sync) { _onSyncUpdate(sync); }); }; var socket = getSocket($scope); socket.on('connect', function() { _startSocket(); }); $scope.getSync = function() { _startSocket(); Sync.get({}, function(sync) { _onSyncUpdate(sync); }, function(e) { var err = 'Could not get sync information' + e.toString(); $scope.sync = { error: err }; }); }; }); // Source: public/src/js/controllers/transactions.js angular.module('insight.transactions').controller('transactionsController', function($scope, $rootScope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress) { $scope.global = Global; $scope.loading = false; $scope.loadedBy = null; var pageNum = 0; var pagesTotal = 1; var COIN = 100000000; var _aggregateItems = function(items) { if (!items) return []; var l = items.length; var ret = []; var tmp = {}; var u = 0; for(var i=0; i < l; i++) { var notAddr = false; // non standard input if (items[i].scriptSig && !items[i].addr) { items[i].addr = 'Unparsed address [' + u++ + ']'; items[i].notAddr = true; notAddr = true; } // non standard output if (items[i].scriptPubKey && !items[i].scriptPubKey.addresses) { items[i].scriptPubKey.addresses = ['Unparsed address [' + u++ + ']']; items[i].notAddr = true; notAddr = true; } // multiple addr at output if (items[i].scriptPubKey && items[i].scriptPubKey.addresses.length > 1) { items[i].addr = items[i].scriptPubKey.addresses.join(','); ret.push(items[i]); continue; } var addr = items[i].addr || (items[i].scriptPubKey && items[i].scriptPubKey.addresses[0]); if (!tmp[addr]) { tmp[addr] = {}; tmp[addr].valueSat = 0; tmp[addr].count = 0; tmp[addr].addr = addr; tmp[addr].items = []; } tmp[addr].isSpent = items[i].spentTxId; tmp[addr].doubleSpentTxID = tmp[addr].doubleSpentTxID || items[i].doubleSpentTxID; tmp[addr].doubleSpentIndex = tmp[addr].doubleSpentIndex || items[i].doubleSpentIndex; tmp[addr].dbError = tmp[addr].dbError || items[i].dbError; tmp[addr].valueSat += Math.round(items[i].value * COIN); tmp[addr].items.push(items[i]); tmp[addr].notAddr = notAddr; if (items[i].unconfirmedInput) tmp[addr].unconfirmedInput = true; tmp[addr].count++; } angular.forEach(tmp, function(v) { v.value = v.value || parseInt(v.valueSat) / COIN; ret.push(v); }); return ret; }; var _processTX = function(tx) { tx.vinSimple = _aggregateItems(tx.vin); tx.voutSimple = _aggregateItems(tx.vout); }; var _paginate = function(data) { $scope.loading = false; pagesTotal = data.pagesTotal; pageNum += 1; data.txs.forEach(function(tx) { _processTX(tx); $scope.txs.push(tx); }); }; var _byBlock = function() { TransactionsByBlock.get({ block: $routeParams.blockHash, pageNum: pageNum }, function(data) { _paginate(data); }); }; var _byAddress = function () { TransactionsByAddress.get({ address: $routeParams.addrStr, pageNum: pageNum }, function(data) { _paginate(data); }); }; var _findTx = function(txid) { Transaction.get({ txId: txid }, function(tx) { $rootScope.titleDetail = tx.txid.substring(0,7) + '...'; $rootScope.flashMessage = null; $scope.tx = tx; _processTX(tx); $scope.txs.unshift(tx); }, function(e) { if (e.status === 400) { $rootScope.flashMessage = 'Invalid Transaction ID: ' + $routeParams.txId; } else if (e.status === 503) { $rootScope.flashMessage = 'Backend Error. ' + e.data; } else { $rootScope.flashMessage = 'Transaction Not Found'; } $location.path('/'); }); }; $scope.findThis = function() { _findTx($routeParams.txId); }; //Initial load $scope.load = function(from) { $scope.loadedBy = from; $scope.loadMore(); }; //Load more transactions for pagination $scope.loadMore = function() { if (pageNum < pagesTotal && !$scope.loading) { $scope.loading = true; if ($scope.loadedBy === 'address') { _byAddress(); } else { _byBlock(); } } }; // Highlighted txout if ($routeParams.v_type == '>' || $routeParams.v_type == '<') { $scope.from_vin = $routeParams.v_type == '<' ? true : false; $scope.from_vout = $routeParams.v_type == '>' ? true : false; $scope.v_index = parseInt($routeParams.v_index); $scope.itemsExpanded = true; } //Init without txs $scope.txs = []; $scope.$on('tx', function(event, txid) { _findTx(txid); }); }); angular.module('insight.transactions').controller('SendRawTransactionController', function($scope, $http, Api) { $scope.transaction = ''; $scope.status = 'ready'; // ready|loading|sent|error $scope.txid = ''; $scope.error = null; $scope.formValid = function() { return !!$scope.transaction; }; $scope.send = function() { var postData = { rawtx: $scope.transaction }; $scope.status = 'loading'; $http.post(Api.apiPrefix + '/tx/send', postData) .success(function(data, status, headers, config) { if(typeof(data.txid) != 'string') { // API returned 200 but the format is not known $scope.status = 'error'; $scope.error = 'The transaction was sent but no transaction id was got back'; return; } $scope.status = 'sent'; $scope.txid = data.txid; }) .error(function(data, status, headers, config) { $scope.status = 'error'; if(data) { $scope.error = data; } else { $scope.error = "No error message given (connection error?)" } }); }; }); // Source: public/src/js/services/address.js angular.module('insight.address').factory('Address', function($resource, Api) { return $resource(Api.apiPrefix + '/addr/:addrStr/?noTxList=1', { addrStr: '@addStr' }, { get: { method: 'GET', interceptor: { response: function (res) { return res.data; }, responseError: function (res) { if (res.status === 404) { return res; } } } } }); }); // Source: public/src/js/services/api.js angular.module('insight.api') .factory('Api', function() { return { apiPrefix: '/insight-api' } }); // Source: public/src/js/services/blocks.js angular.module('insight.blocks') .factory('Block', function($resource, Api) { return $resource(Api.apiPrefix + '/block/:blockHash', { blockHash: '@blockHash' }, { get: { method: 'GET', interceptor: { response: function (res) { return res.data; }, responseError: function (res) { if (res.status === 404) { return res; } } } } }); }) .factory('Blocks', function($resource, Api) { return $resource(Api.apiPrefix + '/blocks'); }) .factory('BlockByHeight', function($resource, Api) { return $resource(Api.apiPrefix + '/block-index/:blockHeight'); }); // Source: public/src/js/services/currency.js angular.module('insight.currency').factory('Currency', function($resource, Api) { return $resource(Api.apiPrefix + '/currency'); }); // Source: public/src/js/services/global.js //Global service for global variables angular.module('insight.system') .factory('Global',[ function() { } ]) .factory('Version', function($resource, Api) { return $resource(Api.apiPrefix + '/version'); }); // Source: public/src/js/services/socket.js var ScopedSocket = function(socket, $rootScope) { this.socket = socket; this.$rootScope = $rootScope; this.listeners = []; }; ScopedSocket.prototype.removeAllListeners = function(opts) { if (!opts) opts = {}; for (var i = 0; i < this.listeners.length; i++) { var details = this.listeners[i]; if (opts.skipConnect && details.event === 'connect') { continue; } this.socket.removeListener(details.event, details.fn); } this.listeners = []; }; ScopedSocket.prototype.on = function(event, callback) { var socket = this.socket; var $rootScope = this.$rootScope; var wrapped_callback = function() { var args = arguments; $rootScope.$apply(function() { callback.apply(socket, args); }); }; socket.on(event, wrapped_callback); this.listeners.push({ event: event, fn: wrapped_callback }); }; ScopedSocket.prototype.emit = function(event, data, callback) { var socket = this.socket; var $rootScope = this.$rootScope; var args = Array.prototype.slice.call(arguments); args.push(function() { var args = arguments; $rootScope.$apply(function() { if (callback) { callback.apply(socket, args); } }); }); socket.emit.apply(socket, args); }; angular.module('insight.socket').factory('getSocket', function($rootScope) { console.log('init my socket'); var socket = io.connect(null, { 'reconnect': true, 'reconnection delay': 500, }); return function(scope) { var scopedSocket = new ScopedSocket(socket, $rootScope); scope.$on('$destroy', function() { scopedSocket.removeAllListeners(); }); socket.on('connect', function() { scopedSocket.removeAllListeners({ skipConnect: true }); }); return scopedSocket; }; }); // Source: public/src/js/services/status.js angular.module('insight.status') .factory('Status', function($resource, Api) { return $resource(Api.apiPrefix + '/status', { q: '@q' }); }) .factory('Sync', function($resource, Api) { return $resource(Api.apiPrefix + '/sync'); }) .factory('PeerSync', function($resource, Api) { return $resource(Api.apiPrefix + '/peer'); }); // Source: public/src/js/services/transactions.js angular.module('insight.transactions') .factory('Transaction', function($resource, Api) { return $resource(Api.apiPrefix + '/tx/:txId', { txId: '@txId' }, { get: { method: 'GET', interceptor: { response: function (res) { return res.data; }, responseError: function (res) { if (res.status === 404) { return res; } } } } }); }) .factory('TransactionsByBlock', function($resource, Api) { return $resource(Api.apiPrefix + '/txs', { block: '@block' }); }) .factory('TransactionsByAddress', function($resource, Api) { return $resource(Api.apiPrefix + '/txs', { address: '@address' }); }) .factory('Transactions', function($resource, Api) { return $resource(Api.apiPrefix + '/txs'); }); // Source: public/src/js/directives.js var ZeroClipboard = window.ZeroClipboard; angular.module('insight') .directive('scroll', function ($window) { return function(scope, element, attrs) { angular.element($window).bind('scroll', function() { if (this.pageYOffset >= 200) { scope.secondaryNavbar = true; } else { scope.secondaryNavbar = false; } scope.$apply(); }); }; }) .directive('whenScrolled', function($window) { return { restric: 'A', link: function(scope, elm, attr) { var pageHeight, clientHeight, scrollPos; $window = angular.element($window); var handler = function() { pageHeight = window.document.documentElement.scrollHeight; clientHeight = window.document.documentElement.clientHeight; scrollPos = window.pageYOffset; if (pageHeight - (scrollPos + clientHeight) === 0) { scope.$apply(attr.whenScrolled); } }; $window.on('scroll', handler); scope.$on('$destroy', function() { return $window.off('scroll', handler); }); } }; }) .directive('clipCopy', function() { ZeroClipboard.config({ moviePath: '/lib/zeroclipboard/ZeroClipboard.swf', trustedDomains: ['*'], allowScriptAccess: 'always', forceHandCursor: true }); return { restric: 'A', scope: { clipCopy: '=clipCopy' }, template: '