aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2014-09-23 01:33:24 +0800
committerobscuren <geffobscura@gmail.com>2014-09-23 01:33:24 +0800
commit154ca03228a9d97a4b2319f525814a16f102430f (patch)
treea6e71cdafb18f011c2472d4792075173c132bee6
parent7855a233a7ed4968d93fc76a74501c931574f6eb (diff)
parentc7d666ad61a76e79867c9f7ae783a2cc44485dd0 (diff)
downloaddexon-154ca03228a9d97a4b2319f525814a16f102430f.tar.gz
dexon-154ca03228a9d97a4b2319f525814a16f102430f.tar.zst
dexon-154ca03228a9d97a4b2319f525814a16f102430f.zip
Merge branch 'release/0.6.5'
-rw-r--r--README.md2
-rw-r--r--ethereal/assets/debugger/debugger.qml377
-rw-r--r--ethereal/assets/ext/filter.js31
-rw-r--r--ethereal/assets/ext/pre.js37
-rw-r--r--ethereal/assets/qml/wallet.qml753
-rw-r--r--ethereal/assets/qml/webapp.qml347
-rw-r--r--ethereum/flags.go1
-rw-r--r--ethereum/main.go10
-rw-r--r--javascript/javascript_runtime.go2
-rw-r--r--javascript/types.go4
-rw-r--r--mist/assets/back.png (renamed from ethereal/assets/back.png)bin1004 -> 1004 bytes
-rw-r--r--mist/assets/browser.pngbin0 -> 12903 bytes
-rw-r--r--mist/assets/bug.png (renamed from ethereal/assets/bug.png)bin1671 -> 1671 bytes
-rw-r--r--mist/assets/close.png (renamed from ethereal/assets/close.png)bin905 -> 905 bytes
-rw-r--r--mist/assets/debugger/debugger.qml435
-rw-r--r--mist/assets/ext/big.js (renamed from ethereal/assets/ext/big.js)0
-rw-r--r--mist/assets/ext/ethereum.js (renamed from ethereal/assets/ext/ethereum.js)0
-rw-r--r--mist/assets/ext/filter.js49
-rw-r--r--mist/assets/ext/home.html (renamed from ethereal/assets/ext/home.html)0
-rw-r--r--mist/assets/ext/html_messaging.js481
-rw-r--r--mist/assets/ext/http.js13
-rw-r--r--mist/assets/ext/pre.js3
-rw-r--r--mist/assets/ext/q.js1909
-rw-r--r--mist/assets/ext/qml_messaging.js13
-rw-r--r--mist/assets/ext/string.js (renamed from ethereal/assets/ext/string.js)0
-rw-r--r--mist/assets/ext/test.html (renamed from ethereal/assets/ext/test.html)0
-rw-r--r--mist/assets/facet.png (renamed from ethereal/assets/facet.png)bin27302 -> 27302 bytes
-rw-r--r--mist/assets/heart.png (renamed from ethereal/assets/heart.png)bin4277 -> 4277 bytes
-rw-r--r--mist/assets/icecream.pngbin0 -> 4643 bytes
-rw-r--r--mist/assets/muted/codemirror.css (renamed from ethereal/assets/muted/codemirror.css)0
-rw-r--r--mist/assets/muted/debugger.html (renamed from ethereal/assets/muted/debugger.html)0
-rw-r--r--mist/assets/muted/eclipse.css (renamed from ethereal/assets/muted/eclipse.css)0
-rw-r--r--mist/assets/muted/index.html (renamed from ethereal/assets/muted/index.html)0
-rw-r--r--mist/assets/muted/lib/codemirror.js (renamed from ethereal/assets/muted/lib/codemirror.js)0
-rw-r--r--mist/assets/muted/lib/go.js (renamed from ethereal/assets/muted/lib/go.js)0
-rw-r--r--mist/assets/muted/lib/matchbrackets.js (renamed from ethereal/assets/muted/lib/matchbrackets.js)0
-rw-r--r--mist/assets/muted/muted.js (renamed from ethereal/assets/muted/muted.js)0
-rw-r--r--mist/assets/net.png (renamed from ethereal/assets/net.png)bin4669 -> 4669 bytes
-rw-r--r--mist/assets/network.png (renamed from ethereal/assets/network.png)bin2900 -> 2900 bytes
-rw-r--r--mist/assets/new.png (renamed from ethereal/assets/new.png)bin4776 -> 4776 bytes
-rw-r--r--mist/assets/pick.png (renamed from ethereal/assets/pick.png)bin932 -> 932 bytes
-rw-r--r--mist/assets/qml/QmlApp.qml (renamed from ethereal/assets/qml/QmlApp.qml)0
-rw-r--r--mist/assets/qml/first_run.qml (renamed from ethereal/assets/qml/first_run.qml)0
-rw-r--r--mist/assets/qml/muted.qml (renamed from ethereal/assets/qml/muted.qml)0
-rw-r--r--mist/assets/qml/test_app.qml (renamed from ethereal/assets/qml/test_app.qml)0
-rw-r--r--mist/assets/qml/transactions.qml (renamed from ethereal/assets/qml/transactions.qml)0
-rw-r--r--mist/assets/qml/views/chain.qml (renamed from ethereal/assets/qml/views/chain.qml)20
-rw-r--r--mist/assets/qml/views/history.qml (renamed from ethereal/assets/qml/views/history.qml)0
-rw-r--r--mist/assets/qml/views/info.qml (renamed from ethereal/assets/qml/views/info.qml)129
-rw-r--r--mist/assets/qml/views/javascript.qml (renamed from ethereal/assets/qml/views/javascript.qml)0
-rw-r--r--mist/assets/qml/views/jeffcoin/jeff.pngbin0 -> 84076 bytes
-rw-r--r--mist/assets/qml/views/jeffcoin/jeffcoin.qml184
-rw-r--r--mist/assets/qml/views/pending_tx.qml (renamed from ethereal/assets/qml/views/pending_tx.qml)0
-rw-r--r--mist/assets/qml/views/transaction.qml (renamed from ethereal/assets/qml/views/transaction.qml)2
-rw-r--r--mist/assets/qml/views/wallet.qml (renamed from ethereal/assets/qml/views/wallet.qml)14
-rw-r--r--mist/assets/qml/wallet.qml894
-rw-r--r--mist/assets/qml/webapp.qml409
-rw-r--r--mist/assets/tx.png (renamed from ethereal/assets/tx.png)bin4070 -> 4070 bytes
-rw-r--r--mist/assets/util/test.html (renamed from ethereal/assets/util/test.html)0
-rw-r--r--mist/assets/wallet.png (renamed from ethereal/assets/wallet.png)bin1114 -> 1114 bytes
-rw-r--r--mist/bindings.go148
-rw-r--r--mist/debugger.go (renamed from ethereal/debugger.go)34
-rw-r--r--mist/errors.go36
-rw-r--r--mist/ext_app.go (renamed from ethereal/ext_app.go)0
-rw-r--r--mist/flags.go (renamed from ethereal/flags.go)13
-rw-r--r--mist/gui.go (renamed from ethereal/gui.go)224
-rw-r--r--mist/html_container.go (renamed from ethereal/html_container.go)0
-rw-r--r--mist/main.go (renamed from ethereal/main.go)14
-rw-r--r--mist/qml_container.go (renamed from ethereal/qml_container.go)0
-rw-r--r--mist/ui_lib.go (renamed from ethereal/ui_lib.go)110
-rw-r--r--utils/cmd.go23
71 files changed, 4910 insertions, 1811 deletions
diff --git a/README.md b/README.md
index 2d4b128fc..8cdcfe2a6 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Status](http://cpt-obvious.ethercasts.com:8010/buildstatusimage?builder=go-ether
Ethereum Go Client © 2014 Jeffrey Wilcke.
-Current state: Proof of Concept 0.6.4.
+Current state: Proof of Concept 0.6.5.
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).
diff --git a/ethereal/assets/debugger/debugger.qml b/ethereal/assets/debugger/debugger.qml
deleted file mode 100644
index 34fe01253..000000000
--- a/ethereal/assets/debugger/debugger.qml
+++ /dev/null
@@ -1,377 +0,0 @@
-import QtQuick 2.0
-import QtQuick.Controls 1.0;
-import QtQuick.Layouts 1.0;
-import QtQuick.Dialogs 1.0;
-import QtQuick.Window 2.1;
-import QtQuick.Controls.Styles 1.1
-import Ethereum 1.0
-
-ApplicationWindow {
- id: win
- visible: false
- title: "IceCREAM"
- minimumWidth: 1280
- minimumHeight: 700
- width: 1290
- height: 750
-
- property alias codeText: codeEditor.text
- property alias dataText: rawDataField.text
-
- onClosing: {
- //compileTimer.stop()
- }
-
- MenuBar {
- Menu {
- title: "Debugger"
- MenuItem {
- text: "Run"
- shortcut: "Ctrl+r"
- onTriggered: debugCurrent()
- }
-
- MenuItem {
- text: "Next"
- shortcut: "Ctrl+n"
- onTriggered: dbg.next()
- }
-
- MenuItem {
- text: "Continue"
- shortcut: "Ctrl+g"
- onTriggered: dbg.continue()
- }
- MenuItem {
- text: "Command"
- shortcut: "Ctrl+l"
- onTriggered: {
- dbgCommand.focus = true
- }
- }
- MenuItem {
- text: "Focus code"
- shortcut: "Ctrl+1"
- onTriggered: {
- codeEditor.focus = true
- }
- }
- MenuItem {
- text: "Focus data"
- shortcut: "Ctrl+2"
- onTriggered: {
- rawDataField.focus = true
- }
- }
-
- /*
- MenuItem {
- text: "Close window"
- shortcut: "Ctrl+w"
- onTriggered: {
- win.close()
- }
- }
- */
- }
- }
-
-
- SplitView {
- anchors.fill: parent
- property var asmModel: ListModel {
- id: asmModel
- }
-
- TableView {
- id: asmTableView
- width: 200
- TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 }
- model: asmModel
- }
-
- Rectangle {
- color: "#00000000"
- anchors.left: asmTableView.right
- anchors.right: parent.right
- SplitView {
- orientation: Qt.Vertical
- anchors.fill: parent
-
- Rectangle {
- color: "#00000000"
- height: 330
- anchors.left: parent.left
- anchors.right: parent.right
-
- TextArea {
- id: codeEditor
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.right: settings.left
- focus: true
-
- /*
- Timer {
- id: compileTimer
- interval: 500 ; running: true ; repeat: true
- onTriggered: {
- dbg.autoComp(codeEditor.text)
- }
- }
- */
- }
-
- Column {
- id: settings
- spacing: 5
- width: 300
- height: parent.height
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.bottom: parent.bottom
-
- Label {
- text: "Arbitrary data"
- }
- TextArea {
- id: rawDataField
- anchors.left: parent.left
- anchors.right: parent.right
- height: 150
- }
-
- Label {
- text: "Amount"
- }
- TextField {
- id: txValue
- width: 200
- placeholderText: "Amount"
- validator: RegExpValidator { regExp: /\d*/ }
- }
- Label {
- text: "Amount of gas"
- }
- TextField {
- id: txGas
- width: 200
- validator: RegExpValidator { regExp: /\d*/ }
- text: "10000"
- placeholderText: "Gas"
- }
- Label {
- text: "Gas price"
- }
- TextField {
- id: txGasPrice
- width: 200
- placeholderText: "Gas price"
- text: "1000000000000"
- validator: RegExpValidator { regExp: /\d*/ }
- }
- }
- }
-
- SplitView {
- orientation: Qt.Vertical
- id: inspectorPane
- height: 500
-
- SplitView {
- orientation: Qt.Horizontal
- height: 150
-
- TableView {
- id: stackTableView
- property var stackModel: ListModel {
- id: stackModel
- }
- height: parent.height
- width: 300
- TableViewColumn{ role: "value" ; title: "Temp" ; width: 200 }
- model: stackModel
- }
-
- TableView {
- id: memoryTableView
- property var memModel: ListModel {
- id: memModel
- }
- height: parent.height
- width: parent.width - stackTableView.width
- TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50}
- TableViewColumn{ role: "value" ; title: "Memory" ; width: 750}
- model: memModel
- }
- }
-
- Rectangle {
- height: 100
- width: parent.width
- TableView {
- id: storageTableView
- property var memModel: ListModel {
- id: storageModel
- }
- height: parent.height
- width: parent.width
- TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2}
- TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2}
- model: storageModel
- }
- }
-
- Rectangle {
- height: 200
- width: parent.width
- TableView {
- id: logTableView
- property var logModel: ListModel {
- id: logModel
- }
- height: parent.height
- width: parent.width
- TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 }
- model: logModel
- }
- }
- }
- }
- }
- }
-
- function exec() {
- dbg.execCommand(dbgCommand.text);
- dbgCommand.text = "";
- }
- statusBar: StatusBar {
- height: 30
-
-
- TextField {
- id: dbgCommand
- y: 1
- x: asmTableView.width
- width: 500
- placeholderText: "Debugger (type 'help')"
- Keys.onReturnPressed: {
- exec()
- }
- }
- }
-
- toolBar: ToolBar {
- height: 30
- RowLayout {
- spacing: 5
-
- Button {
- property var enabled: true
- id: debugStart
- onClicked: {
- debugCurrent()
- }
- text: "Debug"
- }
-
- Button {
- property var enabled: true
- id: debugNextButton
- onClicked: {
- dbg.next()
- }
- text: "Next"
- }
-
- Button {
- id: debugContinueButton
- onClicked: {
- dbg.continue()
- }
- text: "Continue"
- }
- }
-
-
- ComboBox {
- id: snippets
- anchors.right: parent.right
- model: ListModel {
- ListElement { text: "Snippets" ; value: "" }
- ListElement { text: "Call Contract" ; value: "var[2] in;\nvar ret;\n\nin[0] = \"arg1\"\nin[1] = 0xdeadbeef\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" }
- }
- onCurrentIndexChanged: {
- if(currentIndex != 0) {
- var code = snippets.model.get(currentIndex).value;
- codeEditor.insert(codeEditor.cursorPosition, code)
- }
- }
- }
-
- }
-
- function debugCurrent() {
- dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text)
- }
-
- function setAsm(asm) {
- asmModel.append({asm: asm})
- }
-
- function clearAsm() {
- asmModel.clear()
- }
-
- function setInstruction(num) {
- asmTableView.selection.clear()
- asmTableView.selection.select(num)
- }
-
- function setMem(mem) {
- memModel.append({num: mem.num, value: mem.value})
- }
- function clearMem(){
- memModel.clear()
- }
-
- function setStack(stack) {
- stackModel.append({value: stack})
- }
- function addDebugMessage(message){
- debuggerLog.append({value: message})
- }
-
- function clearStack() {
- stackModel.clear()
- }
-
- function clearStorage() {
- storageModel.clear()
- }
-
- function setStorage(storage) {
- storageModel.append({key: storage.key, value: storage.value})
- }
-
- function setLog(msg) {
- // Remove first item once we've reached max log items
- if(logModel.count > 250) {
- logModel.remove(0)
- }
-
- if(msg.len != 0) {
- if(logTableView.flickableItem.atYEnd) {
- logModel.append({message: msg})
- logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain)
- } else {
- logModel.append({message: msg})
- }
- }
- }
-
- function clearLog() {
- logModel.clear()
- }
-}
diff --git a/ethereal/assets/ext/filter.js b/ethereal/assets/ext/filter.js
deleted file mode 100644
index 5c1c03aad..000000000
--- a/ethereal/assets/ext/filter.js
+++ /dev/null
@@ -1,31 +0,0 @@
-var Filter = function(options) {
- this.callbacks = {};
- this.seed = Math.floor(Math.random() * 1000000);
- this.options = options;
-
- if(options == "chain") {
- eth.registerFilterString(options, this.seed);
- } else if(typeof options === "object") {
- eth.registerFilter(options, this.seed);
- }
-};
-
-Filter.prototype.changed = function(callback) {
- var cbseed = Math.floor(Math.random() * 1000000);
- eth.registerFilterCallback(this.seed, cbseed);
-
- var self = this;
- message.connect(function(messages, seed, callbackSeed) {
- if(seed == self.seed && callbackSeed == cbseed) {
- callback.call(self, messages);
- }
- });
-};
-
-Filter.prototype.uninstall = function() {
- eth.uninstallFilter(this.seed)
-}
-
-Filter.prototype.messages = function() {
- return JSON.parse(eth.messages(this.options))
-}
diff --git a/ethereal/assets/ext/pre.js b/ethereal/assets/ext/pre.js
deleted file mode 100644
index 3e8a534e9..000000000
--- a/ethereal/assets/ext/pre.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Helper function for generating pseudo callbacks and sending data to the QML part of the application
-function postData(data, cb) {
- data._seed = Math.floor(Math.random() * 1000000)
- if(cb) {
- eth._callbacks[data._seed] = cb;
- }
-
- if(data.args === undefined) {
- data.args = [];
- }
-
- navigator.qt.postMessage(JSON.stringify(data));
-}
-
-navigator.qt.onmessage = function(ev) {
- var data = JSON.parse(ev.data)
-
- if(data._event !== undefined) {
- eth.trigger(data._event, data.data);
- } else {
- if(data._seed) {
- var cb = eth._callbacks[data._seed];
- if(cb) {
- // Figure out whether the returned data was an array
- // array means multiple return arguments (multiple params)
- if(data.data instanceof Array) {
- cb.apply(this, data.data)
- } else {
- cb.call(this, data.data)
- }
-
- // Remove the "trigger" callback
- delete eth._callbacks[ev._seed];
- }
- }
- }
-}
diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml
deleted file mode 100644
index 90cc42a1f..000000000
--- a/ethereal/assets/qml/wallet.qml
+++ /dev/null
@@ -1,753 +0,0 @@
-import QtQuick 2.0
-import QtQuick.Controls 1.0;
-import QtQuick.Layouts 1.0;
-import QtQuick.Dialogs 1.0;
-import QtQuick.Window 2.1;
-import QtQuick.Controls.Styles 1.1
-import Ethereum 1.0
-
-import "../ext/filter.js" as Eth
-
-ApplicationWindow {
- id: root
-
- property alias miningButtonText: miningButton.text
-
-
- width: 900
- height: 600
- minimumHeight: 300
-
- title: "Ether browser"
-
- // This signal is used by the filter API. The filter API connects using this signal handler from
- // the different QML files and plugins.
- signal message(var callback, int seed, int seedCallback);
- function invokeFilterCallback(data, receiverSeed, callbackSeed) {
- var messages = JSON.parse(data)
- // Signal handler
- message(messages, receiverSeed, callbackSeed);
- }
-
- TextField {
- id: copyElementHax
- visible: false
- }
-
- function copyToClipboard(text) {
- copyElementHax.text = text
- copyElementHax.selectAll()
- copyElementHax.copy()
- }
-
- // Takes care of loading all default plugins
- Component.onCompleted: {
- var walletView = addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true})
- var historyView = addPlugin("./views/history.qml", {noAdd: true, section: "legacy"})
- var newTxView = addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"})
- var chainView = addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"})
- var infoView = addPlugin("./views/info.qml", {noAdd: true, section: "legacy"})
- var pendingTxView = addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"})
- var pendingTxView = addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"})
-
- // Call the ready handler
- gui.done()
-
- }
-
- function addPlugin(path, options) {
- var component = Qt.createComponent(path);
- if(component.status != Component.Ready) {
- if(component.status == Component.Error) {
- console.debug("Error:"+ component.errorString());
- }
-
- return
- }
-
- var views = mainSplit.addComponent(component, options)
- views.menuItem.path = path
-
- mainSplit.views.push(views);
-
- if(!options.noAdd) {
- gui.addPlugin(path)
- }
-
- return views.view
- }
-
- MenuBar {
- Menu {
- title: "File"
- MenuItem {
- text: "Import App"
- shortcut: "Ctrl+o"
- onTriggered: {
- generalFileDialog.show(true, importApp)
- }
- }
-
- MenuItem {
- text: "Browser"
- onTriggered: eth.openBrowser()
- }
-
- MenuItem {
- text: "Add plugin"
- onTriggered: {
- generalFileDialog.show(true, function(path) {
- addPlugin(path, {canClose: true, section: "apps"})
- })
- }
- }
-
- MenuSeparator {}
-
- MenuItem {
- text: "Import key"
- shortcut: "Ctrl+i"
- onTriggered: {
- generalFileDialog.show(true, function(path) {
- gui.importKey(path)
- })
- }
- }
-
- MenuItem {
- text: "Export keys"
- shortcut: "Ctrl+e"
- onTriggered: {
- generalFileDialog.show(false, function(path) {
- })
- }
- }
- }
-
- Menu {
- title: "Developer"
- MenuItem {
- text: "Debugger"
- shortcut: "Ctrl+d"
- onTriggered: eth.startDebugger()
- }
-
- MenuItem {
- text: "Import Tx"
- onTriggered: {
- txImportDialog.visible = true
- }
- }
-
- MenuItem {
- text: "Run JS file"
- onTriggered: {
- generalFileDialog.show(true, function(path) {
- eth.evalJavascriptFile(path)
- })
- }
- }
-
- MenuItem {
- text: "Dump state"
- onTriggered: {
- generalFileDialog.show(false, function(path) {
- // Empty hash for latest
- gui.dumpState("", path)
- })
- }
- }
-
- MenuSeparator {}
-
- MenuItem {
- id: miningSpeed
- text: "Mining: Turbo"
- onTriggered: {
- gui.toggleTurboMining()
- if(text == "Mining: Turbo") {
- text = "Mining: Normal";
- } else {
- text = "Mining: Turbo";
- }
- }
- }
- }
-
- Menu {
- title: "Network"
- MenuItem {
- text: "Add Peer"
- shortcut: "Ctrl+p"
- onTriggered: {
- addPeerWin.visible = true
- }
- }
- MenuItem {
- text: "Show Peers"
- shortcut: "Ctrl+e"
- onTriggered: {
- peerWindow.visible = true
- }
- }
- }
-
- Menu {
- title: "Help"
- MenuItem {
- text: "About"
- onTriggered: {
- aboutWin.visible = true
- }
- }
- }
-
- }
-
- statusBar: StatusBar {
- height: 32
- RowLayout {
- Button {
- id: miningButton
- text: "Start Mining"
- onClicked: {
- gui.toggleMining()
- }
- }
-
- Button {
- id: importAppButton
- text: "Browser"
- onClicked: {
- eth.openBrowser()
- }
- }
-
- RowLayout {
- Label {
- id: walletValueLabel
-
- font.pixelSize: 10
- styleColor: "#797979"
- }
- }
- }
-
- Label {
- y: 6
- objectName: "miningLabel"
- visible: true
- font.pixelSize: 10
- anchors.right: lastBlockLabel.left
- anchors.rightMargin: 5
- }
-
- Label {
- y: 6
- id: lastBlockLabel
- objectName: "lastBlockLabel"
- visible: true
- text: ""
- font.pixelSize: 10
- anchors.right: peerGroup.left
- anchors.rightMargin: 5
- }
-
- ProgressBar {
- id: syncProgressIndicator
- visible: false
- objectName: "syncProgressIndicator"
- y: 3
- width: 140
- indeterminate: true
- anchors.right: peerGroup.left
- anchors.rightMargin: 5
- }
-
- RowLayout {
- id: peerGroup
- y: 7
- anchors.right: parent.right
- MouseArea {
- onDoubleClicked: peerWindow.visible = true
- anchors.fill: parent
- }
-
- Label {
- id: peerLabel
- font.pixelSize: 8
- text: "0 / 0"
- }
- Image {
- id: peerImage
- width: 10; height: 10
- source: "../network.png"
- }
- }
- }
-
-
- property var blockModel: ListModel {
- id: blockModel
- }
-
- SplitView {
- property var views: [];
-
- id: mainSplit
- anchors.fill: parent
- resizing: false
-
- function setView(view, menu) {
- for(var i = 0; i < views.length; i++) {
- views[i].view.visible = false
-
- views[i].menuItem.border.color = "#00000000"
- views[i].menuItem.color = "#00000000"
- }
- view.visible = true
-
- menu.border.color = "#CCCCCC"
- menu.color = "#FFFFFFFF"
- }
-
- function addComponent(component, options) {
- var view = mainView.createView(component, options)
- view.visible = false
- view.anchors.fill = mainView
-
- if( !view.hasOwnProperty("iconSource") ) {
- console.log("Could not load plugin. Property 'iconSourc' not found on view.");
- return;
- }
-
- var menuItem = menu.createMenuItem(view.iconSource, view, options);
- if( view.hasOwnProperty("menuItem") ) {
- view.menuItem = menuItem;
- }
-
- if( view.hasOwnProperty("onReady") ) {
- view.onReady.call(view)
- }
-
- if( options.active ) {
- setView(view, menuItem)
- }
-
-
- return {view: view, menuItem: menuItem}
- }
-
- /*********************
- * Main menu.
- ********************/
- Rectangle {
- id: menu
- Layout.minimumWidth: 180
- Layout.maximumWidth: 180
- anchors.top: parent.top
- color: "#ececec"
-
- Component {
- id: menuItemTemplate
- Rectangle {
- id: menuItem
- property var view;
- property var path;
-
- property alias title: label.text
- property alias icon: icon.source
- property alias secondaryTitle: secondary.text
-
- width: 180
- height: 28
- border.color: "#00000000"
- border.width: 1
- radius: 5
- color: "#00000000"
-
- anchors {
- left: parent.left
- leftMargin: 4
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- mainSplit.setView(view, menuItem)
- }
- }
-
- Image {
- id: icon
- height: 20
- width: 20
- anchors {
- left: parent.left
- verticalCenter: parent.verticalCenter
- leftMargin: 3
- }
- MouseArea {
- anchors.fill: parent
- onClicked: {
- menuItem.closeApp()
- }
- }
- }
-
- Text {
- id: label
- anchors {
- left: icon.right
- verticalCenter: parent.verticalCenter
- leftMargin: 3
- }
-
- color: "#0D0A01"
- font.pixelSize: 12
- }
-
- Text {
- id: secondary
- anchors {
- right: parent.right
- rightMargin: 8
- verticalCenter: parent.verticalCenter
- }
- color: "#AEADBE"
- font.pixelSize: 12
- }
-
-
- function closeApp() {
- if(this.view.hasOwnProperty("onDestroy")) {
- this.view.onDestroy.call(this.view)
- }
-
- this.view.destroy()
- this.destroy()
- gui.removePlugin(this.path)
- }
- }
- }
-
- function createMenuItem(icon, view, options) {
- if(options === undefined) {
- options = {};
- }
-
- var section;
- switch(options.section) {
- case "ethereum":
- section = menuDefault;
- break;
- case "legacy":
- section = menuLegacy;
- break;
- default:
- section = menuApps;
- break;
- }
-
- var comp = menuItemTemplate.createObject(section)
-
- comp.view = view
- comp.title = view.title
- comp.icon = view.iconSource
- /*
- if(view.secondary !== undefined) {
- comp.secondary = view.secondary
- }
- */
-
- return comp
-
- /*
- if(options.canClose) {
- //comp.closeButton.visible = options.canClose
- }
- */
- }
-
- ColumnLayout {
- id: menuColumn
- y: 10
- width: parent.width
- anchors.left: parent.left
- anchors.right: parent.right
- spacing: 3
-
- Text {
- text: "ETHEREUM"
- font.bold: true
- anchors {
- left: parent.left
- leftMargin: 5
- }
- color: "#888888"
- }
-
- ColumnLayout {
- id: menuDefault
- spacing: 3
- anchors {
- left: parent.left
- right: parent.right
- }
- }
-
-
- Text {
- text: "APPS"
- font.bold: true
- anchors {
- left: parent.left
- leftMargin: 5
- }
- color: "#888888"
- }
-
- ColumnLayout {
- id: menuApps
- spacing: 3
- anchors {
- left: parent.left
- right: parent.right
- }
- }
-
- Text {
- text: "DEBUG"
- font.bold: true
- anchors {
- left: parent.left
- leftMargin: 5
- }
- color: "#888888"
- }
-
- ColumnLayout {
- id: menuLegacy
- spacing: 3
- anchors {
- left: parent.left
- right: parent.right
- }
- }
- }
- }
-
- /*********************
- * Main view
- ********************/
- Rectangle {
- id: mainView
- color: "#00000000"
-
- anchors.right: parent.right
- anchors.left: menu.right
- anchors.bottom: parent.bottom
- anchors.top: parent.top
-
- function createView(component) {
- var view = component.createObject(mainView)
-
- return view;
- }
- }
-
-
- }
-
-
- /******************
- * Dialogs
- *****************/
- FileDialog {
- id: generalFileDialog
- property var callback;
- onAccepted: {
- var path = this.fileUrl.toString();
- callback.call(this, path);
- }
-
- function show(selectExisting, callback) {
- generalFileDialog.callback = callback;
- generalFileDialog.selectExisting = selectExisting;
-
- this.open();
- }
- }
-
-
- /******************
- * Wallet functions
- *****************/
- function importApp(path) {
- var ext = path.split('.').pop()
- if(ext == "html" || ext == "htm") {
- eth.openHtml(path)
- }else if(ext == "qml"){
- addPlugin(path, {canClose: true, section: "apps"})
- }
- }
-
-
- function setWalletValue(value) {
- walletValueLabel.text = value
- }
-
- function loadPlugin(name) {
- console.log("Loading plugin" + name)
- var view = mainView.addPlugin(name)
- }
-
- function setPeers(text) {
- peerLabel.text = text
- }
-
- function addPeer(peer) {
- // We could just append the whole peer object but it cries if you try to alter them
- peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version})
- }
-
- function resetPeers(){
- peerModel.clear()
- }
-
- function timeAgo(unixTs){
- var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000
- return (lapsed + " seconds ago")
- }
-
- function convertToPretty(unixTs){
- var a = new Date(unixTs*1000);
- var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
- var year = a.getFullYear();
- var month = months[a.getMonth()];
- var date = a.getDate();
- var hour = a.getHours();
- var min = a.getMinutes();
- var sec = a.getSeconds();
- var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ;
- return time;
- }
-
- /**********************
- * Windows
- *********************/
- Window {
- id: peerWindow
- //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
- height: 200
- width: 700
- Rectangle {
- anchors.fill: parent
- property var peerModel: ListModel {
- id: peerModel
- }
- TableView {
- anchors.fill: parent
- id: peerTable
- model: peerModel
- TableViewColumn{width: 100; role: "ip" ; title: "IP" }
- TableViewColumn{width: 60; role: "port" ; title: "Port" }
- TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
- TableViewColumn{width: 100; role: "latency"; title: "Latency" }
- TableViewColumn{width: 260; role: "version" ; title: "Version" }
- }
- }
- }
-
- Window {
- id: aboutWin
- visible: false
- title: "About"
- minimumWidth: 350
- maximumWidth: 350
- maximumHeight: 200
- minimumHeight: 200
-
- Image {
- id: aboutIcon
- height: 150
- width: 150
- fillMode: Image.PreserveAspectFit
- smooth: true
- source: "../facet.png"
- x: 10
- y: 10
- }
-
- Text {
- anchors.left: aboutIcon.right
- anchors.leftMargin: 10
- font.pointSize: 12
- text: "<h2>Ethereal - Aitne</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>Viktor Trón<br>"
- }
- }
-
- Window {
- id: txImportDialog
- minimumWidth: 270
- maximumWidth: 270
- maximumHeight: 50
- minimumHeight: 50
- TextField {
- id: txImportField
- width: 170
- anchors.verticalCenter: parent.verticalCenter
- anchors.left: parent.left
- anchors.leftMargin: 10
- onAccepted: {
- }
- }
- Button {
- anchors.left: txImportField.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.leftMargin: 5
- text: "Import"
- onClicked: {
- eth.importTx(txImportField.text)
- txImportField.visible = false
- }
- }
- Component.onCompleted: {
- addrField.focus = true
- }
- }
-
- Window {
- id: addPeerWin
- visible: false
- minimumWidth: 230
- maximumWidth: 230
- maximumHeight: 50
- minimumHeight: 50
-
- TextField {
- id: addrField
- anchors.verticalCenter: parent.verticalCenter
- anchors.left: parent.left
- anchors.leftMargin: 10
- placeholderText: "address:port"
- onAccepted: {
- eth.connectToPeer(addrField.text)
- addPeerWin.visible = false
- }
- }
- Button {
- anchors.left: addrField.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.leftMargin: 5
- text: "Add"
- onClicked: {
- eth.connectToPeer(addrField.text)
- addPeerWin.visible = false
- }
- }
- Component.onCompleted: {
- addrField.focus = true
- }
- }
- }
diff --git a/ethereal/assets/qml/webapp.qml b/ethereal/assets/qml/webapp.qml
deleted file mode 100644
index ca6860036..000000000
--- a/ethereal/assets/qml/webapp.qml
+++ /dev/null
@@ -1,347 +0,0 @@
-import QtQuick 2.0
-import QtWebKit 3.0
-import QtWebKit.experimental 1.0
-import QtQuick.Controls 1.0;
-import QtQuick.Controls.Styles 1.0
-import QtQuick.Layouts 1.0;
-import QtQuick.Window 2.1;
-import Ethereum 1.0
-
-ApplicationWindow {
- id: window
- title: "Ethereum"
- width: 1000
- height: 800
- minimumHeight: 300
-
- property alias url: webview.url
- property alias webView: webview
-
- Item {
- objectName: "root"
- id: root
- anchors.fill: parent
- state: "inspectorShown"
-
- RowLayout {
- id: navBar
- height: 40
- anchors {
- left: parent.left
- right: parent.right
- leftMargin: 7
- }
-
- Button {
- id: back
- onClicked: {
- webview.goBack()
- }
- style: ButtonStyle {
- background: Image {
- source: "../back.png"
- width: 30
- height: 30
- }
- }
- }
-
- TextField {
- anchors {
- left: back.right
- right: toggleInspector.left
- leftMargin: 5
- rightMargin: 5
- }
- id: uriNav
- y: parent.height / 2 - this.height / 2
-
- Keys.onReturnPressed: {
- webview.url = this.text;
- }
- }
-
- Button {
- id: toggleInspector
- anchors {
- right: parent.right
- }
- iconSource: "../bug.png"
- onClicked: {
- if(inspector.visible == true){
- inspector.visible = false
- }else{
- inspector.visible = true
- inspector.url = webview.experimental.remoteInspectorUrl
- }
- }
- }
- }
-
-
- WebView {
- objectName: "webView"
- id: webview
- anchors {
- left: parent.left
- right: parent.right
- bottom: parent.bottom
- top: navBar.bottom
- }
- onTitleChanged: { window.title = title }
-
- property var cleanPath: false
- onNavigationRequested: {
- if(!this.cleanPath) {
- var uri = request.url.toString();
- if(!/.*\:\/\/.*/.test(uri)) {
- uri = "http://" + uri;
- }
-
- var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
-
- if(reg.test(uri)) {
- uri.replace(reg, function(match, pre, domain, path) {
- uri = pre;
-
- var lookup = ui.lookupDomain(domain.substring(0, domain.length - 4));
- var ip = [];
- for(var i = 0, l = lookup.length; i < l; i++) {
- ip.push(lookup.charCodeAt(i))
- }
-
- if(ip.length != 0) {
- uri += lookup;
- } else {
- uri += domain;
- }
-
- uri += path;
- });
- }
-
- this.cleanPath = true;
-
- webview.url = uri;
- } else {
- // Prevent inf loop.
- this.cleanPath = false;
- }
- }
-
-
- experimental.preferences.javascriptEnabled: true
- experimental.preferences.navigatorQtObjectEnabled: true
- experimental.preferences.developerExtrasEnabled: true
- experimental.userScripts: ["../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/ethereum.js"]
- experimental.onMessageReceived: {
- console.log("[onMessageReceived]: ", message.data)
- // TODO move to messaging.js
- var data = JSON.parse(message.data)
-
- try {
- switch(data.call) {
- case "getCoinBase":
- postData(data._seed, eth.coinBase())
-
- break
-
- case "getIsListening":
- postData(data._seed, eth.isListening())
-
- break
-
- case "getIsMining":
- postData(data._seed, eth.isMining())
-
- break
-
- case "getPeerCount":
- postData(data._seed, eth.peerCount())
-
- break
-
- case "getTxCountAt":
- require(1)
- postData(data._seed, eth.txCountAt(data.args[0]))
-
- break
-
- case "getBlockByNumber":
- var block = eth.blockByNumber(data.args[0])
- postData(data._seed, block)
-
- break
-
- case "getBlockByHash":
- var block = eth.blockByHash(data.args[0])
- postData(data._seed, block)
-
- break
-
- case "transact":
- require(5)
-
- var tx = eth.transact(data.args[0], data.args[1], data.args[2],data.args[3],data.args[4],data.args[5])
- postData(data._seed, tx)
-
- break
-
- case "getStorage":
- require(2);
-
- var stateObject = eth.stateObject(data.args[0])
- var storage = stateObject.storageAt(data.args[1])
- postData(data._seed, storage)
-
- break
-
- case "getEachStorage":
- require(1);
- var storage = JSON.parse(eth.eachStorage(data.args[0]))
- postData(data._seed, storage)
-
- break
-
- case "getTransactionsFor":
- require(1);
- var txs = eth.transactionsFor(data.args[0], true)
- postData(data._seed, txs)
-
- break
-
- case "getBalance":
- require(1);
-
- postData(data._seed, eth.stateObject(data.args[0]).value());
-
- break
-
- case "getKey":
- var key = eth.key().privateKey;
-
- postData(data._seed, key)
- break
-
- /*
- case "watch":
- require(1)
- eth.watch(data.args[0], data.args[1]);
-
- break
- */
- case "watch":
- require(2)
- eth.watch(data.args[0], data.args[1])
-
- case "disconnect":
- require(1)
- postData(data._seed, null)
-
- break;
-
- case "getSecretToAddress":
- require(1)
- postData(data._seed, eth.secretToAddress(data.args[0]))
-
- break;
-
- case "messages":
- require(1);
-
- var messages = JSON.parse(eth.getMessages(data.args[0]))
- postData(data._seed, messages)
-
- break
-
- case "mutan":
- require(1)
-
- var code = eth.compileMutan(data.args[0])
- postData(data._seed, "0x"+code)
-
- break;
- }
- } catch(e) {
- console.log(data.call + ": " + e)
-
- postData(data._seed, null);
- }
- }
-
- function post(seed, data) {
- console.log("data", data)
- postData(data._seed, data)
- }
-
- function require(args, num) {
- if(args.length < num) {
- throw("required argument count of "+num+" got "+args.length);
- }
- }
- function postData(seed, data) {
- webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
- }
- function postEvent(event, data) {
- webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
- }
-
- function onWatchedCb(data, id) {
- var messages = JSON.parse(data)
- postEvent("watched:"+id, messages)
- }
-
- function onNewBlockCb(block) {
- postEvent("block:new", block)
- }
- function onObjectChangeCb(stateObject) {
- postEvent("object:"+stateObject.address(), stateObject)
- }
- function onStorageChangeCb(storageObject) {
- var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
- postEvent(ev, [storageObject.address, storageObject.value])
- }
- }
-
-
- Rectangle {
- id: sizeGrip
- color: "gray"
- visible: false
- height: 10
- anchors {
- left: root.left
- right: root.right
- }
- y: Math.round(root.height * 2 / 3)
-
- MouseArea {
- anchors.fill: parent
- drag.target: sizeGrip
- drag.minimumY: 0
- drag.maximumY: root.height
- drag.axis: Drag.YAxis
- }
- }
-
- WebView {
- id: inspector
- visible: false
- anchors {
- left: root.left
- right: root.right
- top: sizeGrip.bottom
- bottom: root.bottom
- }
- }
-
- states: [
- State {
- name: "inspectorShown"
- PropertyChanges {
- target: inspector
- }
- }
- ]
- }
-}
diff --git a/ethereum/flags.go b/ethereum/flags.go
index 5ed208411..c488e6314 100644
--- a/ethereum/flags.go
+++ b/ethereum/flags.go
@@ -74,6 +74,7 @@ func Init() {
flag.IntVar(&LogLevel, "loglevel", int(ethlog.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
+ flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
diff --git a/ethereum/main.go b/ethereum/main.go
index b7c8ea1e7..df9737c1f 100644
--- a/ethereum/main.go
+++ b/ethereum/main.go
@@ -13,7 +13,7 @@ import (
const (
ClientIdentifier = "Ethereum(G)"
- Version = "0.6.4"
+ Version = "0.6.5"
)
var logger = ethlog.NewLogger("CLI")
@@ -40,6 +40,12 @@ func main() {
utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
db := utils.NewDatabase()
+ err := utils.DBSanityCheck(db)
+ if err != nil {
+ logger.Errorln(err)
+
+ os.Exit(1)
+ }
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
@@ -70,6 +76,8 @@ func main() {
os.Exit(1)
}
+ fmt.Printf("RLP: %x\nstate: %x\nhash: %x\n", ethutil.Rlp(block), block.GetRoot(), block.Hash())
+
// Leave the Println. This needs clean output for piping
fmt.Printf("%s\n", block.State().Dump())
diff --git a/javascript/javascript_runtime.go b/javascript/javascript_runtime.go
index c794c32a8..ffc672a63 100644
--- a/javascript/javascript_runtime.go
+++ b/javascript/javascript_runtime.go
@@ -42,7 +42,7 @@ func (jsre *JSRE) LoadExtFile(path string) {
}
func (jsre *JSRE) LoadIntFile(file string) {
- assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal", "assets", "ext")
+ assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist", "assets", "ext")
jsre.LoadExtFile(path.Join(assetPath, file))
}
diff --git a/javascript/types.go b/javascript/types.go
index afa0a41c6..53a2977a8 100644
--- a/javascript/types.go
+++ b/javascript/types.go
@@ -88,6 +88,10 @@ func (self *JSEthereum) GetStateObject(addr string) otto.Value {
return self.toVal(&JSStateObject{ethpipe.NewJSObject(self.JSPipe.World().SafeGet(ethutil.Hex2Bytes(addr))), self})
}
+func (self *JSEthereum) Peers() otto.Value {
+ return self.toVal(self.JSPipe.Peers())
+}
+
func (self *JSEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) otto.Value {
r, err := self.JSPipe.Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr)
if err != nil {
diff --git a/ethereal/assets/back.png b/mist/assets/back.png
index 38fc84d6e..38fc84d6e 100644
--- a/ethereal/assets/back.png
+++ b/mist/assets/back.png
Binary files differ
diff --git a/mist/assets/browser.png b/mist/assets/browser.png
new file mode 100644
index 000000000..1d7348170
--- /dev/null
+++ b/mist/assets/browser.png
Binary files differ
diff --git a/ethereal/assets/bug.png b/mist/assets/bug.png
index f5e85dc99..f5e85dc99 100644
--- a/ethereal/assets/bug.png
+++ b/mist/assets/bug.png
Binary files differ
diff --git a/ethereal/assets/close.png b/mist/assets/close.png
index 88df442c5..88df442c5 100644
--- a/ethereal/assets/close.png
+++ b/mist/assets/close.png
Binary files differ
diff --git a/mist/assets/debugger/debugger.qml b/mist/assets/debugger/debugger.qml
new file mode 100644
index 000000000..8d54b5b5d
--- /dev/null
+++ b/mist/assets/debugger/debugger.qml
@@ -0,0 +1,435 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import QtQuick.Dialogs 1.0;
+import QtQuick.Window 2.1;
+import QtQuick.Controls.Styles 1.1
+import Ethereum 1.0
+
+ApplicationWindow {
+ id: win
+ visible: false
+ title: "IceCREAM"
+ minimumWidth: 1280
+ minimumHeight: 700
+ width: 1290
+ height: 750
+
+ property alias codeText: codeEditor.text
+ property alias dataText: rawDataField.text
+
+ onClosing: {
+ dbg.Stop()
+ }
+
+ menuBar: MenuBar {
+ Menu {
+ title: "Edit"
+ MenuItem {
+ text: "Focus code"
+ shortcut: "Ctrl+1"
+ onTriggered: {
+ codeEditor.focus = true
+ }
+ }
+ MenuItem {
+ text: "Focus data"
+ shortcut: "Ctrl+2"
+ onTriggered: {
+ rawDataField.focus = true
+ }
+ }
+
+ MenuItem {
+ text: "Command"
+ shortcut: "Ctrl+l"
+ onTriggered: {
+ dbgCommand.focus = true
+ }
+ }
+ }
+
+ Menu {
+ title: "Debugger"
+ MenuItem {
+ text: "Run"
+ shortcut: "Ctrl+r"
+ onTriggered: debugCurrent()
+ }
+
+ MenuItem {
+ text: "Stop"
+ onTriggered: dbp.stop()
+ }
+
+ MenuSeparator {}
+
+ MenuItem {
+ text: "Next"
+ shortcut: "Ctrl+n"
+ onTriggered: dbg.next()
+ }
+
+ MenuItem {
+ text: "Continue"
+ shortcut: "Ctrl+g"
+ onTriggered: dbg.continue()
+ }
+ }
+ }
+
+
+ SplitView {
+ anchors.fill: parent
+ property var asmModel: ListModel {
+ id: asmModel
+ }
+
+ TableView {
+ id: asmTableView
+ width: 200
+ headerVisible: false
+ TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 }
+ model: asmModel
+ /*
+ alternatingRowColors: false
+ itemDelegate: Item {
+ Rectangle {
+ anchors.fill: parent
+ color: "#DDD"
+ Text {
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ color: "#333"
+ elide: styleData.elideMode
+ text: styleData.value
+ font.pixelSize: 11
+ MouseArea {
+ acceptedButtons: Qt.LeftButton
+ anchors.fill: parent
+ onClicked: {
+ mouse.accepted = true
+ }
+ }
+ }
+ }
+ }
+ */
+ }
+
+ Rectangle {
+ color: "#00000000"
+ anchors.left: asmTableView.right
+ anchors.right: parent.right
+ SplitView {
+ orientation: Qt.Vertical
+ anchors.fill: parent
+
+ Rectangle {
+ color: "#00000000"
+ height: 330
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ TextArea {
+ id: codeEditor
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: settings.left
+ focus: true
+
+ /*
+ Timer {
+ id: compileTimer
+ interval: 500 ; running: true ; repeat: true
+ onTriggered: {
+ dbg.autoComp(codeEditor.text)
+ }
+ }
+ */
+ }
+
+ Column {
+ id: settings
+ spacing: 5
+ width: 300
+ height: parent.height
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+
+ Label {
+ text: "Arbitrary data"
+ }
+ TextArea {
+ id: rawDataField
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 150
+ }
+
+ Label {
+ text: "Amount"
+ }
+ TextField {
+ id: txValue
+ width: 200
+ placeholderText: "Amount"
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ Label {
+ text: "Amount of gas"
+ }
+ TextField {
+ id: txGas
+ width: 200
+ validator: RegExpValidator { regExp: /\d*/ }
+ text: "10000"
+ placeholderText: "Gas"
+ }
+ Label {
+ text: "Gas price"
+ }
+ TextField {
+ id: txGasPrice
+ width: 200
+ placeholderText: "Gas price"
+ text: "1000000000000"
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ }
+ }
+
+ SplitView {
+ orientation: Qt.Vertical
+ id: inspectorPane
+ height: 500
+
+ SplitView {
+ orientation: Qt.Horizontal
+ height: 150
+
+ TableView {
+ id: stackTableView
+ property var stackModel: ListModel {
+ id: stackModel
+ }
+ height: parent.height
+ width: 300
+ TableViewColumn{ role: "value" ; title: "Local VM stack" ; width: stackTableView.width - 2 }
+ model: stackModel
+ }
+
+ TableView {
+ id: memoryTableView
+ property var memModel: ListModel {
+ id: memModel
+ }
+ height: parent.height
+ width: parent.width - stackTableView.width
+ TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 }
+ TableViewColumn{ role: "value" ; title: "Memory" ; width: 650 }
+ model: memModel
+ }
+ }
+
+ Rectangle {
+ height: 100
+ width: parent.width
+ TableView {
+ id: storageTableView
+ property var memModel: ListModel {
+ id: storageModel
+ }
+ height: parent.height
+ width: parent.width
+ TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2 - 1}
+ TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2 - 1}
+ model: storageModel
+ }
+ }
+
+ Rectangle {
+ height: 200
+ width: parent.width * 0.66
+ TableView {
+ id: logTableView
+ property var logModel: ListModel {
+ id: logModel
+ }
+ height: parent.height
+ width: parent.width
+ TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 }
+ model: logModel
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ function exec() {
+ dbg.execCommand(dbgCommand.text);
+ dbgCommand.text = "";
+ }
+ statusBar: StatusBar {
+ height: 30
+
+
+ TextField {
+ id: dbgCommand
+ y: 1
+ x: asmTableView.width
+ width: 500
+ placeholderText: "Debugger (type 'help')"
+ Keys.onReturnPressed: {
+ exec()
+ }
+ }
+
+ RowLayout {
+ anchors.left: dbgCommand.right
+ anchors.leftMargin: 10
+ spacing: 5
+ y: parent.height / 2 - this.height / 2
+
+ Text {
+ objectName: "stackFrame"
+ font.pixelSize: 10
+ text: "<b>stack ptr</b>: 0"
+ }
+
+ Text {
+ objectName: "stackSize"
+ font.pixelSize: 10
+ text: "<b>stack size</b>: 0"
+ }
+
+ Text {
+ objectName: "memSize"
+ font.pixelSize: 10
+ text: "<b>mem size</b>: 0"
+ }
+ }
+ }
+
+ toolBar: ToolBar {
+ height: 30
+ RowLayout {
+ spacing: 10
+
+ Button {
+ property var enabled: true
+ id: debugStart
+ onClicked: {
+ debugCurrent()
+ }
+ text: "Debug"
+ }
+
+ Button {
+ property var enabled: true
+ id: debugNextButton
+ onClicked: {
+ dbg.next()
+ }
+ text: "Next"
+ }
+
+ Button {
+ id: debugContinueButton
+ onClicked: {
+ dbg.continue()
+ }
+ text: "Continue"
+ }
+ }
+
+
+ ComboBox {
+ id: snippets
+ anchors.right: parent.right
+ model: ListModel {
+ ListElement { text: "Snippets" ; value: "" }
+ ListElement { text: "Call Contract" ; value: "var[2] in;\nvar ret;\n\nin[0] = \"arg1\"\nin[1] = 0xdeadbeef\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" }
+ }
+ onCurrentIndexChanged: {
+ if(currentIndex != 0) {
+ var code = snippets.model.get(currentIndex).value;
+ codeEditor.insert(codeEditor.cursorPosition, code)
+ }
+ }
+ }
+
+ }
+
+ function debugCurrent() {
+ dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text)
+ }
+
+ function setAsm(asm) {
+ asmModel.append({asm: asm})
+ }
+
+ function clearAsm() {
+ asmModel.clear()
+ }
+
+ function setInstruction(num) {
+ asmTableView.selection.clear()
+ asmTableView.selection.select(num)
+ asmTableView.positionViewAtRow(num, ListView.Center)
+ }
+
+ function setMem(mem) {
+ memModel.append({num: mem.num, value: mem.value})
+ }
+ function clearMem(){
+ memModel.clear()
+ }
+
+ function setStack(stack) {
+ stackModel.append({value: stack})
+ }
+ function addDebugMessage(message){
+ debuggerLog.append({value: message})
+ }
+
+ function clearStack() {
+ stackModel.clear()
+ }
+
+ function clearStorage() {
+ storageModel.clear()
+ }
+
+ function setStorage(storage) {
+ storageModel.append({key: storage.key, value: storage.value})
+ }
+
+ function setLog(msg) {
+ // Remove first item once we've reached max log items
+ if(logModel.count > 250) {
+ logModel.remove(0)
+ }
+
+ if(msg.len != 0) {
+ if(logTableView.flickableItem.atYEnd) {
+ logModel.append({message: msg})
+ logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain)
+ } else {
+ logModel.append({message: msg})
+ }
+ }
+ }
+
+ function clearLog() {
+ logModel.clear()
+ }
+}
diff --git a/ethereal/assets/ext/big.js b/mist/assets/ext/big.js
index db633fd2f..db633fd2f 100644
--- a/ethereal/assets/ext/big.js
+++ b/mist/assets/ext/big.js
diff --git a/ethereal/assets/ext/ethereum.js b/mist/assets/ext/ethereum.js
index 697a404a3..697a404a3 100644
--- a/ethereal/assets/ext/ethereum.js
+++ b/mist/assets/ext/ethereum.js
diff --git a/mist/assets/ext/filter.js b/mist/assets/ext/filter.js
new file mode 100644
index 000000000..c23706249
--- /dev/null
+++ b/mist/assets/ext/filter.js
@@ -0,0 +1,49 @@
+var ethx = {
+ prototype: Object,
+
+ watch: function(options) {
+ return new Filter(options);
+ },
+
+ note: function() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ var o = []
+ for(var i = 0; i < args.length; i++) {
+ o.push(args[i].toString())
+ }
+
+ eth.notef(o);
+ },
+};
+
+var Filter = function(options) {
+ this.callbacks = [];
+ this.options = options;
+
+ if(options === "chain") {
+ this.id = eth.newFilterString(options);
+ } else if(typeof options === "object") {
+ this.id = eth.newFilter(options);
+ }
+};
+
+Filter.prototype.changed = function(callback) {
+ this.callbacks.push(callback);
+
+ var self = this;
+ messages.connect(function(messages, id) {
+ if(id == self.id) {
+ for(var i = 0; i < self.callbacks.length; i++) {
+ self.callbacks[i].call(self, messages);
+ }
+ }
+ });
+};
+
+Filter.prototype.uninstall = function() {
+ eth.uninstallFilter(this.id)
+}
+
+Filter.prototype.messages = function() {
+ return eth.messages(this.id)
+}
diff --git a/ethereal/assets/ext/home.html b/mist/assets/ext/home.html
index a524e8403..a524e8403 100644
--- a/ethereal/assets/ext/home.html
+++ b/mist/assets/ext/home.html
diff --git a/mist/assets/ext/html_messaging.js b/mist/assets/ext/html_messaging.js
new file mode 100644
index 000000000..3c67c77ea
--- /dev/null
+++ b/mist/assets/ext/html_messaging.js
@@ -0,0 +1,481 @@
+// The magic return variable. The magic return variable will be set during the execution of the QML call.
+(function(window) {
+ function message(type, data) {
+ document.title = JSON.stringify({type: type, data: data});
+
+ return window.____returnData;
+ }
+
+ function isPromise(o) {
+ return typeof o === "object" && o.then
+ }
+
+ window.eth = {
+ _callbacks: {},
+ _events: {},
+ prototype: Object(),
+
+ toHex: function(str) {
+ var hex = "";
+ for(var i = 0; i < str.length; i++) {
+ var n = str.charCodeAt(i).toString(16);
+ hex += n.length < 2 ? '0' + n : n;
+ }
+
+ return hex;
+ },
+
+ toAscii: function(hex) {
+ // Find termination
+ var str = "";
+ var i = 0, l = hex.length;
+ for(; i < l; i+=2) {
+ var code = hex.charCodeAt(i)
+ if(code == 0) {
+ break;
+ }
+
+ str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+
+ return str;
+ },
+
+ fromAscii: function(str, pad) {
+ if(pad === undefined) {
+ pad = 32
+ }
+
+ var hex = this.toHex(str);
+
+ while(hex.length < pad*2)
+ hex += "00";
+
+ return hex
+ },
+
+ block: function(numberOrHash) {
+ return new Promise(function(resolve, reject) {
+ var func;
+ if(typeof numberOrHash == "string") {
+ func = "getBlockByHash";
+ } else {
+ func = "getBlockByNumber";
+ }
+
+ postData({call: func, args: [numberOrHash]}, function(block) {
+ if(block)
+ resolve(block);
+ else
+ reject("not found");
+
+ });
+ });
+ },
+
+ transact: function(params) {
+ if(params === undefined) {
+ params = {};
+ }
+
+ if(params.endowment !== undefined)
+ params.value = params.endowment;
+ if(params.code !== undefined)
+ params.data = params.code;
+
+
+ var promises = []
+ if(isPromise(params.to)) {
+ promises.push(params.to.then(function(_to) { params.to = _to; }));
+ }
+ if(isPromise(params.from)) {
+ promises.push(params.from.then(function(_from) { params.from = _from; }));
+ }
+
+ if(isPromise(params.data)) {
+ promises.push(params.data.then(function(_code) { params.data = _code; }));
+ } else {
+ if(typeof params.data === "object") {
+ data = "";
+ for(var i = 0; i < params.data.length; i++) {
+ data += params.data[i]
+ }
+ } else {
+ data = params.data;
+ }
+ }
+
+ // Make sure everything is string
+ var fields = ["value", "gas", "gasPrice"];
+ for(var i = 0; i < fields.length; i++) {
+ if(params[fields[i]] === undefined) {
+ params[fields[i]] = "";
+ }
+ params[fields[i]] = params[fields[i]].toString();
+ }
+
+ // Load promises then call the last "transact".
+ return Q.all(promises).then(function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "transact", args: params}, function(data) {
+ if(data[1])
+ reject(data[0]);
+ else
+ resolve(data[0]);
+ });
+ });
+ })
+ },
+
+ compile: function(code) {
+ return new Promise(function(resolve, reject) {
+ postData({call: "compile", args: [code]}, function(data) {
+ if(data[1])
+ reject(data[0]);
+ else
+ resolve(data[0]);
+ });
+ });
+ },
+
+ balanceAt: function(address) {
+ var promises = [];
+
+ if(isPromise(address)) {
+ promises.push(address.then(function(_address) { address = _address; }));
+ }
+
+ return Q.all(promises).then(function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getBalanceAt", args: [address]}, function(balance) {
+ resolve(balance);
+ });
+ });
+ });
+ },
+
+ countAt: function(address) {
+ var promises = [];
+
+ if(isPromise(address)) {
+ promises.push(address.then(function(_address) { address = _address; }));
+ }
+
+ return Q.all(promises).then(function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getCountAt", args: [address]}, function(count) {
+ resolve(count);
+ });
+ });
+ });
+ },
+
+ codeAt: function(address) {
+ var promises = [];
+
+ if(isPromise(address)) {
+ promises.push(address.then(function(_address) { address = _address; }));
+ }
+
+ return Q.all(promises).then(function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getCodeAt", args: [address]}, function(code) {
+ resolve(code);
+ });
+ });
+ });
+ },
+
+ storageAt: function(address, storageAddress) {
+ var promises = [];
+
+ if(isPromise(address)) {
+ promises.push(address.then(function(_address) { address = _address; }));
+ }
+
+ if(isPromise(storageAddress)) {
+ promises.push(storageAddress.then(function(_sa) { storageAddress = _sa; }));
+ }
+
+ return Q.all(promises).then(function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getStorageAt", args: [address, storageAddress]}, function(entry) {
+ resolve(entry);
+ });
+ });
+ });
+ },
+
+ stateAt: function(address, storageAddress) {
+ return this.storageAt(address, storageAddress);
+ },
+
+ call: function(params) {
+ if(params === undefined) {
+ params = {};
+ }
+
+ if(params.endowment !== undefined)
+ params.value = params.endowment;
+ if(params.code !== undefined)
+ params.data = params.code;
+
+
+ var promises = []
+ if(isPromise(params.to)) {
+ promises.push(params.to.then(function(_to) { params.to = _to; }));
+ }
+ if(isPromise(params.from)) {
+ promises.push(params.from.then(function(_from) { params.from = _from; }));
+ }
+
+ if(isPromise(params.data)) {
+ promises.push(params.data.then(function(_code) { params.data = _code; }));
+ } else {
+ if(typeof params.data === "object") {
+ data = "";
+ for(var i = 0; i < params.data.length; i++) {
+ data += params.data[i]
+ }
+ } else {
+ data = params.data;
+ }
+ }
+
+ // Make sure everything is string
+ var fields = ["value", "gas", "gasPrice"];
+ for(var i = 0; i < fields.length; i++) {
+ if(params[fields[i]] === undefined) {
+ params[fields[i]] = "";
+ }
+ params[fields[i]] = params[fields[i]].toString();
+ }
+
+ // Load promises then call the last "transact".
+ return Q.all(promises).then(function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "call", args: params}, function(data) {
+ if(data[1])
+ reject(data[0]);
+ else
+ resolve(data[0]);
+ });
+ });
+ })
+ },
+
+ watch: function(params) {
+ return new Filter(params);
+ },
+
+ secretToAddress: function(key) {
+ var promises = [];
+ if(isPromise(key)) {
+ promises.push(key.then(function(_key) { key = _key; }));
+ }
+
+ return Q.all(promises).then(function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getSecretToAddress", args: [key]}, function(address) {
+ resolve(address);
+ });
+ });
+ });
+ },
+
+ on: function(event, cb) {
+ if(eth._events[event] === undefined) {
+ eth._events[event] = [];
+ }
+
+ eth._events[event].push(cb);
+
+ return this
+ },
+
+ off: function(event, cb) {
+ if(eth._events[event] !== undefined) {
+ var callbacks = eth._events[event];
+ for(var i = 0; i < callbacks.length; i++) {
+ if(callbacks[i] === cb) {
+ delete callbacks[i];
+ }
+ }
+ }
+
+ return this
+ },
+
+ trigger: function(event, data) {
+ var callbacks = eth._events[event];
+ if(callbacks !== undefined) {
+ for(var i = 0; i < callbacks.length; i++) {
+ // Figure out whether the returned data was an array
+ // array means multiple return arguments (multiple params)
+ if(data instanceof Array) {
+ callbacks[i].apply(this, data);
+ } else {
+ callbacks[i].call(this, data);
+ }
+ }
+ }
+ },
+ };
+
+ // Eth object properties
+ Object.defineProperty(eth, "key", {
+ get: function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getKey"}, function(k) {
+ resolve(k);
+ });
+ });
+ },
+ });
+
+ Object.defineProperty(eth, "gasPrice", {
+ get: function() {
+ return "1000000000000"
+ }
+ });
+
+ Object.defineProperty(eth, "coinbase", {
+ get: function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getCoinBase"}, function(coinbase) {
+ resolve(coinbase);
+ });
+ });
+ },
+ });
+
+ Object.defineProperty(eth, "listening", {
+ get: function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getIsListening"}, function(listening) {
+ resolve(listening);
+ });
+ });
+ },
+ });
+
+
+ Object.defineProperty(eth, "mining", {
+ get: function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getIsMining"}, function(mining) {
+ resolve(mining);
+ });
+ });
+ },
+ });
+
+ Object.defineProperty(eth, "peerCount", {
+ get: function() {
+ return new Promise(function(resolve, reject) {
+ postData({call: "getPeerCount"}, function(peerCount) {
+ resolve(peerCount);
+ });
+ });
+ },
+ });
+
+ var filters = [];
+ var Filter = function(options) {
+ filters.push(this);
+
+ this.callbacks = [];
+ this.options = options;
+
+ var call;
+ if(options === "chain") {
+ call = "newFilterString"
+ } else if(typeof options === "object") {
+ call = "newFilter"
+ }
+
+ var self = this; // Cheaper than binding
+ this.promise = new Promise(function(resolve, reject) {
+ postData({call: call, args: [options]}, function(id) {
+ self.id = id;
+
+ resolve(id);
+ });
+ });
+ };
+
+ Filter.prototype.changed = function(callback) {
+ var self = this;
+ this.promise.then(function(id) {
+ self.callbacks.push(callback);
+ });
+ };
+
+ Filter.prototype.trigger = function(messages, id) {
+ if(id == this.id) {
+ for(var i = 0; i < this.callbacks.length; i++) {
+ this.callbacks[i].call(this, messages);
+ }
+ }
+ };
+
+ Filter.prototype.uninstall = function() {
+ this.promise.then(function(id) {
+ postData({call: "uninstallFilter", args:[id]});
+ });
+ };
+
+ Filter.prototype.messages = function() {
+ var self=this;
+ return Q.all([this.promise]).then(function() {
+ var id = self.id
+ return new Promise(function(resolve, reject) {
+ postData({call: "getMessages", args: [id]}, function(messages) {
+ resolve(messages);
+ });
+ });
+ });
+ };
+
+ // Register to the messages callback. "messages" will be emitted when new messages
+ // from the client have been created.
+ eth.on("messages", function(messages, id) {
+ for(var i = 0; i < filters.length; i++) {
+ filters[i].trigger(messages, id);
+ }
+ });
+
+ var g_seed = 1;
+ function postData(data, cb) {
+ data._seed = g_seed;
+ if(cb) {
+ eth._callbacks[data._seed] = cb;
+ }
+
+ if(data.args === undefined) {
+ data.args = [];
+ }
+
+ g_seed++;
+
+ navigator.qt.postMessage(JSON.stringify(data));
+ }
+
+ navigator.qt.onmessage = function(ev) {
+ var data = JSON.parse(ev.data)
+
+ if(data._event !== undefined) {
+ eth.trigger(data._event, data.data);
+ } else {
+ if(data._seed) {
+ var cb = eth._callbacks[data._seed];
+ if(cb) {
+ cb.call(this, data.data)
+
+ // Remove the "trigger" callback
+ delete eth._callbacks[ev._seed];
+ }
+ }
+ }
+ }
+})(this);
diff --git a/mist/assets/ext/http.js b/mist/assets/ext/http.js
new file mode 100644
index 000000000..725ce8e6b
--- /dev/null
+++ b/mist/assets/ext/http.js
@@ -0,0 +1,13 @@
+// this function is included locally, but you can also include separately via a header definition
+function request(url, callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = (function(req) {
+ return function() {
+ if(req.readyState === 4) {
+ callback(req);
+ }
+ }
+ })(xhr);
+ xhr.open('GET', url, true);
+ xhr.send('');
+}
diff --git a/mist/assets/ext/pre.js b/mist/assets/ext/pre.js
new file mode 100644
index 000000000..f298fe9a1
--- /dev/null
+++ b/mist/assets/ext/pre.js
@@ -0,0 +1,3 @@
+if(typeof(Promise) === "undefined") {
+ window.Promise = Q.Promise;
+}
diff --git a/mist/assets/ext/q.js b/mist/assets/ext/q.js
new file mode 100644
index 000000000..23c4245ee
--- /dev/null
+++ b/mist/assets/ext/q.js
@@ -0,0 +1,1909 @@
+// vim:ts=4:sts=4:sw=4:
+/*!
+ *
+ * Copyright 2009-2012 Kris Kowal under the terms of the MIT
+ * license found at http://github.com/kriskowal/q/raw/master/LICENSE
+ *
+ * With parts by Tyler Close
+ * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found
+ * at http://www.opensource.org/licenses/mit-license.html
+ * Forked at ref_send.js version: 2009-05-11
+ *
+ * With parts by Mark Miller
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+(function (definition) {
+ // Turn off strict mode for this function so we can assign to global.Q
+ /* jshint strict: false */
+
+ // This file will function properly as a <script> tag, or a module
+ // using CommonJS and NodeJS or RequireJS module formats. In
+ // Common/Node/RequireJS, the module exports the Q API and when
+ // executed as a simple <script>, it creates a Q global instead.
+
+ // Montage Require
+ if (typeof bootstrap === "function") {
+ bootstrap("promise", definition);
+
+ // CommonJS
+ } else if (typeof exports === "object" && typeof module === "object") {
+ module.exports = definition();
+
+ // RequireJS
+ } else if (typeof define === "function" && define.amd) {
+ define(definition);
+
+ // SES (Secure EcmaScript)
+ } else if (typeof ses !== "undefined") {
+ if (!ses.ok()) {
+ return;
+ } else {
+ ses.makeQ = definition;
+ }
+
+ // <script>
+ } else {
+ Q = definition();
+ }
+
+})(function () {
+"use strict";
+
+var hasStacks = false;
+try {
+ throw new Error();
+} catch (e) {
+ hasStacks = !!e.stack;
+}
+
+// All code after this point will be filtered from stack traces reported
+// by Q.
+var qStartingLine = captureLine();
+var qFileName;
+
+// shims
+
+// used for fallback in "allResolved"
+var noop = function () {};
+
+// Use the fastest possible means to execute a task in a future turn
+// of the event loop.
+var nextTick =(function () {
+ // linked list of tasks (single, with head node)
+ var head = {task: void 0, next: null};
+ var tail = head;
+ var flushing = false;
+ var requestTick = void 0;
+ var isNodeJS = false;
+
+ function flush() {
+ /* jshint loopfunc: true */
+
+ while (head.next) {
+ head = head.next;
+ var task = head.task;
+ head.task = void 0;
+ var domain = head.domain;
+
+ if (domain) {
+ head.domain = void 0;
+ domain.enter();
+ }
+
+ try {
+ task();
+
+ } catch (e) {
+ if (isNodeJS) {
+ // In node, uncaught exceptions are considered fatal errors.
+ // Re-throw them synchronously to interrupt flushing!
+
+ // Ensure continuation if the uncaught exception is suppressed
+ // listening "uncaughtException" events (as domains does).
+ // Continue in next event to avoid tick recursion.
+ if (domain) {
+ domain.exit();
+ }
+ setTimeout(flush, 0);
+ if (domain) {
+ domain.enter();
+ }
+
+ throw e;
+
+ } else {
+ // In browsers, uncaught exceptions are not fatal.
+ // Re-throw them asynchronously to avoid slow-downs.
+ setTimeout(function() {
+ throw e;
+ }, 0);
+ }
+ }
+
+ if (domain) {
+ domain.exit();
+ }
+ }
+
+ flushing = false;
+ }
+
+ nextTick = function (task) {
+ tail = tail.next = {
+ task: task,
+ domain: isNodeJS && process.domain,
+ next: null
+ };
+
+ if (!flushing) {
+ flushing = true;
+ requestTick();
+ }
+ };
+
+ if (typeof process !== "undefined" && process.nextTick) {
+ // Node.js before 0.9. Note that some fake-Node environments, like the
+ // Mocha test runner, introduce a `process` global without a `nextTick`.
+ isNodeJS = true;
+
+ requestTick = function () {
+ process.nextTick(flush);
+ };
+
+ } else if (typeof setImmediate === "function") {
+ // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate
+ if (typeof window !== "undefined") {
+ requestTick = setImmediate.bind(window, flush);
+ } else {
+ requestTick = function () {
+ setImmediate(flush);
+ };
+ }
+
+ } else if (typeof MessageChannel !== "undefined") {
+ // modern browsers
+ // http://www.nonblocking.io/2011/06/windownexttick.html
+ var channel = new MessageChannel();
+ // At least Safari Version 6.0.5 (8536.30.1) intermittently cannot create
+ // working message ports the first time a page loads.
+ channel.port1.onmessage = function () {
+ requestTick = requestPortTick;
+ channel.port1.onmessage = flush;
+ flush();
+ };
+ var requestPortTick = function () {
+ // Opera requires us to provide a message payload, regardless of
+ // whether we use it.
+ channel.port2.postMessage(0);
+ };
+ requestTick = function () {
+ setTimeout(flush, 0);
+ requestPortTick();
+ };
+
+ } else {
+ // old browsers
+ requestTick = function () {
+ setTimeout(flush, 0);
+ };
+ }
+
+ return nextTick;
+})();
+
+// Attempt to make generics safe in the face of downstream
+// modifications.
+// There is no situation where this is necessary.
+// If you need a security guarantee, these primordials need to be
+// deeply frozen anyway, and if you don’t need a security guarantee,
+// this is just plain paranoid.
+// However, this **might** have the nice side-effect of reducing the size of
+// the minified code by reducing x.call() to merely x()
+// See Mark Miller’s explanation of what this does.
+// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
+var call = Function.call;
+function uncurryThis(f) {
+ return function () {
+ return call.apply(f, arguments);
+ };
+}
+// This is equivalent, but slower:
+// uncurryThis = Function_bind.bind(Function_bind.call);
+// http://jsperf.com/uncurrythis
+
+var array_slice = uncurryThis(Array.prototype.slice);
+
+var array_reduce = uncurryThis(
+ Array.prototype.reduce || function (callback, basis) {
+ var index = 0,
+ length = this.length;
+ // concerning the initial value, if one is not provided
+ if (arguments.length === 1) {
+ // seek to the first value in the array, accounting
+ // for the possibility that is is a sparse array
+ do {
+ if (index in this) {
+ basis = this[index++];
+ break;
+ }
+ if (++index >= length) {
+ throw new TypeError();
+ }
+ } while (1);
+ }
+ // reduce
+ for (; index < length; index++) {
+ // account for the possibility that the array is sparse
+ if (index in this) {
+ basis = callback(basis, this[index], index);
+ }
+ }
+ return basis;
+ }
+);
+
+var array_indexOf = uncurryThis(
+ Array.prototype.indexOf || function (value) {
+ // not a very good shim, but good enough for our one use of it
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] === value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+);
+
+var array_map = uncurryThis(
+ Array.prototype.map || function (callback, thisp) {
+ var self = this;
+ var collect = [];
+ array_reduce(self, function (undefined, value, index) {
+ collect.push(callback.call(thisp, value, index, self));
+ }, void 0);
+ return collect;
+ }
+);
+
+var object_create = Object.create || function (prototype) {
+ function Type() { }
+ Type.prototype = prototype;
+ return new Type();
+};
+
+var object_hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty);
+
+var object_keys = Object.keys || function (object) {
+ var keys = [];
+ for (var key in object) {
+ if (object_hasOwnProperty(object, key)) {
+ keys.push(key);
+ }
+ }
+ return keys;
+};
+
+var object_toString = uncurryThis(Object.prototype.toString);
+
+function isObject(value) {
+ return value === Object(value);
+}
+
+// generator related shims
+
+// FIXME: Remove this function once ES6 generators are in SpiderMonkey.
+function isStopIteration(exception) {
+ return (
+ object_toString(exception) === "[object StopIteration]" ||
+ exception instanceof QReturnValue
+ );
+}
+
+// FIXME: Remove this helper and Q.return once ES6 generators are in
+// SpiderMonkey.
+var QReturnValue;
+if (typeof ReturnValue !== "undefined") {
+ QReturnValue = ReturnValue;
+} else {
+ QReturnValue = function (value) {
+ this.value = value;
+ };
+}
+
+// long stack traces
+
+var STACK_JUMP_SEPARATOR = "From previous event:";
+
+function makeStackTraceLong(error, promise) {
+ // If possible, transform the error stack trace by removing Node and Q
+ // cruft, then concatenating with the stack trace of `promise`. See #57.
+ if (hasStacks &&
+ promise.stack &&
+ typeof error === "object" &&
+ error !== null &&
+ error.stack &&
+ error.stack.indexOf(STACK_JUMP_SEPARATOR) === -1
+ ) {
+ var stacks = [];
+ for (var p = promise; !!p; p = p.source) {
+ if (p.stack) {
+ stacks.unshift(p.stack);
+ }
+ }
+ stacks.unshift(error.stack);
+
+ var concatedStacks = stacks.join("\n" + STACK_JUMP_SEPARATOR + "\n");
+ error.stack = filterStackString(concatedStacks);
+ }
+}
+
+function filterStackString(stackString) {
+ var lines = stackString.split("\n");
+ var desiredLines = [];
+ for (var i = 0; i < lines.length; ++i) {
+ var line = lines[i];
+
+ if (!isInternalFrame(line) && !isNodeFrame(line) && line) {
+ desiredLines.push(line);
+ }
+ }
+ return desiredLines.join("\n");
+}
+
+function isNodeFrame(stackLine) {
+ return stackLine.indexOf("(module.js:") !== -1 ||
+ stackLine.indexOf("(node.js:") !== -1;
+}
+
+function getFileNameAndLineNumber(stackLine) {
+ // Named functions: "at functionName (filename:lineNumber:columnNumber)"
+ // In IE10 function name can have spaces ("Anonymous function") O_o
+ var attempt1 = /at .+ \((.+):(\d+):(?:\d+)\)$/.exec(stackLine);
+ if (attempt1) {
+ return [attempt1[1], Number(attempt1[2])];
+ }
+
+ // Anonymous functions: "at filename:lineNumber:columnNumber"
+ var attempt2 = /at ([^ ]+):(\d+):(?:\d+)$/.exec(stackLine);
+ if (attempt2) {
+ return [attempt2[1], Number(attempt2[2])];
+ }
+
+ // Firefox style: "function@filename:lineNumber or @filename:lineNumber"
+ var attempt3 = /.*@(.+):(\d+)$/.exec(stackLine);
+ if (attempt3) {
+ return [attempt3[1], Number(attempt3[2])];
+ }
+}
+
+function isInternalFrame(stackLine) {
+ var fileNameAndLineNumber = getFileNameAndLineNumber(stackLine);
+
+ if (!fileNameAndLineNumber) {
+ return false;
+ }
+
+ var fileName = fileNameAndLineNumber[0];
+ var lineNumber = fileNameAndLineNumber[1];
+
+ return fileName === qFileName &&
+ lineNumber >= qStartingLine &&
+ lineNumber <= qEndingLine;
+}
+
+// discover own file name and line number range for filtering stack
+// traces
+function captureLine() {
+ if (!hasStacks) {
+ return;
+ }
+
+ try {
+ throw new Error();
+ } catch (e) {
+ var lines = e.stack.split("\n");
+ var firstLine = lines[0].indexOf("@") > 0 ? lines[1] : lines[2];
+ var fileNameAndLineNumber = getFileNameAndLineNumber(firstLine);
+ if (!fileNameAndLineNumber) {
+ return;
+ }
+
+ qFileName = fileNameAndLineNumber[0];
+ return fileNameAndLineNumber[1];
+ }
+}
+
+function deprecate(callback, name, alternative) {
+ return function () {
+ if (typeof console !== "undefined" &&
+ typeof console.warn === "function") {
+ console.warn(name + " is deprecated, use " + alternative +
+ " instead.", new Error("").stack);
+ }
+ return callback.apply(callback, arguments);
+ };
+}
+
+// end of shims
+// beginning of real work
+
+/**
+ * Constructs a promise for an immediate reference, passes promises through, or
+ * coerces promises from different systems.
+ * @param value immediate reference or promise
+ */
+function Q(value) {
+ // If the object is already a Promise, return it directly. This enables
+ // the resolve function to both be used to created references from objects,
+ // but to tolerably coerce non-promises to promises.
+ if (value instanceof Promise) {
+ return value;
+ }
+
+ // assimilate thenables
+ if (isPromiseAlike(value)) {
+ return coerce(value);
+ } else {
+ return fulfill(value);
+ }
+}
+Q.resolve = Q;
+
+/**
+ * Performs a task in a future turn of the event loop.
+ * @param {Function} task
+ */
+Q.nextTick = nextTick;
+
+/**
+ * Controls whether or not long stack traces will be on
+ */
+Q.longStackSupport = false;
+
+/**
+ * Constructs a {promise, resolve, reject} object.
+ *
+ * `resolve` is a callback to invoke with a more resolved value for the
+ * promise. To fulfill the promise, invoke `resolve` with any value that is
+ * not a thenable. To reject the promise, invoke `resolve` with a rejected
+ * thenable, or invoke `reject` with the reason directly. To resolve the
+ * promise to another thenable, thus putting it in the same state, invoke
+ * `resolve` with that other thenable.
+ */
+Q.defer = defer;
+function defer() {
+ // if "messages" is an "Array", that indicates that the promise has not yet
+ // been resolved. If it is "undefined", it has been resolved. Each
+ // element of the messages array is itself an array of complete arguments to
+ // forward to the resolved promise. We coerce the resolution value to a
+ // promise using the `resolve` function because it handles both fully
+ // non-thenable values and other thenables gracefully.
+ var messages = [], progressListeners = [], resolvedPromise;
+
+ var deferred = object_create(defer.prototype);
+ var promise = object_create(Promise.prototype);
+
+ promise.promiseDispatch = function (resolve, op, operands) {
+ var args = array_slice(arguments);
+ if (messages) {
+ messages.push(args);
+ if (op === "when" && operands[1]) { // progress operand
+ progressListeners.push(operands[1]);
+ }
+ } else {
+ nextTick(function () {
+ resolvedPromise.promiseDispatch.apply(resolvedPromise, args);
+ });
+ }
+ };
+
+ // XXX deprecated
+ promise.valueOf = function () {
+ if (messages) {
+ return promise;
+ }
+ var nearerValue = nearer(resolvedPromise);
+ if (isPromise(nearerValue)) {
+ resolvedPromise = nearerValue; // shorten chain
+ }
+ return nearerValue;
+ };
+
+ promise.inspect = function () {
+ if (!resolvedPromise) {
+ return { state: "pending" };
+ }
+ return resolvedPromise.inspect();
+ };
+
+ if (Q.longStackSupport && hasStacks) {
+ try {
+ throw new Error();
+ } catch (e) {
+ // NOTE: don't try to use `Error.captureStackTrace` or transfer the
+ // accessor around; that causes memory leaks as per GH-111. Just
+ // reify the stack trace as a string ASAP.
+ //
+ // At the same time, cut off the first line; it's always just
+ // "[object Promise]\n", as per the `toString`.
+ promise.stack = e.stack.substring(e.stack.indexOf("\n") + 1);
+ }
+ }
+
+ // NOTE: we do the checks for `resolvedPromise` in each method, instead of
+ // consolidating them into `become`, since otherwise we'd create new
+ // promises with the lines `become(whatever(value))`. See e.g. GH-252.
+
+ function become(newPromise) {
+ resolvedPromise = newPromise;
+ promise.source = newPromise;
+
+ array_reduce(messages, function (undefined, message) {
+ nextTick(function () {
+ newPromise.promiseDispatch.apply(newPromise, message);
+ });
+ }, void 0);
+
+ messages = void 0;
+ progressListeners = void 0;
+ }
+
+ deferred.promise = promise;
+ deferred.resolve = function (value) {
+ if (resolvedPromise) {
+ return;
+ }
+
+ become(Q(value));
+ };
+
+ deferred.fulfill = function (value) {
+ if (resolvedPromise) {
+ return;
+ }
+
+ become(fulfill(value));
+ };
+ deferred.reject = function (reason) {
+ if (resolvedPromise) {
+ return;
+ }
+
+ become(reject(reason));
+ };
+ deferred.notify = function (progress) {
+ if (resolvedPromise) {
+ return;
+ }
+
+ array_reduce(progressListeners, function (undefined, progressListener) {
+ nextTick(function () {
+ progressListener(progress);
+ });
+ }, void 0);
+ };
+
+ return deferred;
+}
+
+/**
+ * Creates a Node-style callback that will resolve or reject the deferred
+ * promise.
+ * @returns a nodeback
+ */
+defer.prototype.makeNodeResolver = function () {
+ var self = this;
+ return function (error, value) {
+ if (error) {
+ self.reject(error);
+ } else if (arguments.length > 2) {
+ self.resolve(array_slice(arguments, 1));
+ } else {
+ self.resolve(value);
+ }
+ };
+};
+
+/**
+ * @param resolver {Function} a function that returns nothing and accepts
+ * the resolve, reject, and notify functions for a deferred.
+ * @returns a promise that may be resolved with the given resolve and reject
+ * functions, or rejected by a thrown exception in resolver
+ */
+Q.Promise = promise; // ES6
+Q.promise = promise;
+function promise(resolver) {
+ if (typeof resolver !== "function") {
+ throw new TypeError("resolver must be a function.");
+ }
+ var deferred = defer();
+ try {
+ resolver(deferred.resolve, deferred.reject, deferred.notify);
+ } catch (reason) {
+ deferred.reject(reason);
+ }
+ return deferred.promise;
+}
+
+promise.race = race; // ES6
+promise.all = all; // ES6
+promise.reject = reject; // ES6
+promise.resolve = Q; // ES6
+
+// XXX experimental. This method is a way to denote that a local value is
+// serializable and should be immediately dispatched to a remote upon request,
+// instead of passing a reference.
+Q.passByCopy = function (object) {
+ //freeze(object);
+ //passByCopies.set(object, true);
+ return object;
+};
+
+Promise.prototype.passByCopy = function () {
+ //freeze(object);
+ //passByCopies.set(object, true);
+ return this;
+};
+
+/**
+ * If two promises eventually fulfill to the same value, promises that value,
+ * but otherwise rejects.
+ * @param x {Any*}
+ * @param y {Any*}
+ * @returns {Any*} a promise for x and y if they are the same, but a rejection
+ * otherwise.
+ *
+ */
+Q.join = function (x, y) {
+ return Q(x).join(y);
+};
+
+Promise.prototype.join = function (that) {
+ return Q([this, that]).spread(function (x, y) {
+ if (x === y) {
+ // TODO: "===" should be Object.is or equiv
+ return x;
+ } else {
+ throw new Error("Can't join: not the same: " + x + " " + y);
+ }
+ });
+};
+
+/**
+ * Returns a promise for the first of an array of promises to become fulfilled.
+ * @param answers {Array[Any*]} promises to race
+ * @returns {Any*} the first promise to be fulfilled
+ */
+Q.race = race;
+function race(answerPs) {
+ return promise(function(resolve, reject) {
+ // Switch to this once we can assume at least ES5
+ // answerPs.forEach(function(answerP) {
+ // Q(answerP).then(resolve, reject);
+ // });
+ // Use this in the meantime
+ for (var i = 0, len = answerPs.length; i < len; i++) {
+ Q(answerPs[i]).then(resolve, reject);
+ }
+ });
+}
+
+Promise.prototype.race = function () {
+ return this.then(Q.race);
+};
+
+/**
+ * Constructs a Promise with a promise descriptor object and optional fallback
+ * function. The descriptor contains methods like when(rejected), get(name),
+ * set(name, value), post(name, args), and delete(name), which all
+ * return either a value, a promise for a value, or a rejection. The fallback
+ * accepts the operation name, a resolver, and any further arguments that would
+ * have been forwarded to the appropriate method above had a method been
+ * provided with the proper name. The API makes no guarantees about the nature
+ * of the returned object, apart from that it is usable whereever promises are
+ * bought and sold.
+ */
+Q.makePromise = Promise;
+function Promise(descriptor, fallback, inspect) {
+ if (fallback === void 0) {
+ fallback = function (op) {
+ return reject(new Error(
+ "Promise does not support operation: " + op
+ ));
+ };
+ }
+ if (inspect === void 0) {
+ inspect = function () {
+ return {state: "unknown"};
+ };
+ }
+
+ var promise = object_create(Promise.prototype);
+
+ promise.promiseDispatch = function (resolve, op, args) {
+ var result;
+ try {
+ if (descriptor[op]) {
+ result = descriptor[op].apply(promise, args);
+ } else {
+ result = fallback.call(promise, op, args);
+ }
+ } catch (exception) {
+ result = reject(exception);
+ }
+ if (resolve) {
+ resolve(result);
+ }
+ };
+
+ promise.inspect = inspect;
+
+ // XXX deprecated `valueOf` and `exception` support
+ if (inspect) {
+ var inspected = inspect();
+ if (inspected.state === "rejected") {
+ promise.exception = inspected.reason;
+ }
+
+ promise.valueOf = function () {
+ var inspected = inspect();
+ if (inspected.state === "pending" ||
+ inspected.state === "rejected") {
+ return promise;
+ }
+ return inspected.value;
+ };
+ }
+
+ return promise;
+}
+
+Promise.prototype.toString = function () {
+ return "[object Promise]";
+};
+
+Promise.prototype.then = function (fulfilled, rejected, progressed) {
+ var self = this;
+ var deferred = defer();
+ var done = false; // ensure the untrusted promise makes at most a
+ // single call to one of the callbacks
+
+ function _fulfilled(value) {
+ try {
+ return typeof fulfilled === "function" ? fulfilled(value) : value;
+ } catch (exception) {
+ return reject(exception);
+ }
+ }
+
+ function _rejected(exception) {
+ if (typeof rejected === "function") {
+ makeStackTraceLong(exception, self);
+ try {
+ return rejected(exception);
+ } catch (newException) {
+ return reject(newException);
+ }
+ }
+ return reject(exception);
+ }
+
+ function _progressed(value) {
+ return typeof progressed === "function" ? progressed(value) : value;
+ }
+
+ nextTick(function () {
+ self.promiseDispatch(function (value) {
+ if (done) {
+ return;
+ }
+ done = true;
+
+ deferred.resolve(_fulfilled(value));
+ }, "when", [function (exception) {
+ if (done) {
+ return;
+ }
+ done = true;
+
+ deferred.resolve(_rejected(exception));
+ }]);
+ });
+
+ // Progress propagator need to be attached in the current tick.
+ self.promiseDispatch(void 0, "when", [void 0, function (value) {
+ var newValue;
+ var threw = false;
+ try {
+ newValue = _progressed(value);
+ } catch (e) {
+ threw = true;
+ if (Q.onerror) {
+ Q.onerror(e);
+ } else {
+ throw e;
+ }
+ }
+
+ if (!threw) {
+ deferred.notify(newValue);
+ }
+ }]);
+
+ return deferred.promise;
+};
+
+/**
+ * Registers an observer on a promise.
+ *
+ * Guarantees:
+ *
+ * 1. that fulfilled and rejected will be called only once.
+ * 2. that either the fulfilled callback or the rejected callback will be
+ * called, but not both.
+ * 3. that fulfilled and rejected will not be called in this turn.
+ *
+ * @param value promise or immediate reference to observe
+ * @param fulfilled function to be called with the fulfilled value
+ * @param rejected function to be called with the rejection exception
+ * @param progressed function to be called on any progress notifications
+ * @return promise for the return value from the invoked callback
+ */
+Q.when = when;
+function when(value, fulfilled, rejected, progressed) {
+ return Q(value).then(fulfilled, rejected, progressed);
+}
+
+Promise.prototype.thenResolve = function (value) {
+ return this.then(function () { return value; });
+};
+
+Q.thenResolve = function (promise, value) {
+ return Q(promise).thenResolve(value);
+};
+
+Promise.prototype.thenReject = function (reason) {
+ return this.then(function () { throw reason; });
+};
+
+Q.thenReject = function (promise, reason) {
+ return Q(promise).thenReject(reason);
+};
+
+/**
+ * If an object is not a promise, it is as "near" as possible.
+ * If a promise is rejected, it is as "near" as possible too.
+ * If it’s a fulfilled promise, the fulfillment value is nearer.
+ * If it’s a deferred promise and the deferred has been resolved, the
+ * resolution is "nearer".
+ * @param object
+ * @returns most resolved (nearest) form of the object
+ */
+
+// XXX should we re-do this?
+Q.nearer = nearer;
+function nearer(value) {
+ if (isPromise(value)) {
+ var inspected = value.inspect();
+ if (inspected.state === "fulfilled") {
+ return inspected.value;
+ }
+ }
+ return value;
+}
+
+/**
+ * @returns whether the given object is a promise.
+ * Otherwise it is a fulfilled value.
+ */
+Q.isPromise = isPromise;
+function isPromise(object) {
+ return isObject(object) &&
+ typeof object.promiseDispatch === "function" &&
+ typeof object.inspect === "function";
+}
+
+Q.isPromiseAlike = isPromiseAlike;
+function isPromiseAlike(object) {
+ return isObject(object) && typeof object.then === "function";
+}
+
+/**
+ * @returns whether the given object is a pending promise, meaning not
+ * fulfilled or rejected.
+ */
+Q.isPending = isPending;
+function isPending(object) {
+ return isPromise(object) && object.inspect().state === "pending";
+}
+
+Promise.prototype.isPending = function () {
+ return this.inspect().state === "pending";
+};
+
+/**
+ * @returns whether the given object is a value or fulfilled
+ * promise.
+ */
+Q.isFulfilled = isFulfilled;
+function isFulfilled(object) {
+ return !isPromise(object) || object.inspect().state === "fulfilled";
+}
+
+Promise.prototype.isFulfilled = function () {
+ return this.inspect().state === "fulfilled";
+};
+
+/**
+ * @returns whether the given object is a rejected promise.
+ */
+Q.isRejected = isRejected;
+function isRejected(object) {
+ return isPromise(object) && object.inspect().state === "rejected";
+}
+
+Promise.prototype.isRejected = function () {
+ return this.inspect().state === "rejected";
+};
+
+//// BEGIN UNHANDLED REJECTION TRACKING
+
+// This promise library consumes exceptions thrown in handlers so they can be
+// handled by a subsequent promise. The exceptions get added to this array when
+// they are created, and removed when they are handled. Note that in ES6 or
+// shimmed environments, this would naturally be a `Set`.
+var unhandledReasons = [];
+var unhandledRejections = [];
+var trackUnhandledRejections = true;
+
+function resetUnhandledRejections() {
+ unhandledReasons.length = 0;
+ unhandledRejections.length = 0;
+
+ if (!trackUnhandledRejections) {
+ trackUnhandledRejections = true;
+ }
+}
+
+function trackRejection(promise, reason) {
+ if (!trackUnhandledRejections) {
+ return;
+ }
+
+ unhandledRejections.push(promise);
+ if (reason && typeof reason.stack !== "undefined") {
+ unhandledReasons.push(reason.stack);
+ } else {
+ unhandledReasons.push("(no stack) " + reason);
+ }
+}
+
+function untrackRejection(promise) {
+ if (!trackUnhandledRejections) {
+ return;
+ }
+
+ var at = array_indexOf(unhandledRejections, promise);
+ if (at !== -1) {
+ unhandledRejections.splice(at, 1);
+ unhandledReasons.splice(at, 1);
+ }
+}
+
+Q.resetUnhandledRejections = resetUnhandledRejections;
+
+Q.getUnhandledReasons = function () {
+ // Make a copy so that consumers can't interfere with our internal state.
+ return unhandledReasons.slice();
+};
+
+Q.stopUnhandledRejectionTracking = function () {
+ resetUnhandledRejections();
+ trackUnhandledRejections = false;
+};
+
+resetUnhandledRejections();
+
+//// END UNHANDLED REJECTION TRACKING
+
+/**
+ * Constructs a rejected promise.
+ * @param reason value describing the failure
+ */
+Q.reject = reject;
+function reject(reason) {
+ var rejection = Promise({
+ "when": function (rejected) {
+ // note that the error has been handled
+ if (rejected) {
+ untrackRejection(this);
+ }
+ return rejected ? rejected(reason) : this;
+ }
+ }, function fallback() {
+ return this;
+ }, function inspect() {
+ return { state: "rejected", reason: reason };
+ });
+
+ // Note that the reason has not been handled.
+ trackRejection(rejection, reason);
+
+ return rejection;
+}
+
+/**
+ * Constructs a fulfilled promise for an immediate reference.
+ * @param value immediate reference
+ */
+Q.fulfill = fulfill;
+function fulfill(value) {
+ return Promise({
+ "when": function () {
+ return value;
+ },
+ "get": function (name) {
+ return value[name];
+ },
+ "set": function (name, rhs) {
+ value[name] = rhs;
+ },
+ "delete": function (name) {
+ delete value[name];
+ },
+ "post": function (name, args) {
+ // Mark Miller proposes that post with no name should apply a
+ // promised function.
+ if (name === null || name === void 0) {
+ return value.apply(void 0, args);
+ } else {
+ return value[name].apply(value, args);
+ }
+ },
+ "apply": function (thisp, args) {
+ return value.apply(thisp, args);
+ },
+ "keys": function () {
+ return object_keys(value);
+ }
+ }, void 0, function inspect() {
+ return { state: "fulfilled", value: value };
+ });
+}
+
+/**
+ * Converts thenables to Q promises.
+ * @param promise thenable promise
+ * @returns a Q promise
+ */
+function coerce(promise) {
+ var deferred = defer();
+ nextTick(function () {
+ try {
+ promise.then(deferred.resolve, deferred.reject, deferred.notify);
+ } catch (exception) {
+ deferred.reject(exception);
+ }
+ });
+ return deferred.promise;
+}
+
+/**
+ * Annotates an object such that it will never be
+ * transferred away from this process over any promise
+ * communication channel.
+ * @param object
+ * @returns promise a wrapping of that object that
+ * additionally responds to the "isDef" message
+ * without a rejection.
+ */
+Q.master = master;
+function master(object) {
+ return Promise({
+ "isDef": function () {}
+ }, function fallback(op, args) {
+ return dispatch(object, op, args);
+ }, function () {
+ return Q(object).inspect();
+ });
+}
+
+/**
+ * Spreads the values of a promised array of arguments into the
+ * fulfillment callback.
+ * @param fulfilled callback that receives variadic arguments from the
+ * promised array
+ * @param rejected callback that receives the exception if the promise
+ * is rejected.
+ * @returns a promise for the return value or thrown exception of
+ * either callback.
+ */
+Q.spread = spread;
+function spread(value, fulfilled, rejected) {
+ return Q(value).spread(fulfilled, rejected);
+}
+
+Promise.prototype.spread = function (fulfilled, rejected) {
+ return this.all().then(function (array) {
+ return fulfilled.apply(void 0, array);
+ }, rejected);
+};
+
+/**
+ * The async function is a decorator for generator functions, turning
+ * them into asynchronous generators. Although generators are only part
+ * of the newest ECMAScript 6 drafts, this code does not cause syntax
+ * errors in older engines. This code should continue to work and will
+ * in fact improve over time as the language improves.
+ *
+ * ES6 generators are currently part of V8 version 3.19 with the
+ * --harmony-generators runtime flag enabled. SpiderMonkey has had them
+ * for longer, but under an older Python-inspired form. This function
+ * works on both kinds of generators.
+ *
+ * Decorates a generator function such that:
+ * - it may yield promises
+ * - execution will continue when that promise is fulfilled
+ * - the value of the yield expression will be the fulfilled value
+ * - it returns a promise for the return value (when the generator
+ * stops iterating)
+ * - the decorated function returns a promise for the return value
+ * of the generator or the first rejected promise among those
+ * yielded.
+ * - if an error is thrown in the generator, it propagates through
+ * every following yield until it is caught, or until it escapes
+ * the generator function altogether, and is translated into a
+ * rejection for the promise returned by the decorated generator.
+ */
+Q.async = async;
+function async(makeGenerator) {
+ return function () {
+ // when verb is "send", arg is a value
+ // when verb is "throw", arg is an exception
+ function continuer(verb, arg) {
+ var result;
+
+ // Until V8 3.19 / Chromium 29 is released, SpiderMonkey is the only
+ // engine that has a deployed base of browsers that support generators.
+ // However, SM's generators use the Python-inspired semantics of
+ // outdated ES6 drafts. We would like to support ES6, but we'd also
+ // like to make it possible to use generators in deployed browsers, so
+ // we also support Python-style generators. At some point we can remove
+ // this block.
+
+ if (typeof StopIteration === "undefined") {
+ // ES6 Generators
+ try {
+ result = generator[verb](arg);
+ } catch (exception) {
+ return reject(exception);
+ }
+ if (result.done) {
+ return Q(result.value);
+ } else {
+ return when(result.value, callback, errback);
+ }
+ } else {
+ // SpiderMonkey Generators
+ // FIXME: Remove this case when SM does ES6 generators.
+ try {
+ result = generator[verb](arg);
+ } catch (exception) {
+ if (isStopIteration(exception)) {
+ return Q(exception.value);
+ } else {
+ return reject(exception);
+ }
+ }
+ return when(result, callback, errback);
+ }
+ }
+ var generator = makeGenerator.apply(this, arguments);
+ var callback = continuer.bind(continuer, "next");
+ var errback = continuer.bind(continuer, "throw");
+ return callback();
+ };
+}
+
+/**
+ * The spawn function is a small wrapper around async that immediately
+ * calls the generator and also ends the promise chain, so that any
+ * unhandled errors are thrown instead of forwarded to the error
+ * handler. This is useful because it's extremely common to run
+ * generators at the top-level to work with libraries.
+ */
+Q.spawn = spawn;
+function spawn(makeGenerator) {
+ Q.done(Q.async(makeGenerator)());
+}
+
+// FIXME: Remove this interface once ES6 generators are in SpiderMonkey.
+/**
+ * Throws a ReturnValue exception to stop an asynchronous generator.
+ *
+ * This interface is a stop-gap measure to support generator return
+ * values in older Firefox/SpiderMonkey. In browsers that support ES6
+ * generators like Chromium 29, just use "return" in your generator
+ * functions.
+ *
+ * @param value the return value for the surrounding generator
+ * @throws ReturnValue exception with the value.
+ * @example
+ * // ES6 style
+ * Q.async(function* () {
+ * var foo = yield getFooPromise();
+ * var bar = yield getBarPromise();
+ * return foo + bar;
+ * })
+ * // Older SpiderMonkey style
+ * Q.async(function () {
+ * var foo = yield getFooPromise();
+ * var bar = yield getBarPromise();
+ * Q.return(foo + bar);
+ * })
+ */
+Q["return"] = _return;
+function _return(value) {
+ throw new QReturnValue(value);
+}
+
+/**
+ * The promised function decorator ensures that any promise arguments
+ * are settled and passed as values (`this` is also settled and passed
+ * as a value). It will also ensure that the result of a function is
+ * always a promise.
+ *
+ * @example
+ * var add = Q.promised(function (a, b) {
+ * return a + b;
+ * });
+ * add(Q(a), Q(B));
+ *
+ * @param {function} callback The function to decorate
+ * @returns {function} a function that has been decorated.
+ */
+Q.promised = promised;
+function promised(callback) {
+ return function () {
+ return spread([this, all(arguments)], function (self, args) {
+ return callback.apply(self, args);
+ });
+ };
+}
+
+/**
+ * sends a message to a value in a future turn
+ * @param object* the recipient
+ * @param op the name of the message operation, e.g., "when",
+ * @param args further arguments to be forwarded to the operation
+ * @returns result {Promise} a promise for the result of the operation
+ */
+Q.dispatch = dispatch;
+function dispatch(object, op, args) {
+ return Q(object).dispatch(op, args);
+}
+
+Promise.prototype.dispatch = function (op, args) {
+ var self = this;
+ var deferred = defer();
+ nextTick(function () {
+ self.promiseDispatch(deferred.resolve, op, args);
+ });
+ return deferred.promise;
+};
+
+/**
+ * Gets the value of a property in a future turn.
+ * @param object promise or immediate reference for target object
+ * @param name name of property to get
+ * @return promise for the property value
+ */
+Q.get = function (object, key) {
+ return Q(object).dispatch("get", [key]);
+};
+
+Promise.prototype.get = function (key) {
+ return this.dispatch("get", [key]);
+};
+
+/**
+ * Sets the value of a property in a future turn.
+ * @param object promise or immediate reference for object object
+ * @param name name of property to set
+ * @param value new value of property
+ * @return promise for the return value
+ */
+Q.set = function (object, key, value) {
+ return Q(object).dispatch("set", [key, value]);
+};
+
+Promise.prototype.set = function (key, value) {
+ return this.dispatch("set", [key, value]);
+};
+
+/**
+ * Deletes a property in a future turn.
+ * @param object promise or immediate reference for target object
+ * @param name name of property to delete
+ * @return promise for the return value
+ */
+Q.del = // XXX legacy
+Q["delete"] = function (object, key) {
+ return Q(object).dispatch("delete", [key]);
+};
+
+Promise.prototype.del = // XXX legacy
+Promise.prototype["delete"] = function (key) {
+ return this.dispatch("delete", [key]);
+};
+
+/**
+ * Invokes a method in a future turn.
+ * @param object promise or immediate reference for target object
+ * @param name name of method to invoke
+ * @param value a value to post, typically an array of
+ * invocation arguments for promises that
+ * are ultimately backed with `resolve` values,
+ * as opposed to those backed with URLs
+ * wherein the posted value can be any
+ * JSON serializable object.
+ * @return promise for the return value
+ */
+// bound locally because it is used by other methods
+Q.mapply = // XXX As proposed by "Redsandro"
+Q.post = function (object, name, args) {
+ return Q(object).dispatch("post", [name, args]);
+};
+
+Promise.prototype.mapply = // XXX As proposed by "Redsandro"
+Promise.prototype.post = function (name, args) {
+ return this.dispatch("post", [name, args]);
+};
+
+/**
+ * Invokes a method in a future turn.
+ * @param object promise or immediate reference for target object
+ * @param name name of method to invoke
+ * @param ...args array of invocation arguments
+ * @return promise for the return value
+ */
+Q.send = // XXX Mark Miller's proposed parlance
+Q.mcall = // XXX As proposed by "Redsandro"
+Q.invoke = function (object, name /*...args*/) {
+ return Q(object).dispatch("post", [name, array_slice(arguments, 2)]);
+};
+
+Promise.prototype.send = // XXX Mark Miller's proposed parlance
+Promise.prototype.mcall = // XXX As proposed by "Redsandro"
+Promise.prototype.invoke = function (name /*...args*/) {
+ return this.dispatch("post", [name, array_slice(arguments, 1)]);
+};
+
+/**
+ * Applies the promised function in a future turn.
+ * @param object promise or immediate reference for target function
+ * @param args array of application arguments
+ */
+Q.fapply = function (object, args) {
+ return Q(object).dispatch("apply", [void 0, args]);
+};
+
+Promise.prototype.fapply = function (args) {
+ return this.dispatch("apply", [void 0, args]);
+};
+
+/**
+ * Calls the promised function in a future turn.
+ * @param object promise or immediate reference for target function
+ * @param ...args array of application arguments
+ */
+Q["try"] =
+Q.fcall = function (object /* ...args*/) {
+ return Q(object).dispatch("apply", [void 0, array_slice(arguments, 1)]);
+};
+
+Promise.prototype.fcall = function (/*...args*/) {
+ return this.dispatch("apply", [void 0, array_slice(arguments)]);
+};
+
+/**
+ * Binds the promised function, transforming return values into a fulfilled
+ * promise and thrown errors into a rejected one.
+ * @param object promise or immediate reference for target function
+ * @param ...args array of application arguments
+ */
+Q.fbind = function (object /*...args*/) {
+ var promise = Q(object);
+ var args = array_slice(arguments, 1);
+ return function fbound() {
+ return promise.dispatch("apply", [
+ this,
+ args.concat(array_slice(arguments))
+ ]);
+ };
+};
+Promise.prototype.fbind = function (/*...args*/) {
+ var promise = this;
+ var args = array_slice(arguments);
+ return function fbound() {
+ return promise.dispatch("apply", [
+ this,
+ args.concat(array_slice(arguments))
+ ]);
+ };
+};
+
+/**
+ * Requests the names of the owned properties of a promised
+ * object in a future turn.
+ * @param object promise or immediate reference for target object
+ * @return promise for the keys of the eventually settled object
+ */
+Q.keys = function (object) {
+ return Q(object).dispatch("keys", []);
+};
+
+Promise.prototype.keys = function () {
+ return this.dispatch("keys", []);
+};
+
+/**
+ * Turns an array of promises into a promise for an array. If any of
+ * the promises gets rejected, the whole array is rejected immediately.
+ * @param {Array*} an array (or promise for an array) of values (or
+ * promises for values)
+ * @returns a promise for an array of the corresponding values
+ */
+// By Mark Miller
+// http://wiki.ecmascript.org/doku.php?id=strawman:concurrency&rev=1308776521#allfulfilled
+Q.all = all;
+function all(promises) {
+ return when(promises, function (promises) {
+ var countDown = 0;
+ var deferred = defer();
+ array_reduce(promises, function (undefined, promise, index) {
+ var snapshot;
+ if (
+ isPromise(promise) &&
+ (snapshot = promise.inspect()).state === "fulfilled"
+ ) {
+ promises[index] = snapshot.value;
+ } else {
+ ++countDown;
+ when(
+ promise,
+ function (value) {
+ promises[index] = value;
+ if (--countDown === 0) {
+ deferred.resolve(promises);
+ }
+ },
+ deferred.reject,
+ function (progress) {
+ deferred.notify({ index: index, value: progress });
+ }
+ );
+ }
+ }, void 0);
+ if (countDown === 0) {
+ deferred.resolve(promises);
+ }
+ return deferred.promise;
+ });
+}
+
+Promise.prototype.all = function () {
+ return all(this);
+};
+
+/**
+ * Waits for all promises to be settled, either fulfilled or
+ * rejected. This is distinct from `all` since that would stop
+ * waiting at the first rejection. The promise returned by
+ * `allResolved` will never be rejected.
+ * @param promises a promise for an array (or an array) of promises
+ * (or values)
+ * @return a promise for an array of promises
+ */
+Q.allResolved = deprecate(allResolved, "allResolved", "allSettled");
+function allResolved(promises) {
+ return when(promises, function (promises) {
+ promises = array_map(promises, Q);
+ return when(all(array_map(promises, function (promise) {
+ return when(promise, noop, noop);
+ })), function () {
+ return promises;
+ });
+ });
+}
+
+Promise.prototype.allResolved = function () {
+ return allResolved(this);
+};
+
+/**
+ * @see Promise#allSettled
+ */
+Q.allSettled = allSettled;
+function allSettled(promises) {
+ return Q(promises).allSettled();
+}
+
+/**
+ * Turns an array of promises into a promise for an array of their states (as
+ * returned by `inspect`) when they have all settled.
+ * @param {Array[Any*]} values an array (or promise for an array) of values (or
+ * promises for values)
+ * @returns {Array[State]} an array of states for the respective values.
+ */
+Promise.prototype.allSettled = function () {
+ return this.then(function (promises) {
+ return all(array_map(promises, function (promise) {
+ promise = Q(promise);
+ function regardless() {
+ return promise.inspect();
+ }
+ return promise.then(regardless, regardless);
+ }));
+ });
+};
+
+/**
+ * Captures the failure of a promise, giving an oportunity to recover
+ * with a callback. If the given promise is fulfilled, the returned
+ * promise is fulfilled.
+ * @param {Any*} promise for something
+ * @param {Function} callback to fulfill the returned promise if the
+ * given promise is rejected
+ * @returns a promise for the return value of the callback
+ */
+Q.fail = // XXX legacy
+Q["catch"] = function (object, rejected) {
+ return Q(object).then(void 0, rejected);
+};
+
+Promise.prototype.fail = // XXX legacy
+Promise.prototype["catch"] = function (rejected) {
+ return this.then(void 0, rejected);
+};
+
+/**
+ * Attaches a listener that can respond to progress notifications from a
+ * promise's originating deferred. This listener receives the exact arguments
+ * passed to ``deferred.notify``.
+ * @param {Any*} promise for something
+ * @param {Function} callback to receive any progress notifications
+ * @returns the given promise, unchanged
+ */
+Q.progress = progress;
+function progress(object, progressed) {
+ return Q(object).then(void 0, void 0, progressed);
+}
+
+Promise.prototype.progress = function (progressed) {
+ return this.then(void 0, void 0, progressed);
+};
+
+/**
+ * Provides an opportunity to observe the settling of a promise,
+ * regardless of whether the promise is fulfilled or rejected. Forwards
+ * the resolution to the returned promise when the callback is done.
+ * The callback can return a promise to defer completion.
+ * @param {Any*} promise
+ * @param {Function} callback to observe the resolution of the given
+ * promise, takes no arguments.
+ * @returns a promise for the resolution of the given promise when
+ * ``fin`` is done.
+ */
+Q.fin = // XXX legacy
+Q["finally"] = function (object, callback) {
+ return Q(object)["finally"](callback);
+};
+
+Promise.prototype.fin = // XXX legacy
+Promise.prototype["finally"] = function (callback) {
+ callback = Q(callback);
+ return this.then(function (value) {
+ return callback.fcall().then(function () {
+ return value;
+ });
+ }, function (reason) {
+ // TODO attempt to recycle the rejection with "this".
+ return callback.fcall().then(function () {
+ throw reason;
+ });
+ });
+};
+
+/**
+ * Terminates a chain of promises, forcing rejections to be
+ * thrown as exceptions.
+ * @param {Any*} promise at the end of a chain of promises
+ * @returns nothing
+ */
+Q.done = function (object, fulfilled, rejected, progress) {
+ return Q(object).done(fulfilled, rejected, progress);
+};
+
+Promise.prototype.done = function (fulfilled, rejected, progress) {
+ var onUnhandledError = function (error) {
+ // forward to a future turn so that ``when``
+ // does not catch it and turn it into a rejection.
+ nextTick(function () {
+ makeStackTraceLong(error, promise);
+ if (Q.onerror) {
+ Q.onerror(error);
+ } else {
+ throw error;
+ }
+ });
+ };
+
+ // Avoid unnecessary `nextTick`ing via an unnecessary `when`.
+ var promise = fulfilled || rejected || progress ?
+ this.then(fulfilled, rejected, progress) :
+ this;
+
+ if (typeof process === "object" && process && process.domain) {
+ onUnhandledError = process.domain.bind(onUnhandledError);
+ }
+
+ promise.then(void 0, onUnhandledError);
+};
+
+/**
+ * Causes a promise to be rejected if it does not get fulfilled before
+ * some milliseconds time out.
+ * @param {Any*} promise
+ * @param {Number} milliseconds timeout
+ * @param {Any*} custom error message or Error object (optional)
+ * @returns a promise for the resolution of the given promise if it is
+ * fulfilled before the timeout, otherwise rejected.
+ */
+Q.timeout = function (object, ms, error) {
+ return Q(object).timeout(ms, error);
+};
+
+Promise.prototype.timeout = function (ms, error) {
+ var deferred = defer();
+ var timeoutId = setTimeout(function () {
+ if (!error || "string" === typeof error) {
+ error = new Error(error || "Timed out after " + ms + " ms");
+ error.code = "ETIMEDOUT";
+ }
+ deferred.reject(error);
+ }, ms);
+
+ this.then(function (value) {
+ clearTimeout(timeoutId);
+ deferred.resolve(value);
+ }, function (exception) {
+ clearTimeout(timeoutId);
+ deferred.reject(exception);
+ }, deferred.notify);
+
+ return deferred.promise;
+};
+
+/**
+ * Returns a promise for the given value (or promised value), some
+ * milliseconds after it resolved. Passes rejections immediately.
+ * @param {Any*} promise
+ * @param {Number} milliseconds
+ * @returns a promise for the resolution of the given promise after milliseconds
+ * time has elapsed since the resolution of the given promise.
+ * If the given promise rejects, that is passed immediately.
+ */
+Q.delay = function (object, timeout) {
+ if (timeout === void 0) {
+ timeout = object;
+ object = void 0;
+ }
+ return Q(object).delay(timeout);
+};
+
+Promise.prototype.delay = function (timeout) {
+ return this.then(function (value) {
+ var deferred = defer();
+ setTimeout(function () {
+ deferred.resolve(value);
+ }, timeout);
+ return deferred.promise;
+ });
+};
+
+/**
+ * Passes a continuation to a Node function, which is called with the given
+ * arguments provided as an array, and returns a promise.
+ *
+ * Q.nfapply(FS.readFile, [__filename])
+ * .then(function (content) {
+ * })
+ *
+ */
+Q.nfapply = function (callback, args) {
+ return Q(callback).nfapply(args);
+};
+
+Promise.prototype.nfapply = function (args) {
+ var deferred = defer();
+ var nodeArgs = array_slice(args);
+ nodeArgs.push(deferred.makeNodeResolver());
+ this.fapply(nodeArgs).fail(deferred.reject);
+ return deferred.promise;
+};
+
+/**
+ * Passes a continuation to a Node function, which is called with the given
+ * arguments provided individually, and returns a promise.
+ * @example
+ * Q.nfcall(FS.readFile, __filename)
+ * .then(function (content) {
+ * })
+ *
+ */
+Q.nfcall = function (callback /*...args*/) {
+ var args = array_slice(arguments, 1);
+ return Q(callback).nfapply(args);
+};
+
+Promise.prototype.nfcall = function (/*...args*/) {
+ var nodeArgs = array_slice(arguments);
+ var deferred = defer();
+ nodeArgs.push(deferred.makeNodeResolver());
+ this.fapply(nodeArgs).fail(deferred.reject);
+ return deferred.promise;
+};
+
+/**
+ * Wraps a NodeJS continuation passing function and returns an equivalent
+ * version that returns a promise.
+ * @example
+ * Q.nfbind(FS.readFile, __filename)("utf-8")
+ * .then(console.log)
+ * .done()
+ */
+Q.nfbind =
+Q.denodeify = function (callback /*...args*/) {
+ var baseArgs = array_slice(arguments, 1);
+ return function () {
+ var nodeArgs = baseArgs.concat(array_slice(arguments));
+ var deferred = defer();
+ nodeArgs.push(deferred.makeNodeResolver());
+ Q(callback).fapply(nodeArgs).fail(deferred.reject);
+ return deferred.promise;
+ };
+};
+
+Promise.prototype.nfbind =
+Promise.prototype.denodeify = function (/*...args*/) {
+ var args = array_slice(arguments);
+ args.unshift(this);
+ return Q.denodeify.apply(void 0, args);
+};
+
+Q.nbind = function (callback, thisp /*...args*/) {
+ var baseArgs = array_slice(arguments, 2);
+ return function () {
+ var nodeArgs = baseArgs.concat(array_slice(arguments));
+ var deferred = defer();
+ nodeArgs.push(deferred.makeNodeResolver());
+ function bound() {
+ return callback.apply(thisp, arguments);
+ }
+ Q(bound).fapply(nodeArgs).fail(deferred.reject);
+ return deferred.promise;
+ };
+};
+
+Promise.prototype.nbind = function (/*thisp, ...args*/) {
+ var args = array_slice(arguments, 0);
+ args.unshift(this);
+ return Q.nbind.apply(void 0, args);
+};
+
+/**
+ * Calls a method of a Node-style object that accepts a Node-style
+ * callback with a given array of arguments, plus a provided callback.
+ * @param object an object that has the named method
+ * @param {String} name name of the method of object
+ * @param {Array} args arguments to pass to the method; the callback
+ * will be provided by Q and appended to these arguments.
+ * @returns a promise for the value or error
+ */
+Q.nmapply = // XXX As proposed by "Redsandro"
+Q.npost = function (object, name, args) {
+ return Q(object).npost(name, args);
+};
+
+Promise.prototype.nmapply = // XXX As proposed by "Redsandro"
+Promise.prototype.npost = function (name, args) {
+ var nodeArgs = array_slice(args || []);
+ var deferred = defer();
+ nodeArgs.push(deferred.makeNodeResolver());
+ this.dispatch("post", [name, nodeArgs]).fail(deferred.reject);
+ return deferred.promise;
+};
+
+/**
+ * Calls a method of a Node-style object that accepts a Node-style
+ * callback, forwarding the given variadic arguments, plus a provided
+ * callback argument.
+ * @param object an object that has the named method
+ * @param {String} name name of the method of object
+ * @param ...args arguments to pass to the method; the callback will
+ * be provided by Q and appended to these arguments.
+ * @returns a promise for the value or error
+ */
+Q.nsend = // XXX Based on Mark Miller's proposed "send"
+Q.nmcall = // XXX Based on "Redsandro's" proposal
+Q.ninvoke = function (object, name /*...args*/) {
+ var nodeArgs = array_slice(arguments, 2);
+ var deferred = defer();
+ nodeArgs.push(deferred.makeNodeResolver());
+ Q(object).dispatch("post", [name, nodeArgs]).fail(deferred.reject);
+ return deferred.promise;
+};
+
+Promise.prototype.nsend = // XXX Based on Mark Miller's proposed "send"
+Promise.prototype.nmcall = // XXX Based on "Redsandro's" proposal
+Promise.prototype.ninvoke = function (name /*...args*/) {
+ var nodeArgs = array_slice(arguments, 1);
+ var deferred = defer();
+ nodeArgs.push(deferred.makeNodeResolver());
+ this.dispatch("post", [name, nodeArgs]).fail(deferred.reject);
+ return deferred.promise;
+};
+
+/**
+ * If a function would like to support both Node continuation-passing-style and
+ * promise-returning-style, it can end its internal promise chain with
+ * `nodeify(nodeback)`, forwarding the optional nodeback argument. If the user
+ * elects to use a nodeback, the result will be sent there. If they do not
+ * pass a nodeback, they will receive the result promise.
+ * @param object a result (or a promise for a result)
+ * @param {Function} nodeback a Node.js-style callback
+ * @returns either the promise or nothing
+ */
+Q.nodeify = nodeify;
+function nodeify(object, nodeback) {
+ return Q(object).nodeify(nodeback);
+}
+
+Promise.prototype.nodeify = function (nodeback) {
+ if (nodeback) {
+ this.then(function (value) {
+ nextTick(function () {
+ nodeback(null, value);
+ });
+ }, function (error) {
+ nextTick(function () {
+ nodeback(error);
+ });
+ });
+ } else {
+ return this;
+ }
+};
+
+// All code before this point will be filtered from stack traces.
+var qEndingLine = captureLine();
+
+return Q;
+
+});
+
diff --git a/mist/assets/ext/qml_messaging.js b/mist/assets/ext/qml_messaging.js
new file mode 100644
index 000000000..8222c848d
--- /dev/null
+++ b/mist/assets/ext/qml_messaging.js
@@ -0,0 +1,13 @@
+function HandleMessage(data) {
+ var message;
+ try { message = JSON.parse(data) } catch(e) {};
+
+ if(message) {
+ switch(message.type) {
+ case "coinbase":
+ return eth.coinBase();
+ case "block":
+ return eth.blockByNumber(0);
+ }
+ }
+}
diff --git a/ethereal/assets/ext/string.js b/mist/assets/ext/string.js
index 2473b5c36..2473b5c36 100644
--- a/ethereal/assets/ext/string.js
+++ b/mist/assets/ext/string.js
diff --git a/ethereal/assets/ext/test.html b/mist/assets/ext/test.html
index 4bac7d36f..4bac7d36f 100644
--- a/ethereal/assets/ext/test.html
+++ b/mist/assets/ext/test.html
diff --git a/ethereal/assets/facet.png b/mist/assets/facet.png
index 49a266e96..49a266e96 100644
--- a/ethereal/assets/facet.png
+++ b/mist/assets/facet.png
Binary files differ
diff --git a/ethereal/assets/heart.png b/mist/assets/heart.png
index 3c874ab7f..3c874ab7f 100644
--- a/ethereal/assets/heart.png
+++ b/mist/assets/heart.png
Binary files differ
diff --git a/mist/assets/icecream.png b/mist/assets/icecream.png
new file mode 100644
index 000000000..2438ca845
--- /dev/null
+++ b/mist/assets/icecream.png
Binary files differ
diff --git a/ethereal/assets/muted/codemirror.css b/mist/assets/muted/codemirror.css
index 098a317a2..098a317a2 100644
--- a/ethereal/assets/muted/codemirror.css
+++ b/mist/assets/muted/codemirror.css
diff --git a/ethereal/assets/muted/debugger.html b/mist/assets/muted/debugger.html
index b7552f030..b7552f030 100644
--- a/ethereal/assets/muted/debugger.html
+++ b/mist/assets/muted/debugger.html
diff --git a/ethereal/assets/muted/eclipse.css b/mist/assets/muted/eclipse.css
index 317218e3d..317218e3d 100644
--- a/ethereal/assets/muted/eclipse.css
+++ b/mist/assets/muted/eclipse.css
diff --git a/ethereal/assets/muted/index.html b/mist/assets/muted/index.html
index 14949b5ac..14949b5ac 100644
--- a/ethereal/assets/muted/index.html
+++ b/mist/assets/muted/index.html
diff --git a/ethereal/assets/muted/lib/codemirror.js b/mist/assets/muted/lib/codemirror.js
index 0ab217711..0ab217711 100644
--- a/ethereal/assets/muted/lib/codemirror.js
+++ b/mist/assets/muted/lib/codemirror.js
diff --git a/ethereal/assets/muted/lib/go.js b/mist/assets/muted/lib/go.js
index 9f1c1c4ab..9f1c1c4ab 100644
--- a/ethereal/assets/muted/lib/go.js
+++ b/mist/assets/muted/lib/go.js
diff --git a/ethereal/assets/muted/lib/matchbrackets.js b/mist/assets/muted/lib/matchbrackets.js
index dcdde81df..dcdde81df 100644
--- a/ethereal/assets/muted/lib/matchbrackets.js
+++ b/mist/assets/muted/lib/matchbrackets.js
diff --git a/ethereal/assets/muted/muted.js b/mist/assets/muted/muted.js
index 72e858d7a..72e858d7a 100644
--- a/ethereal/assets/muted/muted.js
+++ b/mist/assets/muted/muted.js
diff --git a/ethereal/assets/net.png b/mist/assets/net.png
index 65a20ea00..65a20ea00 100644
--- a/ethereal/assets/net.png
+++ b/mist/assets/net.png
Binary files differ
diff --git a/ethereal/assets/network.png b/mist/assets/network.png
index 0a9ffe2ec..0a9ffe2ec 100644
--- a/ethereal/assets/network.png
+++ b/mist/assets/network.png
Binary files differ
diff --git a/ethereal/assets/new.png b/mist/assets/new.png
index e80096748..e80096748 100644
--- a/ethereal/assets/new.png
+++ b/mist/assets/new.png
Binary files differ
diff --git a/ethereal/assets/pick.png b/mist/assets/pick.png
index 2f5a261c2..2f5a261c2 100644
--- a/ethereal/assets/pick.png
+++ b/mist/assets/pick.png
Binary files differ
diff --git a/ethereal/assets/qml/QmlApp.qml b/mist/assets/qml/QmlApp.qml
index f5c503f4c..f5c503f4c 100644
--- a/ethereal/assets/qml/QmlApp.qml
+++ b/mist/assets/qml/QmlApp.qml
diff --git a/ethereal/assets/qml/first_run.qml b/mist/assets/qml/first_run.qml
index 0b1dac4c6..0b1dac4c6 100644
--- a/ethereal/assets/qml/first_run.qml
+++ b/mist/assets/qml/first_run.qml
diff --git a/ethereal/assets/qml/muted.qml b/mist/assets/qml/muted.qml
index fac8267c4..fac8267c4 100644
--- a/ethereal/assets/qml/muted.qml
+++ b/mist/assets/qml/muted.qml
diff --git a/ethereal/assets/qml/test_app.qml b/mist/assets/qml/test_app.qml
index c69587839..c69587839 100644
--- a/ethereal/assets/qml/test_app.qml
+++ b/mist/assets/qml/test_app.qml
diff --git a/ethereal/assets/qml/transactions.qml b/mist/assets/qml/transactions.qml
index e9a035a85..e9a035a85 100644
--- a/ethereal/assets/qml/transactions.qml
+++ b/mist/assets/qml/transactions.qml
diff --git a/ethereal/assets/qml/views/chain.qml b/mist/assets/qml/views/chain.qml
index 9eaa49db1..130ff8bb9 100644
--- a/ethereal/assets/qml/views/chain.qml
+++ b/mist/assets/qml/views/chain.qml
@@ -10,7 +10,6 @@ Rectangle {
id: root
property var title: "Network"
property var iconSource: "../net.png"
- property var secondary: "Hi"
property var menuItem
objectName: "chainView"
@@ -99,20 +98,23 @@ Rectangle {
function addBlock(block, initial) {
- var txs = JSON.parse(block.transactions);
- var amount = 0
if(initial == undefined){
initial = false
}
+ /*
+ var txs = JSON.parse(block.transactions);
if(txs != null){
amount = txs.length
}
+ */
+ var txs = block.transactions;
+ var amount = block.transactions.length;
if(initial){
- blockModel.append({number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
+ blockModel.append({size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
} else {
- blockModel.insert(0, {number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
+ blockModel.insert(0, {size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
}
//root.secondary.text = "#" + block.number;
@@ -137,7 +139,7 @@ Rectangle {
anchors.top: parent.top
anchors.left: parent.left
Text { text: '<h3>Block details</h3>'; color: "#F2F2F2"}
- Text { text: '<b>Block number:</b> ' + number; color: "#F2F2F2"}
+ Text { text: '<b>Block number:</b> ' + number + " (Size: " + size + ")"; color: "#F2F2F2"}
Text { text: '<b>Hash:</b> ' + hash; color: "#F2F2F2"}
Text { text: '<b>Coinbase:</b> &lt;' + name + '&gt; ' + coinbase; color: "#F2F2F2"}
Text { text: '<b>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
@@ -242,11 +244,11 @@ Rectangle {
singleBlock.set(0,block)
popup.height = 300
transactionModel.clear()
- if(block.txs != undefined){
- for(var i = 0; i < block.txs.count; ++i) {
+ if(block.txs !== undefined){
+ for(var i = 0; i < block.txs.length; i++) {
transactionModel.insert(0, block.txs.get(i))
}
- if(block.txs.get(0).data){
+ if(block.txs.length > 0 && block.txs.get(0).data){
popup.showContractData(block.txs.get(0))
}
}
diff --git a/ethereal/assets/qml/views/history.qml b/mist/assets/qml/views/history.qml
index 9eee883e3..9eee883e3 100644
--- a/ethereal/assets/qml/views/history.qml
+++ b/mist/assets/qml/views/history.qml
diff --git a/ethereal/assets/qml/views/info.qml b/mist/assets/qml/views/info.qml
index ca6ca077e..8a1d4d84a 100644
--- a/ethereal/assets/qml/views/info.qml
+++ b/mist/assets/qml/views/info.qml
@@ -44,59 +44,103 @@ Rectangle {
gui.setCustomIdentifier(text)
}
}
- }
- property var addressModel: ListModel {
- id: addressModel
+ TextArea {
+ objectName: "statsPane"
+ width: parent.width
+ height: 200
+ selectByMouse: true
+ readOnly: true
+ font.family: "Courier"
+ }
}
- TableView {
- id: addressView
+
+ RowLayout {
+ id: logLayout
width: parent.width
height: 200
- anchors.bottom: logLayout.top
- TableViewColumn{ role: "name"; title: "name" }
- TableViewColumn{ role: "address"; title: "address"; width: 300}
-
- model: addressModel
- itemDelegate: Item {
- Text {
- anchors {
- left: parent.left
- right: parent.right
- leftMargin: 10
- verticalCenter: parent.verticalCenter
- }
- color: styleData.textColor
- elide: styleData.elideMode
- text: styleData.value
- font.pixelSize: 11
- MouseArea {
- acceptedButtons: Qt.LeftButton | Qt.RightButton
- propagateComposedEvents: true
- anchors.fill: parent
- onClicked: {
- addressView.selection.clear()
- addressView.selection.select(styleData.row)
-
- if(mouse.button == Qt.RightButton) {
- contextMenu.row = styleData.row;
- contextMenu.popup()
+ anchors.bottom: parent.bottom
+
+ TableView {
+ id: addressView
+ width: parent.width
+ height: 200
+ anchors {
+ left: parent.left
+ right: logLevelSlider.left
+ bottom: parent.bottom
+ top: parent.top
+ }
+ TableViewColumn{ role: "name"; title: "name" }
+ TableViewColumn{ role: "address"; title: "address"; width: 300}
+
+ property var addressModel: ListModel {
+ id: addressModel
+ }
+
+ model: addressModel
+ itemDelegate: Item {
+ Text {
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 10
+ verticalCenter: parent.verticalCenter
+ }
+ color: styleData.textColor
+ elide: styleData.elideMode
+ text: styleData.value
+ font.pixelSize: 11
+ MouseArea {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ propagateComposedEvents: true
+ anchors.fill: parent
+ onClicked: {
+ addressView.selection.clear()
+ addressView.selection.select(styleData.row)
+
+ if(mouse.button == Qt.RightButton) {
+ contextMenu.row = styleData.row;
+ contextMenu.popup()
+ }
}
}
}
}
+ Menu {
+ id: contextMenu
+ property var row;
+
+ MenuItem {
+ text: "Copy"
+ onTriggered: {
+ copyToClipboard(addressModel.get(this.row).address)
+ }
+ }
+ }
}
- Menu {
- id: contextMenu
- property var row;
+ Slider {
+ id: logLevelSlider
+ value: gui.getLogLevelInt()
+ anchors {
+ right: parent.right
+ top: parent.top
+ bottom: parent.bottom
- MenuItem {
- text: "Copy"
- onTriggered: {
- copyToClipboard(addressModel.get(this.row).address)
- }
+ rightMargin: 5
+ leftMargin: 5
+ topMargin: 5
+ bottomMargin: 5
+ }
+
+ orientation: Qt.Vertical
+ maximumValue: 5
+ stepSize: 1
+
+ onValueChanged: {
+ gui.setLogLevel(value)
}
}
}
@@ -104,6 +148,8 @@ Rectangle {
property var logModel: ListModel {
id: logModel
}
+
+ /*
RowLayout {
id: logLayout
width: parent.width
@@ -147,6 +193,7 @@ Rectangle {
}
}
}
+ */
function addDebugMessage(message){
debuggerLog.append({value: message})
diff --git a/ethereal/assets/qml/views/javascript.qml b/mist/assets/qml/views/javascript.qml
index ea05c4148..ea05c4148 100644
--- a/ethereal/assets/qml/views/javascript.qml
+++ b/mist/assets/qml/views/javascript.qml
diff --git a/mist/assets/qml/views/jeffcoin/jeff.png b/mist/assets/qml/views/jeffcoin/jeff.png
new file mode 100644
index 000000000..2b9c6651a
--- /dev/null
+++ b/mist/assets/qml/views/jeffcoin/jeff.png
Binary files differ
diff --git a/mist/assets/qml/views/jeffcoin/jeffcoin.qml b/mist/assets/qml/views/jeffcoin/jeffcoin.qml
new file mode 100644
index 000000000..6a57791a7
--- /dev/null
+++ b/mist/assets/qml/views/jeffcoin/jeffcoin.qml
@@ -0,0 +1,184 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import QtQuick.Dialogs 1.0;
+import QtQuick.Window 2.1;
+import QtQuick.Controls.Styles 1.1
+
+Rectangle {
+ id: root
+ property var title: "JeffCoin"
+ property var iconSource: "./views/jeffcoin/jeff.png"
+ property var menuItem
+ property var filter
+ property var address: "fc0a9436890478bb9b1c6ed7455c2535366f4a99"
+
+ function insertTx(message, blockNumber) {
+ if(!message) return;
+
+ var from = message.from
+ var to = message.input.substr(24, 40)
+ var value = eth.fromNumber(message.input.substr(64, 64))
+
+ var me = eth.key().address;
+ if((to == me|| from == me) && message.input.length == 128) {
+ txModel.insert(0, {confirmations: blockNumber - message.number, from: from, to: to, value: value})
+ }
+ }
+
+ function setBalance() {
+ var jeffCoinAmount = eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
+ menuItem.secondaryTitle = jeffCoinAmount
+
+ balance.text = "<b>Balance</b>: " + jeffCoinAmount;
+ }
+
+ function onReady() {
+ setBalance()
+
+ filter = new ethx.watch({latest: -1, to: address})
+ filter.changed(function(messages) {
+ setBalance()
+
+ var blockNumber = eth.block(-1).number;
+ for(var i = 0; i < messages.length; i++) {
+ insertTx(messages.get(i), blockNumber);
+ }
+ });
+
+ var blockNumber = eth.block(-1).number;
+ var messages = filter.messages()
+ for(var i = messages.length-1; i >= 0; i--) {
+ var message = messages.get(i)
+
+ insertTx(message, blockNumber)
+ }
+
+ var chainChanged = ethx.watch("chain")
+ chainChanged.changed(function() {
+ for(var i = 0; i < txModel.count; i++) {
+ var entry = txModel.get(i);
+ entry.confirmations++;
+ }
+ });
+ }
+
+ function onDestroy() {
+ filter.uninstall()
+ }
+
+ ColumnLayout {
+ spacing: 10
+ y: 40
+ anchors.fill: parent
+
+ Text {
+ id: balance
+ text: "<b>Balance</b>: " + eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
+ font.pixelSize: 24
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top
+ topMargin: 20
+ }
+ }
+
+ Rectangle {
+ id: newTxPane
+ color: "#ececec"
+ border.color: "#cccccc"
+ border.width: 1
+ anchors {
+ top: balance.bottom
+ topMargin: 10
+ left: parent.left
+ leftMargin: 5
+ right: parent.right
+ rightMargin: 5
+ }
+ height: 100
+
+ RowLayout {
+ id: amountFields
+ spacing: 10
+ anchors {
+ top: parent.top
+ topMargin: 20
+ left: parent.left
+ leftMargin: 20
+ }
+
+ Text {
+ text: "JΞF "
+ }
+
+ // There's something off with the row layout where textfields won't listen to the width setting
+ Rectangle {
+ width: 50
+ height: 20
+ TextField {
+ id: txValue
+ width: parent.width
+ placeholderText: "0.00"
+ }
+ }
+ }
+
+ RowLayout {
+ id: toFields
+ spacing: 10
+ anchors {
+ top: amountFields.bottom
+ topMargin: 5
+ left: parent.left
+ leftMargin: 20
+ }
+
+ Text {
+ text: "To"
+ }
+
+ Rectangle {
+ width: 200
+ height: 20
+ TextField {
+ id: txTo
+ width: parent.width
+ placeholderText: "Address or name"
+ }
+ }
+
+ Button {
+ text: "Send"
+ onClicked: {
+ eth.transact({from: eth.key().privateKey, to:address, gas: "9000", gasPrice: "100000000000", data: ["0x"+txTo.text, txValue.text]})
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: newTxPane.bottom
+ topMargin: 10
+ bottom: parent.bottom
+ }
+ TableView {
+ id: txTableView
+ anchors.fill : parent
+ TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
+ TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
+ TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
+ TableViewColumn{ role: "confirmations" ; title: "Confirmations" ; width: 100 }
+
+ model: ListModel {
+ id: txModel
+ Component.onCompleted: {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ethereal/assets/qml/views/pending_tx.qml b/mist/assets/qml/views/pending_tx.qml
index abfa25790..abfa25790 100644
--- a/ethereal/assets/qml/views/pending_tx.qml
+++ b/mist/assets/qml/views/pending_tx.qml
diff --git a/ethereal/assets/qml/views/transaction.qml b/mist/assets/qml/views/transaction.qml
index fb8ba8a6d..7d689733f 100644
--- a/ethereal/assets/qml/views/transaction.qml
+++ b/mist/assets/qml/views/transaction.qml
@@ -180,6 +180,8 @@ Rectangle {
txResult.text = "Your transaction has been submitted:\n"
txOutput.text = res[0].address
mainContractColumn.state = "DONE"
+
+ console.log(res)
}
}
}
diff --git a/ethereal/assets/qml/views/wallet.qml b/mist/assets/qml/views/wallet.qml
index 5e10a7022..fbe1dfd0e 100644
--- a/ethereal/assets/qml/views/wallet.qml
+++ b/mist/assets/qml/views/wallet.qml
@@ -9,7 +9,7 @@ import Ethereum 1.0
Rectangle {
id: root
property var title: "Wallet"
- property var iconSource: "../wallet.png"
+ property var iconSource: "../facet.png"
property var menuItem
objectName: "walletView"
@@ -151,10 +151,16 @@ Rectangle {
model: ListModel {
id: txModel
Component.onCompleted: {
- var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"}))
+ var filter = ethx.watch({latest: -1, from: eth.key().address});
+ filter.changed(addTxs)
+
+ addTxs(filter.messages())
+ }
+
+ function addTxs(messages) {
for(var i = 0; i < messages.length; i++) {
- var message = messages[i];
- this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)})
+ var message = messages.get(i);
+ txModel.insert(0, {num: txModel.count, from: message.from, to: message.to, value: eth.numberToHuman(message.value)})
}
}
}
diff --git a/mist/assets/qml/wallet.qml b/mist/assets/qml/wallet.qml
new file mode 100644
index 000000000..24191eae8
--- /dev/null
+++ b/mist/assets/qml/wallet.qml
@@ -0,0 +1,894 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import QtQuick.Dialogs 1.0;
+import QtQuick.Window 2.1;
+import QtQuick.Controls.Styles 1.1
+import Ethereum 1.0
+
+import "../ext/filter.js" as Eth
+import "../ext/http.js" as Http
+
+ApplicationWindow {
+ id: root
+
+ property alias miningButtonText: miningButton.text
+ property var ethx : Eth.ethx
+ property var web
+
+ width: 1200
+ height: 820
+ minimumHeight: 300
+
+ title: "Mist"
+
+ // This signal is used by the filter API. The filter API connects using this signal handler from
+ // the different QML files and plugins.
+ signal messages(var messages, int id);
+ function invokeFilterCallback(data, receiverSeed) {
+ //var messages = JSON.parse(data)
+ // Signal handler
+ messages(data, receiverSeed);
+ root.web.messages(data, receiverSeed);
+ }
+
+ TextField {
+ id: copyElementHax
+ visible: false
+ }
+
+ function copyToClipboard(text) {
+ copyElementHax.text = text
+ copyElementHax.selectAll()
+ copyElementHax.copy()
+ }
+
+ // Takes care of loading all default plugins
+ Component.onCompleted: {
+ var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
+ var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true});
+ root.web = browser.view;
+
+ addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
+ addPlugin("./views/javascript.qml", {noAdd: true, close: false, section: "legacy"});
+
+ addPlugin("./views/jeffcoin/jeffcoin.qml", {noAdd: true, close: false, section: "apps"})
+
+ mainSplit.setView(wallet.view, wallet.menuItem);
+
+ // Call the ready handler
+ gui.done();
+ }
+
+ function addViews(view, path, options) {
+ var views = mainSplit.addComponent(view, options)
+ views.menuItem.path = path
+
+ mainSplit.views.push(views);
+
+ if(!options.noAdd) {
+ gui.addPlugin(path)
+ }
+
+ return views
+ }
+
+ function addPlugin(path, options) {
+ try {
+ if(typeof(path) === "string" && /^https?/.test(path)) {
+ console.log('load http')
+ Http.request(path, function(o) {
+ if(o.status === 200) {
+ var view = Qt.createQmlObject(o.responseText, mainView, path)
+ addViews(view, path, options)
+ }
+ })
+
+ return
+ }
+
+ var component = Qt.createComponent(path);
+ if(component.status != Component.Ready) {
+ if(component.status == Component.Error) {
+ ethx.note("error: ", component.errorString());
+ }
+
+ return
+ }
+
+ var view = mainView.createView(component, options)
+ var views = addViews(view, path, options)
+
+ return views
+ } catch(e) {
+ ethx.note(e)
+ }
+ }
+
+ menuBar: MenuBar {
+ Menu {
+ title: "File"
+ MenuItem {
+ text: "Import App"
+ shortcut: "Ctrl+o"
+ onTriggered: {
+ generalFileDialog.show(true, importApp)
+ }
+ }
+
+ /*
+ MenuItem {
+ text: "Browser"
+ onTriggered: eth.openBrowser()
+ }
+ */
+
+ MenuItem {
+ text: "Add plugin"
+ onTriggered: {
+ generalFileDialog.show(true, function(path) {
+ addPlugin(path, {close: true, section: "apps"})
+ })
+ }
+ }
+
+ MenuSeparator {}
+
+ MenuItem {
+ text: "Import key"
+ shortcut: "Ctrl+i"
+ onTriggered: {
+ generalFileDialog.show(true, function(path) {
+ gui.importKey(path)
+ })
+ }
+ }
+
+ MenuItem {
+ text: "Export keys"
+ shortcut: "Ctrl+e"
+ onTriggered: {
+ generalFileDialog.show(false, function(path) {
+ })
+ }
+ }
+
+ }
+
+ Menu {
+ title: "Developer"
+ MenuItem {
+ iconSource: "../icecream.png"
+ text: "Debugger"
+ shortcut: "Ctrl+d"
+ onTriggered: eth.startDebugger()
+ }
+
+ MenuItem {
+ text: "Import Tx"
+ onTriggered: {
+ txImportDialog.visible = true
+ }
+ }
+
+ MenuItem {
+ text: "Run JS file"
+ onTriggered: {
+ generalFileDialog.show(true, function(path) {
+ eth.evalJavascriptFile(path)
+ })
+ }
+ }
+
+ MenuItem {
+ text: "Dump state"
+ onTriggered: {
+ generalFileDialog.show(false, function(path) {
+ // Empty hash for latest
+ gui.dumpState("", path)
+ })
+ }
+ }
+
+ MenuSeparator {}
+
+ MenuItem {
+ id: miningSpeed
+ text: "Mining: Turbo"
+ onTriggered: {
+ gui.toggleTurboMining()
+ if(text == "Mining: Turbo") {
+ text = "Mining: Normal";
+ } else {
+ text = "Mining: Turbo";
+ }
+ }
+ }
+ }
+
+ Menu {
+ title: "Network"
+ MenuItem {
+ text: "Add Peer"
+ shortcut: "Ctrl+p"
+ onTriggered: {
+ addPeerWin.visible = true
+ }
+ }
+ MenuItem {
+ text: "Show Peers"
+ shortcut: "Ctrl+e"
+ onTriggered: {
+ peerWindow.visible = true
+ }
+ }
+ }
+
+ Menu {
+ title: "Help"
+ MenuItem {
+ text: "About"
+ onTriggered: {
+ aboutWin.visible = true
+ }
+ }
+ }
+
+ Menu {
+ title: "GLOBAL SHORTCUTS"
+ visible: false
+ MenuItem {
+ visible: false
+ shortcut: "Ctrl+l"
+ onTriggered: {
+ url.focus = true
+ }
+ }
+ }
+ }
+
+ statusBar: StatusBar {
+ height: 32
+ RowLayout {
+ Button {
+ id: miningButton
+ text: "Start Mining"
+ onClicked: {
+ gui.toggleMining()
+ }
+ }
+
+ RowLayout {
+ Label {
+ id: walletValueLabel
+
+ font.pixelSize: 10
+ styleColor: "#797979"
+ }
+ }
+ }
+
+ Label {
+ y: 6
+ objectName: "miningLabel"
+ visible: true
+ font.pixelSize: 10
+ anchors.right: lastBlockLabel.left
+ anchors.rightMargin: 5
+ }
+
+ Label {
+ y: 6
+ id: lastBlockLabel
+ objectName: "lastBlockLabel"
+ visible: true
+ text: ""
+ font.pixelSize: 10
+ anchors.right: peerGroup.left
+ anchors.rightMargin: 5
+ }
+
+ ProgressBar {
+ id: syncProgressIndicator
+ visible: false
+ objectName: "syncProgressIndicator"
+ y: 3
+ width: 140
+ indeterminate: true
+ anchors.right: peerGroup.left
+ anchors.rightMargin: 5
+ }
+
+ RowLayout {
+ id: peerGroup
+ y: 7
+ anchors.right: parent.right
+ MouseArea {
+ onDoubleClicked: peerWindow.visible = true
+ anchors.fill: parent
+ }
+
+ Label {
+ id: peerLabel
+ font.pixelSize: 8
+ text: "0 / 0"
+ }
+ Image {
+ id: peerImage
+ width: 10; height: 10
+ source: "../network.png"
+ }
+ }
+ }
+
+
+ property var blockModel: ListModel {
+ id: blockModel
+ }
+
+ SplitView {
+ property var views: [];
+
+ id: mainSplit
+ anchors.fill: parent
+ resizing: false
+
+ function setView(view, menu) {
+ for(var i = 0; i < views.length; i++) {
+ views[i].view.visible = false
+ views[i].menuItem.setSelection(false)
+ }
+ view.visible = true
+
+ //menu.border.color = "#CCCCCC"
+ //menu.color = "#FFFFFFFF"
+ menu.setSelection(true)
+ }
+
+ function addComponent(view, options) {
+ view.visible = false
+ view.anchors.fill = mainView
+
+ if( !view.hasOwnProperty("iconSource") ) {
+ console.log("Could not load plugin. Property 'iconSourc' not found on view.");
+ return;
+ }
+
+ var menuItem = menu.createMenuItem(view.iconSource, view, options);
+ if( view.hasOwnProperty("menuItem") ) {
+ view.menuItem = menuItem;
+ }
+
+ if( view.hasOwnProperty("onReady") ) {
+ view.onReady.call(view)
+ }
+
+ if( options.active ) {
+ setView(view, menuItem)
+ }
+
+
+ return {view: view, menuItem: menuItem}
+ }
+
+ /*********************
+ * Main menu.
+ ********************/
+ Rectangle {
+ id: menu
+ Layout.minimumWidth: 210
+ Layout.maximumWidth: 210
+ anchors.top: parent.top
+ color: "#ececec"
+
+ Component {
+ id: menuItemTemplate
+ Rectangle {
+ id: menuItem
+ property var view;
+ property var path;
+ property var closable;
+
+ property alias title: label.text
+ property alias icon: icon.source
+ property alias secondaryTitle: secondary.text
+ function setSelection(on) {
+ sel.visible = on
+ }
+
+ width: 206
+ height: 28
+ color: "#00000000"
+
+ anchors {
+ left: parent.left
+ leftMargin: 4
+ }
+
+ Rectangle {
+ id: sel
+ visible: false
+ anchors.fill: parent
+ color: "#00000000"
+ Rectangle {
+ id: r
+ anchors.fill: parent
+ border.color: "#CCCCCC"
+ border.width: 1
+ radius: 5
+ color: "#FFFFFFFF"
+ }
+ Rectangle {
+ anchors {
+ top: r.top
+ bottom: r.bottom
+ right: r.right
+ }
+ width: 10
+ color: "#FFFFFFFF"
+
+ Rectangle {
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ }
+ height: 1
+ color: "#CCCCCC"
+ }
+
+ Rectangle {
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ }
+ height: 1
+ color: "#CCCCCC"
+ }
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ mainSplit.setView(view, menuItem)
+ }
+ }
+
+ Image {
+ id: icon
+ height: 20
+ width: 20
+ anchors {
+ left: parent.left
+ verticalCenter: parent.verticalCenter
+ leftMargin: 3
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ menuItem.closeApp()
+ }
+ }
+ }
+
+ Text {
+ id: label
+ anchors {
+ left: icon.right
+ verticalCenter: parent.verticalCenter
+ leftMargin: 3
+ }
+
+ color: "#0D0A01"
+ font.pixelSize: 12
+ }
+
+ Text {
+ id: secondary
+ anchors {
+ right: parent.right
+ rightMargin: 8
+ verticalCenter: parent.verticalCenter
+ }
+ color: "#AEADBE"
+ font.pixelSize: 12
+ }
+
+
+ function closeApp() {
+ if(!this.closable) { return; }
+
+ if(this.view.hasOwnProperty("onDestroy")) {
+ this.view.onDestroy.call(this.view)
+ }
+
+ this.view.destroy()
+ this.destroy()
+ gui.removePlugin(this.path)
+ }
+ }
+ }
+
+ function createMenuItem(icon, view, options) {
+ if(options === undefined) {
+ options = {};
+ }
+
+ var section;
+ switch(options.section) {
+ case "ethereum":
+ section = menuDefault;
+ break;
+ case "legacy":
+ section = menuLegacy;
+ break;
+ default:
+ section = menuApps;
+ break;
+ }
+
+ var comp = menuItemTemplate.createObject(section)
+
+ comp.view = view
+ comp.title = view.title
+ comp.icon = view.iconSource
+ comp.closable = options.close;
+
+ return comp
+ }
+
+ ColumnLayout {
+ id: menuColumn
+ y: 10
+ width: parent.width
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: 3
+
+ Text {
+ text: "ETHEREUM"
+ font.bold: true
+ anchors {
+ left: parent.left
+ leftMargin: 5
+ }
+ color: "#888888"
+ }
+
+ ColumnLayout {
+ id: menuDefault
+ spacing: 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ }
+
+
+ Text {
+ text: "APPS"
+ font.bold: true
+ anchors {
+ left: parent.left
+ leftMargin: 5
+ }
+ color: "#888888"
+ }
+
+ ColumnLayout {
+ id: menuApps
+ spacing: 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ }
+
+ Text {
+ text: "DEBUG"
+ font.bold: true
+ anchors {
+ left: parent.left
+ leftMargin: 5
+ }
+ color: "#888888"
+ }
+
+ ColumnLayout {
+ id: menuLegacy
+ spacing: 3
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ }
+ }
+ }
+
+ /*********************
+ * Main view
+ ********************/
+ Rectangle {
+ anchors.right: parent.right
+ anchors.left: menu.right
+ anchors.bottom: parent.bottom
+ anchors.top: parent.top
+ color: "#00000000"
+
+ Rectangle {
+ id: urlPane
+ height: 40
+ color: "#00000000"
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 5
+ rightMargin: 5
+ top: parent.top
+ topMargin: 5
+ }
+ TextField {
+ id: url
+ objectName: "url"
+ placeholderText: "DApp URL"
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.top
+ topMargin: 5
+ rightMargin: 5
+ leftMargin: 5
+ }
+
+ Keys.onReturnPressed: {
+ addPlugin(this.text, {close: true, section: "apps"})
+ }
+ }
+
+ }
+
+ // Border
+ Rectangle {
+ id: divider
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: urlPane.bottom
+ }
+ z: -1
+ height: 1
+ color: "#CCCCCC"
+ }
+
+ Rectangle {
+ id: mainView
+ color: "#00000000"
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.top: divider.bottom
+
+ function createView(component) {
+ var view = component.createObject(mainView)
+
+ return view;
+ }
+ }
+ }
+ }
+
+
+ /******************
+ * Dialogs
+ *****************/
+ FileDialog {
+ id: generalFileDialog
+ property var callback;
+ onAccepted: {
+ var path = this.fileUrl.toString();
+ callback.call(this, path);
+ }
+
+ function show(selectExisting, callback) {
+ generalFileDialog.callback = callback;
+ generalFileDialog.selectExisting = selectExisting;
+
+ this.open();
+ }
+ }
+
+
+ /******************
+ * Wallet functions
+ *****************/
+ function importApp(path) {
+ var ext = path.split('.').pop()
+ if(ext == "html" || ext == "htm") {
+ eth.openHtml(path)
+ }else if(ext == "qml"){
+ addPlugin(path, {close: true, section: "apps"})
+ }
+ }
+
+
+ function setWalletValue(value) {
+ walletValueLabel.text = value
+ }
+
+ function loadPlugin(name) {
+ console.log("Loading plugin" + name)
+ var view = mainView.addPlugin(name)
+ }
+
+ function setPeers(text) {
+ peerLabel.text = text
+ }
+
+ function addPeer(peer) {
+ // We could just append the whole peer object but it cries if you try to alter them
+ peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version})
+ }
+
+ function resetPeers(){
+ peerModel.clear()
+ }
+
+ function timeAgo(unixTs){
+ var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000
+ return (lapsed + " seconds ago")
+ }
+
+ function convertToPretty(unixTs){
+ var a = new Date(unixTs*1000);
+ var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
+ var year = a.getFullYear();
+ var month = months[a.getMonth()];
+ var date = a.getDate();
+ var hour = a.getHours();
+ var min = a.getMinutes();
+ var sec = a.getSeconds();
+ var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ;
+ return time;
+ }
+
+ /**********************
+ * Windows
+ *********************/
+ Window {
+ id: peerWindow
+ //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint
+ height: 200
+ width: 700
+ Rectangle {
+ anchors.fill: parent
+ property var peerModel: ListModel {
+ id: peerModel
+ }
+ TableView {
+ anchors.fill: parent
+ id: peerTable
+ model: peerModel
+ TableViewColumn{width: 100; role: "ip" ; title: "IP" }
+ TableViewColumn{width: 60; role: "port" ; title: "Port" }
+ TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
+ TableViewColumn{width: 100; role: "latency"; title: "Latency" }
+ TableViewColumn{width: 260; role: "version" ; title: "Version" }
+ }
+ }
+ }
+
+ Window {
+ id: aboutWin
+ visible: false
+ title: "About"
+ minimumWidth: 350
+ maximumWidth: 350
+ maximumHeight: 200
+ minimumHeight: 200
+
+ Image {
+ id: aboutIcon
+ height: 150
+ width: 150
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ source: "../facet.png"
+ x: 10
+ y: 10
+ }
+
+ Text {
+ anchors.left: aboutIcon.right
+ anchors.leftMargin: 10
+ anchors.top: parent.top
+ anchors.topMargin: 30
+ font.pointSize: 12
+ text: "<h2>Mist (0.6.5)</h2><h4>Amalthea</h4><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br><h3>Building</h3>Maran Hidskes"
+ }
+ }
+
+ Window {
+ id: txImportDialog
+ minimumWidth: 270
+ maximumWidth: 270
+ maximumHeight: 50
+ minimumHeight: 50
+ TextField {
+ id: txImportField
+ width: 170
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.leftMargin: 10
+ onAccepted: {
+ }
+ }
+ Button {
+ anchors.left: txImportField.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.leftMargin: 5
+ text: "Import"
+ onClicked: {
+ eth.importTx(txImportField.text)
+ txImportField.visible = false
+ }
+ }
+ Component.onCompleted: {
+ addrField.focus = true
+ }
+ }
+
+ Window {
+ id: addPeerWin
+ visible: false
+ minimumWidth: 300
+ maximumWidth: 300
+ maximumHeight: 50
+ minimumHeight: 50
+ title: "Connect to peer"
+
+ ComboBox {
+ id: addrField
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ anchors.right: addPeerButton.left
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+ onAccepted: {
+ eth.connectToPeer(addrField.currentText)
+ addPeerWin.visible = false
+ }
+
+ editable: true
+ model: ListModel { id: pastPeers }
+
+ Component.onCompleted: {
+ var ips = eth.pastPeers()
+ for(var i = 0; i < ips.length; i++) {
+ pastPeers.append({text: ips.get(i)})
+ }
+
+ pastPeers.insert(0, {text: "poc-6.ethdev.com:30303"})
+ }
+ }
+
+ Button {
+ id: addPeerButton
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.rightMargin: 10
+ text: "Add"
+ onClicked: {
+ eth.connectToPeer(addrField.currentText)
+ addPeerWin.visible = false
+ }
+ }
+ Component.onCompleted: {
+ addrField.focus = true
+ }
+ }
+ }
diff --git a/mist/assets/qml/webapp.qml b/mist/assets/qml/webapp.qml
new file mode 100644
index 000000000..8801b6b47
--- /dev/null
+++ b/mist/assets/qml/webapp.qml
@@ -0,0 +1,409 @@
+import QtQuick 2.0
+import QtWebKit 3.0
+import QtWebKit.experimental 1.0
+import QtQuick.Controls 1.0;
+import QtQuick.Controls.Styles 1.0
+import QtQuick.Layouts 1.0;
+import QtQuick.Window 2.1;
+import Ethereum 1.0
+
+import "../ext/qml_messaging.js" as Messaging
+
+//ApplicationWindow {
+ Rectangle {
+ id: window
+ property var title: "Browser"
+ property var iconSource: "../browser.png"
+ property var menuItem
+
+ property alias url: webview.url
+ property alias webView: webview
+
+ Component.onCompleted: {
+ webview.url = "http://etherian.io"
+ }
+
+ signal messages(var messages, int id);
+ onMessages: {
+ // Bit of a cheat to get proper JSON
+ var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
+ webview.postEvent("messages", [m, id]);
+ }
+
+ Item {
+ objectName: "root"
+ id: root
+ anchors.fill: parent
+ state: "inspectorShown"
+
+ RowLayout {
+ id: navBar
+ height: 40
+ anchors {
+ left: parent.left
+ right: parent.right
+ leftMargin: 7
+ }
+
+ Button {
+ id: back
+ onClicked: {
+ webview.goBack()
+ }
+ style: ButtonStyle {
+ background: Image {
+ source: "../back.png"
+ width: 30
+ height: 30
+ }
+ }
+ }
+
+ TextField {
+ anchors {
+ left: back.right
+ right: toggleInspector.left
+ leftMargin: 5
+ rightMargin: 5
+ }
+ text: "http://etherian.io"
+ id: uriNav
+ y: parent.height / 2 - this.height / 2
+
+ Keys.onReturnPressed: {
+ webview.url = this.text;
+ }
+ }
+
+ Button {
+ id: toggleInspector
+ anchors {
+ right: parent.right
+ }
+ iconSource: "../bug.png"
+ onClicked: {
+ if(inspector.visible == true){
+ inspector.visible = false
+ }else{
+ inspector.visible = true
+ inspector.url = webview.experimental.remoteInspectorUrl
+ }
+ }
+ }
+ }
+
+
+ WebView {
+ objectName: "webView"
+ id: webview
+ anchors {
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+ top: navBar.bottom
+ }
+
+ property var cleanPath: false
+ onNavigationRequested: {
+ if(!this.cleanPath) {
+ var uri = request.url.toString();
+ if(!/.*\:\/\/.*/.test(uri)) {
+ uri = "http://" + uri;
+ }
+
+ var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
+
+ if(reg.test(uri)) {
+ uri.replace(reg, function(match, pre, domain, path) {
+ uri = pre;
+
+ var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
+ var ip = [];
+ for(var i = 0, l = lookup.length; i < l; i++) {
+ ip.push(lookup.charCodeAt(i))
+ }
+
+ if(ip.length != 0) {
+ uri += lookup;
+ } else {
+ uri += domain;
+ }
+
+ uri += path;
+ });
+ }
+
+ this.cleanPath = true;
+
+ webview.url = uri;
+ } else {
+ // Prevent inf loop.
+ this.cleanPath = false;
+ }
+ }
+
+ function sendMessage(data) {
+ webview.experimental.postMessage(JSON.stringify(data))
+ }
+
+ onTitleChanged: {
+ var data = Messaging.HandleMessage(title);
+ if(data) {
+ sendMessage(data)
+ }
+ }
+
+ experimental.preferences.javascriptEnabled: true
+ experimental.preferences.navigatorQtObjectEnabled: true
+ experimental.preferences.developerExtrasEnabled: true
+ experimental.userScripts: ["../ext/q.js", "../ext/pre.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"]
+ experimental.onMessageReceived: {
+ console.log("[onMessageReceived]: ", message.data)
+ // TODO move to messaging.js
+ var data = JSON.parse(message.data)
+
+ try {
+ switch(data.call) {
+ case "compile":
+ postData(data._seed, eth.compile(data.args[0]))
+ break
+
+ case "getCoinBase":
+ postData(data._seed, eth.coinBase())
+
+ break
+
+ case "getIsListening":
+ postData(data._seed, eth.isListening())
+
+ break
+
+ case "getIsMining":
+ postData(data._seed, eth.isMining())
+
+ break
+
+ case "getPeerCount":
+ postData(data._seed, eth.peerCount())
+
+ break
+
+ case "getCountAt":
+ require(1)
+ postData(data._seed, eth.txCountAt(data.args[0]))
+
+ break
+
+ case "getCodeAt":
+ require(1)
+ var code = eth.codeAt(data.args[0])
+ postData(data._seed, code);
+
+ break
+
+ case "getBlockByNumber":
+ var block = eth.blockByNumber(data.args[0])
+ postData(data._seed, block)
+
+ break
+
+ case "getBlockByHash":
+ var block = eth.blockByHash(data.args[0])
+ postData(data._seed, block)
+
+ break
+
+ case "transact":
+ require(5)
+
+ var tx = eth.transact(data.args)
+ postData(data._seed, tx)
+
+ break
+
+ case "getStorageAt":
+ require(2);
+
+ var storage = eth.storageAt(data.args[0], data.args[1]);
+ postData(data._seed, storage)
+
+ break
+
+ case "call":
+ require(1);
+ var ret = eth.call(data.args)
+ postData(data._seed, ret)
+
+ break
+
+ case "getEachStorage":
+ require(1);
+ var storage = JSON.parse(eth.eachStorage(data.args[0]))
+ postData(data._seed, storage)
+
+ break
+
+ case "getTransactionsFor":
+ require(1);
+ var txs = eth.transactionsFor(data.args[0], true)
+ postData(data._seed, txs)
+
+ break
+
+ case "getBalanceAt":
+ require(1);
+
+ postData(data._seed, eth.balanceAt(data.args[0]));
+
+ break
+
+ case "getKey":
+ var key = eth.key().privateKey;
+
+ postData(data._seed, key)
+ break
+
+ case "watch":
+ require(2)
+ eth.watch(data.args[0], data.args[1])
+
+ case "disconnect":
+ require(1)
+ postData(data._seed, null)
+
+ break;
+
+ case "getSecretToAddress":
+ require(1)
+
+ var addr = eth.secretToAddress(data.args[0])
+ console.log("getsecret", addr)
+ postData(data._seed, addr)
+
+ break;
+
+ case "messages":
+ require(1);
+
+ var messages = JSON.parse(eth.getMessages(data.args[0]))
+ postData(data._seed, messages)
+
+ break
+
+ case "mutan":
+ require(1)
+
+ var code = eth.compileMutan(data.args[0])
+ postData(data._seed, "0x"+code)
+
+ break;
+
+ case "newFilterString":
+ require(1)
+ var id = eth.newFilterString(data.args[0])
+ postData(data._seed, id);
+ break;
+ case "newFilter":
+ require(1)
+ var id = eth.newFilter(data.args[0])
+
+ postData(data._seed, id);
+ break;
+
+ case "getMessages":
+ require(1);
+
+ var messages = eth.messages(data.args[0]);
+ var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
+ postData(data._seed, m);
+
+ break;
+
+ case "deleteFilter":
+ require(1);
+ eth.uninstallFilter(data.args[0])
+ break;
+ }
+ } catch(e) {
+ console.log(data.call + ": " + e)
+
+ postData(data._seed, null);
+ }
+ }
+
+
+ function post(seed, data) {
+ postData(data._seed, data)
+ }
+
+ function require(args, num) {
+ if(args.length < num) {
+ throw("required argument count of "+num+" got "+args.length);
+ }
+ }
+ function postData(seed, data) {
+ webview.experimental.postMessage(JSON.stringify({data: data, _seed: seed}))
+ }
+ function postEvent(event, data) {
+ webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
+ }
+
+ function onWatchedCb(data, id) {
+ var messages = JSON.parse(data)
+ postEvent("watched:"+id, messages)
+ }
+
+ function onNewBlockCb(block) {
+ postEvent("block:new", block)
+ }
+ function onObjectChangeCb(stateObject) {
+ postEvent("object:"+stateObject.address(), stateObject)
+ }
+ function onStorageChangeCb(storageObject) {
+ var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
+ postEvent(ev, [storageObject.address, storageObject.value])
+ }
+ }
+
+
+ Rectangle {
+ id: sizeGrip
+ color: "gray"
+ visible: false
+ height: 10
+ anchors {
+ left: root.left
+ right: root.right
+ }
+ y: Math.round(root.height * 2 / 3)
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: sizeGrip
+ drag.minimumY: 0
+ drag.maximumY: root.height
+ drag.axis: Drag.YAxis
+ }
+ }
+
+ WebView {
+ id: inspector
+ visible: false
+ anchors {
+ left: root.left
+ right: root.right
+ top: sizeGrip.bottom
+ bottom: root.bottom
+ }
+ }
+
+ states: [
+ State {
+ name: "inspectorShown"
+ PropertyChanges {
+ target: inspector
+ }
+ }
+ ]
+ }
+ }
diff --git a/ethereal/assets/tx.png b/mist/assets/tx.png
index 62204c315..62204c315 100644
--- a/ethereal/assets/tx.png
+++ b/mist/assets/tx.png
Binary files differ
diff --git a/ethereal/assets/util/test.html b/mist/assets/util/test.html
index d458e6670..d458e6670 100644
--- a/ethereal/assets/util/test.html
+++ b/mist/assets/util/test.html
diff --git a/ethereal/assets/wallet.png b/mist/assets/wallet.png
index 92c401e52..92c401e52 100644
--- a/ethereal/assets/wallet.png
+++ b/mist/assets/wallet.png
Binary files differ
diff --git a/mist/bindings.go b/mist/bindings.go
new file mode 100644
index 000000000..141c4a469
--- /dev/null
+++ b/mist/bindings.go
@@ -0,0 +1,148 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strconv"
+
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethlog"
+ "github.com/ethereum/eth-go/ethpipe"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/go-ethereum/utils"
+)
+
+type plugin struct {
+ Name string `json:"name"`
+ Path string `json:"path"`
+}
+
+func (gui *Gui) Println(v ...interface{}) {
+ gui.printLog(fmt.Sprintln(v...))
+}
+
+func (gui *Gui) Printf(format string, v ...interface{}) {
+ gui.printLog(fmt.Sprintf(format, v...))
+}
+
+// Print function that logs directly to the GUI
+func (gui *Gui) printLog(s string) {
+ /*
+ str := strings.TrimRight(s, "\n")
+ lines := strings.Split(str, "\n")
+
+ view := gui.getObjectByName("infoView")
+ for _, line := range lines {
+ view.Call("addLog", line)
+ }
+ */
+}
+func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) {
+ var data string
+ if len(recipient) == 0 {
+ code, err := ethutil.Compile(d, false)
+ if err != nil {
+ return nil, err
+ }
+ data = ethutil.Bytes2Hex(code)
+ } else {
+ data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
+ }
+
+ return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
+}
+
+func (gui *Gui) SetCustomIdentifier(customIdentifier string) {
+ gui.clientIdentity.SetCustomIdentifier(customIdentifier)
+ gui.config.Save("id", customIdentifier)
+}
+
+func (gui *Gui) GetCustomIdentifier() string {
+ return gui.clientIdentity.GetCustomIdentifier()
+}
+
+func (gui *Gui) ToggleTurboMining() {
+ gui.miner.ToggleTurbo()
+}
+
+// functions that allow Gui to implement interface ethlog.LogSystem
+func (gui *Gui) SetLogLevel(level ethlog.LogLevel) {
+ gui.logLevel = level
+ gui.stdLog.SetLogLevel(level)
+ gui.config.Save("loglevel", level)
+}
+
+func (gui *Gui) GetLogLevel() ethlog.LogLevel {
+ return gui.logLevel
+}
+
+func (self *Gui) AddPlugin(pluginPath string) {
+ self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath}
+
+ json, _ := json.MarshalIndent(self.plugins, "", " ")
+ ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
+}
+
+func (self *Gui) RemovePlugin(pluginPath string) {
+ delete(self.plugins, pluginPath)
+
+ json, _ := json.MarshalIndent(self.plugins, "", " ")
+ ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
+}
+
+// this extra function needed to give int typecast value to gui widget
+// that sets initial loglevel to default
+func (gui *Gui) GetLogLevelInt() int {
+ return int(gui.logLevel)
+}
+func (self *Gui) DumpState(hash, path string) {
+ var stateDump []byte
+
+ if len(hash) == 0 {
+ stateDump = self.eth.StateManager().CurrentState().Dump()
+ } else {
+ var block *ethchain.Block
+ if hash[0] == '#' {
+ i, _ := strconv.Atoi(hash[1:])
+ block = self.eth.BlockChain().GetBlockByNumber(uint64(i))
+ } else {
+ block = self.eth.BlockChain().GetBlock(ethutil.Hex2Bytes(hash))
+ }
+
+ if block == nil {
+ logger.Infof("block err: not found %s\n", hash)
+ return
+ }
+
+ stateDump = block.State().Dump()
+ }
+
+ file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm)
+ if err != nil {
+ logger.Infoln("dump err: ", err)
+ return
+ }
+ defer file.Close()
+
+ logger.Infof("dumped state (%s) to %s\n", hash, path)
+
+ file.Write(stateDump)
+}
+func (gui *Gui) ToggleMining() {
+ var txt string
+ if gui.eth.Mining {
+ utils.StopMining(gui.eth)
+ txt = "Start mining"
+
+ gui.getObjectByName("miningLabel").Set("visible", false)
+ } else {
+ utils.StartMining(gui.eth)
+ gui.miner = utils.GetMiner()
+ txt = "Stop mining"
+
+ gui.getObjectByName("miningLabel").Set("visible", true)
+ }
+
+ gui.win.Root().Set("miningButtonText", txt)
+}
diff --git a/ethereal/debugger.go b/mist/debugger.go
index 7bc544377..9d1de8c42 100644
--- a/ethereal/debugger.go
+++ b/mist/debugger.go
@@ -5,6 +5,7 @@ import (
"math/big"
"strconv"
"strings"
+ "unicode"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethstate"
@@ -93,9 +94,7 @@ func (self *DebuggerWindow) ClearLog() {
}
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
- if !self.Db.done {
- self.Db.Q <- true
- }
+ self.Stop()
defer func() {
if r := recover(); r != nil {
@@ -185,6 +184,12 @@ func (self *DebuggerWindow) Continue() {
self.Next()
}
+func (self *DebuggerWindow) Stop() {
+ if !self.Db.done {
+ self.Db.Q <- true
+ }
+}
+
func (self *DebuggerWindow) ExecCommand(command string) {
if len(command) > 0 {
cmd := strings.Split(command, " ")
@@ -271,9 +276,20 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et
d.win.Root().Call("clearStorage")
addr := 0
- for i := 0; i+32 <= mem.Len(); i += 32 {
- d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
- addr++
+ for i := 0; i+16 <= mem.Len(); i += 16 {
+ dat := mem.Data()[i : i+16]
+ var str string
+
+ for _, d := range dat {
+ if unicode.IsGraphic(rune(d)) {
+ str += string(d)
+ } else {
+ str += "?"
+ }
+ }
+
+ d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s % x", str, dat)})
+ addr += 16
}
for _, val := range stack.Data() {
@@ -284,6 +300,12 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
})
+ stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32))
+ psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64())
+ d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`<b>stack ptr</b>: %v`, stackFrameAt))
+ d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`<b>stack size</b>: %d`, psize))
+ d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`<b>mem size</b>: %v`, mem.Len()))
+
out:
for {
select {
diff --git a/mist/errors.go b/mist/errors.go
new file mode 100644
index 000000000..409b7a281
--- /dev/null
+++ b/mist/errors.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "gopkg.in/qml.v1"
+)
+
+func ErrorWindow(err error) {
+ engine := qml.NewEngine()
+ component, e := engine.LoadString("local", qmlErr)
+ if e != nil {
+ fmt.Println("err:", err)
+ os.Exit(1)
+ }
+
+ win := component.CreateWindow(nil)
+ win.Root().ObjectByName("label").Set("text", err.Error())
+ win.Show()
+ win.Wait()
+}
+
+const qmlErr = `
+import QtQuick 2.0; import QtQuick.Controls 1.0;
+ApplicationWindow {
+ width: 600; height: 150;
+ flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
+ title: "Error"
+ Text {
+ x: parent.width / 2 - this.width / 2;
+ y: parent.height / 2 - this.height / 2;
+ objectName: "label";
+ }
+}
+`
diff --git a/ethereal/ext_app.go b/mist/ext_app.go
index 514084c97..514084c97 100644
--- a/ethereal/ext_app.go
+++ b/mist/ext_app.go
diff --git a/ethereal/flags.go b/mist/flags.go
index c9327c3d3..d2e7d3fb0 100644
--- a/ethereal/flags.go
+++ b/mist/flags.go
@@ -1,15 +1,16 @@
package main
import (
- "bitbucket.org/kardianos/osext"
"flag"
"fmt"
- "github.com/ethereum/eth-go/ethlog"
"os"
"os/user"
"path"
"path/filepath"
"runtime"
+
+ "bitbucket.org/kardianos/osext"
+ "github.com/ethereum/eth-go/ethlog"
)
var Identifier string
@@ -43,7 +44,7 @@ func defaultAssetPath() string {
// assume a debug build and use the source directory as
// asset directory.
pwd, _ := os.Getwd()
- if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") {
+ if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist") {
assetPath = path.Join(pwd, "assets")
} else {
switch runtime.GOOS {
@@ -52,7 +53,7 @@ func defaultAssetPath() string {
exedir, _ := osext.ExecutableFolder()
assetPath = filepath.Join(exedir, "../Resources")
case "linux":
- assetPath = "/usr/share/ethereal"
+ assetPath = "/usr/share/mist"
case "windows":
assetPath = "./assets"
default:
@@ -63,7 +64,7 @@ func defaultAssetPath() string {
}
func defaultDataDir() string {
usr, _ := user.Current()
- return path.Join(usr.HomeDir, ".ethereal")
+ return path.Join(usr.HomeDir, ".mist")
}
var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini")
@@ -78,7 +79,7 @@ func Init() {
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
- flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
+ flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support")
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
diff --git a/ethereal/gui.go b/mist/gui.go
index 6d16ec484..f80e46761 100644
--- a/ethereal/gui.go
+++ b/mist/gui.go
@@ -1,11 +1,14 @@
package main
+import "C"
+
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
- "os"
+ "path"
+ "runtime"
"strconv"
"strings"
"time"
@@ -19,16 +22,33 @@ import (
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
- "github.com/ethereum/go-ethereum/utils"
"gopkg.in/qml.v1"
)
-var logger = ethlog.NewLogger("GUI")
+/*
+func LoadExtension(path string) (uintptr, error) {
+ lib, err := ffi.NewLibrary(path)
+ if err != nil {
+ return 0, err
+ }
+
+ so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
+ if err != nil {
+ return 0, err
+ }
+
+ ptr := so()
+
+ err = lib.Close()
+ if err != nil {
+ return 0, err
+ }
-type plugin struct {
- Name string `json:"name"`
- Path string `json:"path"`
+ return ptr.Interface().(uintptr), nil
}
+*/
+
+var logger = ethlog.NewLogger("GUI")
type Gui struct {
// The main application window
@@ -56,7 +76,8 @@ type Gui struct {
plugins map[string]plugin
- miner *ethminer.Miner
+ miner *ethminer.Miner
+ stdLog ethlog.LogSystem
}
// Create GUI, but doesn't start it
@@ -68,12 +89,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden
pipe := ethpipe.NewJSPipe(ethereum)
gui := &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: ethlog.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)}
- data, err := ethutil.ReadAllFile(ethutil.Config.ExecPath + "/plugins.json")
- if err != nil {
- fmt.Println(err)
- }
- fmt.Println("plugins:", string(data))
-
+ data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json"))
json.Unmarshal([]byte(data), &gui.plugins)
return gui
@@ -100,6 +116,16 @@ func (gui *Gui) Start(assetPath string) {
context.SetVar("gui", gui)
context.SetVar("eth", gui.uiLib)
+ /*
+ vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
+ fmt.Printf("Fetched vec with addr: %#x\n", vec)
+ if errr != nil {
+ fmt.Println(errr)
+ } else {
+ context.SetVar("vec", (unsafe.Pointer)(vec))
+ }
+ */
+
// Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
@@ -145,24 +171,6 @@ func (gui *Gui) Stop() {
logger.Infoln("Stopped")
}
-func (gui *Gui) ToggleMining() {
- var txt string
- if gui.eth.Mining {
- utils.StopMining(gui.eth)
- txt = "Start mining"
-
- gui.getObjectByName("miningLabel").Set("visible", false)
- } else {
- utils.StartMining(gui.eth)
- gui.miner = utils.GetMiner()
- txt = "Stop mining"
-
- gui.getObjectByName("miningLabel").Set("visible", true)
- }
-
- gui.win.Root().Set("miningButtonText", txt)
-}
-
func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml"))
if err != nil {
@@ -176,44 +184,9 @@ func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
return gui.win, nil
}
-func (self *Gui) DumpState(hash, path string) {
- var stateDump []byte
-
- if len(hash) == 0 {
- stateDump = self.eth.StateManager().CurrentState().Dump()
- } else {
- var block *ethchain.Block
- if hash[0] == '#' {
- i, _ := strconv.Atoi(hash[1:])
- block = self.eth.BlockChain().GetBlockByNumber(uint64(i))
- } else {
- block = self.eth.BlockChain().GetBlock(ethutil.Hex2Bytes(hash))
- }
-
- if block == nil {
- logger.Infof("block err: not found %s\n", hash)
- return
- }
-
- stateDump = block.State().Dump()
- }
-
- file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm)
- if err != nil {
- logger.Infoln("dump err: ", err)
- return
- }
- defer file.Close()
-
- logger.Infof("dumped state (%s) to %s\n", hash, path)
-
- file.Write(stateDump)
-}
-
// The done handler will be called by QML when all views have been loaded
func (gui *Gui) Done() {
gui.qmlDone = true
-
}
func (gui *Gui) ImportKey(filePath string) {
@@ -337,7 +310,7 @@ func (gui *Gui) insertTransaction(window string, tx *ethchain.Transaction) {
ptx.Address = r
if window == "post" {
- gui.getObjectByName("transactionView").Call("addTx", ptx, inout)
+ //gui.getObjectByName("transactionView").Call("addTx", ptx, inout)
} else {
gui.getObjectByName("pendingTxView").Call("addTx", ptx, inout)
}
@@ -397,6 +370,8 @@ func (gui *Gui) update() {
}()
for _, plugin := range gui.plugins {
+ logger.Infoln("Loading plugin ", plugin.Name)
+
gui.win.Root().Call("addPlugin", plugin.Path, "")
}
@@ -411,6 +386,7 @@ func (gui *Gui) update() {
peerUpdateTicker := time.NewTicker(5 * time.Second)
generalUpdateTicker := time.NewTicker(1 * time.Second)
+ statsUpdateTicker := time.NewTicker(5 * time.Second)
state := gui.eth.StateManager().TransState()
@@ -450,12 +426,12 @@ func (gui *Gui) update() {
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
object.SubAmount(tx.Value)
- gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "send")
+ //gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "send")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
object.AddAmount(tx.Value)
- gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "recv")
+ //gui.getObjectByName("transactionView").Call("addTx", ethpipe.NewJSTx(tx), "recv")
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
}
@@ -487,6 +463,10 @@ func (gui *Gui) update() {
pow := gui.miner.GetPow()
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash")
}
+
+ case <-statsUpdateTicker.C:
+ gui.setStatsPane()
+
}
}
}()
@@ -506,9 +486,30 @@ func (gui *Gui) update() {
reactor.Subscribe("peerList", peerChan)
}
-func (gui *Gui) CopyToClipboard(data string) {
- //clipboard.WriteAll("test")
- fmt.Println("COPY currently BUGGED. Here are the contents:\n", data)
+func (gui *Gui) setStatsPane() {
+ var memStats runtime.MemStats
+ runtime.ReadMemStats(&memStats)
+
+ statsPane := gui.getObjectByName("statsPane")
+ statsPane.Set("text", fmt.Sprintf(`###### Mist 0.6.5 (%s) #######
+
+eth %d (p2p = %d)
+
+CPU: # %d
+Goroutines: # %d
+CGoCalls: # %d
+
+Alloc: %d
+Heap Alloc: %d
+
+CGNext: %x
+NumGC: %d
+`, runtime.Version(),
+ eth.ProtocolVersion, eth.P2PVersion,
+ runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
+ memStats.Alloc, memStats.HeapAlloc,
+ memStats.NextGC, memStats.NumGC,
+ ))
}
func (gui *Gui) setPeerInfo() {
@@ -527,82 +528,3 @@ func (gui *Gui) privateKey() string {
func (gui *Gui) address() []byte {
return gui.eth.KeyManager().Address()
}
-
-func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (*ethpipe.JSReceipt, error) {
- var data string
- if len(recipient) == 0 {
- code, err := ethutil.Compile(d, false)
- if err != nil {
- return nil, err
- }
- data = ethutil.Bytes2Hex(code)
- } else {
- data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
- }
-
- return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
-}
-
-func (gui *Gui) SetCustomIdentifier(customIdentifier string) {
- gui.clientIdentity.SetCustomIdentifier(customIdentifier)
- gui.config.Save("id", customIdentifier)
-}
-
-func (gui *Gui) GetCustomIdentifier() string {
- return gui.clientIdentity.GetCustomIdentifier()
-}
-
-func (gui *Gui) ToggleTurboMining() {
- gui.miner.ToggleTurbo()
-}
-
-// functions that allow Gui to implement interface ethlog.LogSystem
-func (gui *Gui) SetLogLevel(level ethlog.LogLevel) {
- gui.logLevel = level
- gui.config.Save("loglevel", level)
-}
-
-func (gui *Gui) GetLogLevel() ethlog.LogLevel {
- return gui.logLevel
-}
-
-func (self *Gui) AddPlugin(pluginPath string) {
- self.plugins[pluginPath] = plugin{Name: "SomeName", Path: pluginPath}
-
- json, _ := json.MarshalIndent(self.plugins, "", " ")
- ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
-}
-
-func (self *Gui) RemovePlugin(pluginPath string) {
- delete(self.plugins, pluginPath)
-
- json, _ := json.MarshalIndent(self.plugins, "", " ")
- ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
-}
-
-// this extra function needed to give int typecast value to gui widget
-// that sets initial loglevel to default
-func (gui *Gui) GetLogLevelInt() int {
- return int(gui.logLevel)
-}
-
-func (gui *Gui) Println(v ...interface{}) {
- gui.printLog(fmt.Sprintln(v...))
-}
-
-func (gui *Gui) Printf(format string, v ...interface{}) {
- gui.printLog(fmt.Sprintf(format, v...))
-}
-
-// Print function that logs directly to the GUI
-func (gui *Gui) printLog(s string) {
- /*
- str := strings.TrimRight(s, "\n")
- lines := strings.Split(str, "\n")
-
- view := gui.getObjectByName("infoView")
- for _, line := range lines {
- view.Call("addLog", line)
- }
- */
-}
diff --git a/ethereal/html_container.go b/mist/html_container.go
index 69edea570..69edea570 100644
--- a/ethereal/html_container.go
+++ b/mist/html_container.go
diff --git a/ethereal/main.go b/mist/main.go
index 4cb8630e8..747616f8f 100644
--- a/ethereal/main.go
+++ b/mist/main.go
@@ -11,8 +11,8 @@ import (
)
const (
- ClientIdentifier = "Ethereal"
- Version = "0.6.4"
+ ClientIdentifier = "Mist"
+ Version = "0.6.5"
)
var ethereum *eth.Ethereum
@@ -25,9 +25,15 @@ func run() error {
utils.InitDataDir(Datadir)
- utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
+ stdLog := utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
db := utils.NewDatabase()
+ err := utils.DBSanityCheck(db)
+ if err != nil {
+ ErrorWindow(err)
+
+ os.Exit(1)
+ }
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
@@ -47,6 +53,7 @@ func run() error {
}
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
+ gui.stdLog = stdLog
utils.RegisterInterrupt(func(os.Signal) {
gui.Stop()
@@ -64,7 +71,6 @@ func main() {
// This is a bit of a cheat, but ey!
os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999")
- //qml.Init(nil)
qml.Run(run)
var interrupted = false
diff --git a/ethereal/qml_container.go b/mist/qml_container.go
index 85bd7c699..85bd7c699 100644
--- a/ethereal/qml_container.go
+++ b/mist/qml_container.go
diff --git a/ethereal/ui_lib.go b/mist/ui_lib.go
index 4b8210da6..a913af7db 100644
--- a/ethereal/ui_lib.go
+++ b/mist/ui_lib.go
@@ -37,11 +37,14 @@ type UiLib struct {
jsEngine *javascript.JSRE
filterCallbacks map[int][]int
- filters map[int]*GuiFilter
}
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
- return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int), filters: make(map[int]*GuiFilter)}
+ return &UiLib{JSPipe: ethpipe.NewJSPipe(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*ethpipe.JSFilter)}
+}
+
+func (self *UiLib) Notef(args []interface{}) {
+ logger.Infoln(args...)
}
func (self *UiLib) LookupDomain(domain string) string {
@@ -53,7 +56,7 @@ func (self *UiLib) LookupDomain(domain string) string {
data := world.Config().Get("DnsReg").StorageString(domain).Bytes()
// Left padded = A record, Right padded = CNAME
- if data[0] == 0 {
+ if len(data) > 0 && data[0] == 0 {
data = bytes.TrimLeft(data, "\x00")
var ipSlice []string
for _, d := range data {
@@ -68,6 +71,10 @@ func (self *UiLib) LookupDomain(domain string) string {
}
}
+func (self *UiLib) PastPeers() *ethutil.List {
+ return ethutil.NewList(eth.PastPeers())
+}
+
func (self *UiLib) ImportTx(rlpTx string) {
tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
self.eth.TxPool().QueueTransaction(tx)
@@ -160,49 +167,40 @@ func (self *UiLib) StartDebugger() {
dbWindow.Show()
}
-func (self *UiLib) RegisterFilter(object map[string]interface{}, seed int) {
- filter := &GuiFilter{ethpipe.NewJSFilterFromMap(object, self.eth), seed}
- self.filters[seed] = filter
-
+func (self *UiLib) NewFilter(object map[string]interface{}) int {
+ filter, id := self.eth.InstallFilter(object)
filter.MessageCallback = func(messages ethstate.Messages) {
- for _, callbackSeed := range self.filterCallbacks[seed] {
- self.win.Root().Call("invokeFilterCallback", filter.MessagesToJson(messages), seed, callbackSeed)
- }
+ self.win.Root().Call("invokeFilterCallback", ethpipe.ToJSMessages(messages), id)
}
+ return id
}
-func (self *UiLib) RegisterFilterString(typ string, seed int) {
- filter := &GuiFilter{ethpipe.NewJSFilterFromMap(nil, self.eth), seed}
- self.filters[seed] = filter
-
- if typ == "chain" {
- filter.BlockCallback = func(block *ethchain.Block) {
- for _, callbackSeed := range self.filterCallbacks[seed] {
- self.win.Root().Call("invokeFilterCallback", "{}", seed, callbackSeed)
- }
- }
+func (self *UiLib) NewFilterString(typ string) int {
+ filter, id := self.eth.InstallFilter(nil)
+ filter.BlockCallback = func(block *ethchain.Block) {
+ self.win.Root().Call("invokeFilterCallback", "{}", id)
}
-}
-func (self *UiLib) RegisterFilterCallback(seed, cbSeed int) {
- self.filterCallbacks[seed] = append(self.filterCallbacks[seed], cbSeed)
+ return id
}
-func (self *UiLib) UninstallFilter(seed int) {
- filter := self.filters[seed]
+func (self *UiLib) Messages(id int) *ethutil.List {
+ filter := self.eth.GetFilter(id)
if filter != nil {
- filter.Uninstall()
- delete(self.filters, seed)
+ messages := filter.Find()
+
+ return ethpipe.ToJSMessages(messages)
}
+
+ return ethutil.EmptyList()
}
-type GuiFilter struct {
- *ethpipe.JSFilter
- seed int
+func (self *UiLib) UninstallFilter(id int) {
+ self.eth.UninstallFilter(id)
}
-func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt, error) {
+func mapToTxParams(object map[string]interface{}) map[string]string {
// Default values
if object["from"] == nil {
object["from"] = ""
@@ -224,6 +222,8 @@ func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt,
var data []string
if list, ok := object["data"].(*qml.List); ok {
list.Convert(&data)
+ } else if str, ok := object["data"].(string); ok {
+ data = []string{str}
}
for _, str := range data {
@@ -239,13 +239,49 @@ func (self *UiLib) Transact(object map[string]interface{}) (*ethpipe.JSReceipt,
dataStr += str
}
+ object["data"] = dataStr
+ fmt.Println(object)
+
+ conv := make(map[string]string)
+ for key, value := range object {
+ if v, ok := value.(string); ok {
+ conv[key] = v
+ }
+ }
+
+ return conv
+}
+
+func (self *UiLib) Transact(params map[string]interface{}) (*ethpipe.JSReceipt, error) {
+ object := mapToTxParams(params)
return self.JSPipe.Transact(
- object["from"].(string),
- object["to"].(string),
- object["value"].(string),
- object["gas"].(string),
- object["gasPrice"].(string),
- dataStr,
+ object["from"],
+ object["to"],
+ object["value"],
+ object["gas"],
+ object["gasPrice"],
+ object["data"],
+ )
+}
+
+func (self *UiLib) Compile(code string) (string, error) {
+ bcode, err := ethutil.Compile(code, false)
+ if err != nil {
+ return err.Error(), err
+ }
+
+ return ethutil.Bytes2Hex(bcode), err
+}
+
+func (self *UiLib) Call(params map[string]interface{}) (string, error) {
+ object := mapToTxParams(params)
+
+ return self.JSPipe.Execute(
+ object["to"],
+ object["value"],
+ object["gas"],
+ object["gasPrice"],
+ object["data"],
)
}
diff --git a/utils/cmd.go b/utils/cmd.go
index cda735c27..700542cae 100644
--- a/utils/cmd.go
+++ b/utils/cmd.go
@@ -80,6 +80,16 @@ func confirm(message string) bool {
return r == "y"
}
+func DBSanityCheck(db ethutil.Database) error {
+ d, _ := db.Get([]byte("ProtocolVersion"))
+ protov := ethutil.NewValue(d).Uint()
+ if protov != eth.ProtocolVersion && protov != 0 {
+ return fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, eth.ProtocolVersion, ethutil.Config.ExecPath+"/database")
+ }
+
+ return nil
+}
+
func InitDataDir(Datadir string) {
_, err := os.Stat(Datadir)
if err != nil {
@@ -90,18 +100,22 @@ func InitDataDir(Datadir string) {
}
}
-func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) {
+func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) ethlog.LogSystem {
var writer io.Writer
if LogFile == "" {
writer = os.Stdout
} else {
writer = openLogFile(Datadir, LogFile)
}
- ethlog.AddLogSystem(ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.LogLevel(LogLevel)))
+
+ sys := ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.LogLevel(LogLevel))
+ ethlog.AddLogSystem(sys)
if DebugFile != "" {
writer = openLogFile(Datadir, DebugFile)
ethlog.AddLogSystem(ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.DebugLevel))
}
+
+ return sys
}
func InitConfig(ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager {
@@ -112,7 +126,6 @@ func InitConfig(ConfigFile string, Datadir string, EnvPrefix string) *ethutil.Co
func exit(err error) {
status := 0
if err != nil {
- fmt.Println(err)
logger.Errorln("Fatal: ", err)
status = 1
}
@@ -176,7 +189,7 @@ func DefaultAssetPath() string {
// assume a debug build and use the source directory as
// asset directory.
pwd, _ := os.Getwd()
- if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") {
+ if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "mist") {
assetPath = path.Join(pwd, "assets")
} else {
switch runtime.GOOS {
@@ -185,7 +198,7 @@ func DefaultAssetPath() string {
exedir, _ := osext.ExecutableFolder()
assetPath = filepath.Join(exedir, "../Resources")
case "linux":
- assetPath = "/usr/share/ethereal"
+ assetPath = "/usr/share/mist"
case "windows":
assetPath = "./assets"
default: