summaryrefslogtreecommitdiffstats
path: root/qx08/source
diff options
context:
space:
mode:
authorTobi Oetiker <tobi@oetiker.ch>2008-11-04 11:14:06 +0100
committerTobi Oetiker <tobi@oetiker.ch>2008-11-04 11:14:06 +0100
commit996d9e92e597bc462d874b335865ff7844e2d332 (patch)
tree136211ddffb3eebacf540783a82bd7e31bc34f9b /qx08/source
parent2bb54a7dd3145c431b0e63cfd83df37f9a4b40d4 (diff)
downloadsmokeping-996d9e92e597bc462d874b335865ff7844e2d332.tar.gz
smokeping-996d9e92e597bc462d874b335865ff7844e2d332.tar.xz
added 0.8 port
Diffstat (limited to 'qx08/source')
-rw-r--r--qx08/source/class/tr/Application.js84
-rw-r--r--qx08/source/class/tr/Server.js72
-rw-r--r--qx08/source/class/tr/test/DemoTest.js55
-rw-r--r--qx08/source/class/tr/ui/ActionButton.js196
-rw-r--r--qx08/source/class/tr/ui/Cellrenderer.js46
-rw-r--r--qx08/source/class/tr/ui/Footer.js45
-rw-r--r--qx08/source/class/tr/ui/TraceTable.js214
-rw-r--r--qx08/source/index.html8
-rw-r--r--qx08/source/resource/tr/test.pngbin0 -> 2478 bytes
9 files changed, 720 insertions, 0 deletions
diff --git a/qx08/source/class/tr/Application.js b/qx08/source/class/tr/Application.js
new file mode 100644
index 0000000..7113b14
--- /dev/null
+++ b/qx08/source/class/tr/Application.js
@@ -0,0 +1,84 @@
+/* ************************************************************************
+
+ Copyright: OETIKER+PARTNER AG
+
+ License: Gnu GPL Verrsion 3
+
+ Authors: Tobias Oetiker <tobi@oetiker.ch>
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#asset(tr/*)
+
+************************************************************************ */
+
+/**
+ * This is the main application class of your custom application "qx08"
+ */
+qx.Class.define("tr.Application",
+{
+ extend : qx.application.Standalone,
+
+ /*
+ *****************************************************************************
+ MEMBERS
+ *****************************************************************************
+ */
+
+ members :
+ {
+ /**
+ * This method contains the initial application code and gets called
+ * during startup of the application
+ */
+ main : function()
+ {
+ var self=this;
+ // Call super class
+ this.base(arguments);
+
+ // Enable logging in debug variant
+ if (qx.core.Variant.isSet("qx.debug", "on"))
+ {
+ // support native logging capabilities, e.g. Firebug for Firefox
+ qx.log.appender.Native;
+ // support additional cross-browser console. Press F7 to toggle visibility
+ qx.log.appender.Console;
+ }
+
+ /*
+ -------------------------------------------------------------------------
+ Below is your actual application code...
+ -------------------------------------------------------------------------
+ */
+
+
+ // if we run with a file:// url make sure
+ // the app finds the Tr service (Tr.cgi)
+ Tr.Server.getInstance().setLocalUrl(
+ 'http://johan.oetiker.ch/~oetiker/tr/'
+ );
+ var root=this.getRoot();
+ // Document is the application root
+ var root = new qx.ui.container.Composite(new qx.ui.layout.VBox());
+ this.getRoot().add(root, { left : 0, top: 0});
+
+
+
+ var top = new qx.ui.container.Composite(new qx.ui.layout.HBox());
+ var title = new qx.ui.basic.Atom('SmokeTrace 2.4.2');
+ with(title){
+ setTextColor('#b0b0b0');
+ setFont(qx.bom.Font.fromString('20px bold sans-serif'));
+ }
+ top.add(title);
+ top.add(new qx.ui.basic.HorizontalSpacer());
+ top.add(new Tr.ui.ActionButton());
+ root.add(top);
+ var trace = new Tr.ui.TraceTable();
+ root.add(trace);
+ root.add(new Tr.ui.Footer(this.tr("SmokeTrace is part of the of the SmokePing suite created by Tobi Oetiker, Copyright 2008."),'http://oss.oetiker.ch/smokeping'));
+ }
+});
diff --git a/qx08/source/class/tr/Server.js b/qx08/source/class/tr/Server.js
new file mode 100644
index 0000000..89496f1
--- /dev/null
+++ b/qx08/source/class/tr/Server.js
@@ -0,0 +1,72 @@
+/* ************************************************************************
+#module(Tr)
+************************************************************************ */
+
+/**
+ * A Tr specific rpc call which works
+ */
+
+qx.Class.define('Tr.Server', {
+ extend: qx.io.remote.Rpc,
+ type: "singleton",
+
+ /*
+ *****************************************************************************
+ CONSTRUCTOR
+ *****************************************************************************
+ */
+
+ /**
+ * @param local_url {String} When running the application in file:// mode.
+ * where will we find our RPC server.
+ */
+ construct: function (local_url) {
+ this.base(arguments);
+ this.set({
+ timeout: 7000000,
+ url: 'tr.cgi',
+ serviceName: 'Tr',
+ crossDomain: true
+ });
+ return this;
+ },
+
+ /*
+ *****************************************************************************
+ MEMBERS
+ *****************************************************************************
+ */
+
+ members :
+ {
+
+ /*
+ ---------------------------------------------------------------------------
+ CORE METHODS
+ ---------------------------------------------------------------------------
+ */
+
+ /**
+ * Tell about the BaseUrl we found.
+ *
+ * @type member
+ *
+ * @param {void}
+ *
+ * @return BaseUrl {Strings}
+ */
+
+ getBaseUrl: function(){
+ return this.__base_url;
+ },
+
+ setLocalUrl: function(local_url){
+ if ( document.location.host === '' ) {
+ with(this){
+ setUrl(local_url+'tr.cgi');
+ }
+ }
+ }
+
+ }
+});
diff --git a/qx08/source/class/tr/test/DemoTest.js b/qx08/source/class/tr/test/DemoTest.js
new file mode 100644
index 0000000..76ce685
--- /dev/null
+++ b/qx08/source/class/tr/test/DemoTest.js
@@ -0,0 +1,55 @@
+/* ************************************************************************
+
+ Copyright:
+
+ License:
+
+ Authors:
+
+************************************************************************ */
+
+/**
+ * This class demonstrates how to define unit tests for your application.
+ *
+ * Execute <code>generate.py test</code> to generate a testrunner application
+ * and open it from <tt>test/index.html</tt>
+ *
+ * The methods that contain the tests are instance methods with a
+ * <code>test</code> prefix. You can create an arbitrary number of test
+ * classes like this one. They can be organized in a regular class hierarchy,
+ * i.e. using deeper namespaces and a corresponding file structure within the
+ * <tt>test</tt> folder.
+ */
+qx.Class.define("tr.test.DemoTest",
+{
+ extend : qx.dev.unit.TestCase,
+
+ members :
+ {
+ /*
+ ---------------------------------------------------------------------------
+ TESTS
+ ---------------------------------------------------------------------------
+ */
+
+ /**
+ * Here are some simple tests
+ */
+ testSimple : function()
+ {
+ this.assertEquals(4, 3+1, "This should never fail!");
+ this.assertFalse(false, "Can false be true?!");
+ },
+
+ /**
+ * Here are some more advanced tests
+ */
+ testAdvanced: function ()
+ {
+ var a = 3;
+ var b = a;
+ this.assertIdentical(a, b, "A rose by any other name is still a rose");
+ this.assertInRange(3, 1, 10, "You must be kidding, 3 can never be outside [1,10]!");
+ }
+ }
+});
diff --git a/qx08/source/class/tr/ui/ActionButton.js b/qx08/source/class/tr/ui/ActionButton.js
new file mode 100644
index 0000000..5d6eb5d
--- /dev/null
+++ b/qx08/source/class/tr/ui/ActionButton.js
@@ -0,0 +1,196 @@
+/* ************************************************************************
+#module(Tr)
+************************************************************************ */
+
+/**
+ * a widget showing the Tr graph overview
+ */
+
+qx.Class.define('Tr.ui.ActionButton',
+{
+ extend: qx.ui.Containter.
+
+ /*
+ *****************************************************************************
+ CONSTRUCTOR
+ *****************************************************************************
+ */
+
+ construct: function () {
+ this.base(arguments);
+
+ this.set({
+ height: 'auto',
+ width: 'auto',
+ horizontalChildrenAlign: 'left'
+ });
+ var hbox = new qx.ui.layout.HorizontalBoxLayout;
+ hbox.set({
+ height: 'auto',
+ width: 'auto',
+ verticalChildrenAlign: 'middle'
+ });
+ var lab1 = new qx.ui.basic.Label(this.tr("Host"));
+ lab1.set({
+ paddingRight: 6
+ });
+ hbox.add(lab1);
+ var host = new qx.ui.form.TextField();
+ host.set({
+ width: 200,
+ height: 'auto',
+ border: 'dark-shadow',
+ padding: 1
+ });
+ hbox.add(host);
+ this.__host = host;
+ var lab2 = new qx.ui.basic.Label(this.tr("Delay"));
+ lab2.set({
+ paddingRight: 6,
+ paddingLeft: 12
+ });
+ hbox.add(lab2);
+ var delay = new qx.ui.form.Spinner(1,2,60);
+ delay.set({
+ border: 'dark-shadow',
+ width: 45
+ });
+ hbox.add(delay);
+ this.__delay = delay;
+
+ var lab3 = new qx.ui.basic.Label(this.tr("Rounds"));
+ lab3.set({
+ paddingRight: 6,
+ paddingLeft: 12
+ });
+ hbox.add(lab3);
+ var rounds = new qx.ui.form.Spinner(1,20,200);
+ rounds.set({
+ border: 'dark-shadow',
+ width: 45
+ });
+ hbox.add(rounds);
+ this.__rounds = rounds;
+
+ var button = new qx.ui.form.Button('');
+ this.__button = button;
+ button.set({
+ marginLeft: 10,
+ width: 60,
+ height: 'auto',
+ border: 'dark-shadow',
+ padding: 2
+ });
+ hbox.add(button);
+ this.add(hbox);
+ var info = new qx.ui.basic.Atom();
+ info.set({
+ marginTop: 3,
+ padding: 3,
+ textColor: 'red',
+ width: '100%',
+ height: 'auto',
+ backgroundColor: '#f0f0f0',
+ visibility: false
+ });
+ qx.event.message.Bus.subscribe('tr.info',this.__set_info,this);
+ this.add(info);
+ this.__info = info;
+
+ qx.event.message.Bus.subscribe('tr.status',this.__set_status,this);
+ qx.event.message.Bus.dispatch('tr.status','stopped');
+
+ var start_trace = function(event) {
+ qx.event.message.Bus.dispatch('tr.cmd',{
+ action: button.getUserData('action'),
+ host: host.getValue(),
+ delay: delay.getValue(),
+ rounds: rounds.getValue()
+ });
+ };
+
+// var self=this;
+// host.addEventListener('keyup',function(e){if(e.getKeyIdentifier() == 'Enter'){start_trace()}});
+ host.addEventListener('execute',start_trace);
+ button.addEventListener('execute', start_trace );
+
+ var history = qx.client.History.getInstance();
+ var history_action = function(event){
+ var targ = event.getData();
+ host.setValue(targ);
+ history.addToHistory(targ,'SmokeTrace to '+targ);
+ start_trace();
+ }
+ history.addEventListener('request', history_action);
+
+ // if we got called with a host on the commandline
+ var initial_host = qx.client.History.getInstance().getState();
+ if (initial_host){
+ host.setValue(initial_host);
+ history.addToHistory(initial_host,'SmokeTrace to '+initial_host);
+ // dispatch this task once all the initializations are done
+ qx.client.Timer.once(start_trace,this,0);
+ }
+ },
+
+ members: {
+ __set_info: function(e){
+ this.__info.set({
+ label: e.getData(),
+ visibility: true
+ });
+ },
+ __set_status: function(m){
+ var host = this.__host;
+ var rounds = this.__rounds;
+ var delay = this.__delay;
+ with(this.__button){
+ // this.debug(m.getData());
+ switch(m.getData()){
+ case 'starting':
+ if (getUserData('action') == 'go') {
+ setLabel(this.tr("Starting"));
+ this.__info.setVisibility(false);
+ border: 'dark-shadow'
+ setEnabled(false);
+ host.setEnabled(false);
+ rounds.setEnabled(false);
+ delay.setEnabled(false);
+ }
+ break;
+ case 'stopping':
+ if (getUserData('action') == 'stop') {
+ setLabel(this.tr("Stopping"));
+ setEnabled(false);
+ host.setEnabled(false);
+ rounds.setEnabled(false);
+ delay.setEnabled(false);
+ }
+ break;
+ case 'stopped':
+ setUserData('action','go');
+ setLabel(this.tr("Go"));
+ setEnabled(true);
+ host.setEnabled(true);
+ rounds.setEnabled(true);
+ delay.setEnabled(true);
+ break;
+ case 'started':
+ setUserData('action','stop');
+ setLabel(this.tr("Stop"));
+ setEnabled(true);
+ host.setEnabled(false);
+ rounds.setEnabled(false);
+ delay.setEnabled(false);
+ break;
+ default:
+ alert('Unknown Status Message: '+m.getData());
+ }
+ }
+ }
+ }
+
+
+});
+
+
diff --git a/qx08/source/class/tr/ui/Cellrenderer.js b/qx08/source/class/tr/ui/Cellrenderer.js
new file mode 100644
index 0000000..f18672d
--- /dev/null
+++ b/qx08/source/class/tr/ui/Cellrenderer.js
@@ -0,0 +1,46 @@
+/* ************************************************************************
+
+ Tr Frontend
+
+ Author:
+ * Tobias Oetiker
+
+************************************************************************ */
+/* ************************************************************************
+#module(Tr)
+************************************************************************ */
+
+/**
+ * A configurable cell renderre
+ */
+
+qx.Class.define('Tr.ui.Cellrenderer',
+{
+ extend: qx.ui.table.cellrenderer.Number,
+ /**
+ * Format a number with a configurable number of fraction digits
+ * and add optional pre and postfix.
+ * @param digits {Integer} how many digits should there be. Default is 0.
+ * @param prefix {String} optional prefix.
+ * @param postfix {String} optional postfix.
+ */
+
+ construct: function (digits,postfix,prefix) {
+ if (digits == undefined){
+ digits = 0;
+ }
+ this.base(arguments)
+ var format = new qx.util.format.NumberFormat();
+ format.set({
+ maximumFractionDigits: digits,
+ minimumFractionDigits: digits
+ });
+ if (postfix != undefined){
+ format.setPostfix(postfix);
+ }
+ if (prefix != undefined){
+ format.setPrefix(prefix);
+ }
+ this.setNumberFormat(format);
+ }
+});
diff --git a/qx08/source/class/tr/ui/Footer.js b/qx08/source/class/tr/ui/Footer.js
new file mode 100644
index 0000000..1882043
--- /dev/null
+++ b/qx08/source/class/tr/ui/Footer.js
@@ -0,0 +1,45 @@
+/* ************************************************************************
+#module(Tr)
+************************************************************************ */
+
+/**
+ * a widget showing the footer
+ */
+
+qx.Class.define('Tr.ui.Footer',
+{
+ extend: qx.ui.layout.HorizontalBoxLayout,
+
+ /*
+ *****************************************************************************
+ CONSTRUCTOR
+ *****************************************************************************
+ */
+
+ construct: function (text,url) {
+ this.base(arguments);
+ this.set({
+ horizontalChildrenAlign: 'right',
+ height: 'auto'
+ });
+ var logo = new qx.ui.form.Button(text);
+ logo.set({
+ textColor: '#b0b0b0',
+ backgroundColor: null,
+ font: qx.ui.core.Font.fromString('10px sans-serif'),
+ border: null
+ });
+
+ logo.addEventListener('execute', function(e){
+ var w = new qx.client.NativeWindow(url);
+ w.set({
+ width: 1000,
+ height: 800
+ });
+ w.open()
+ });
+ this.add(logo);
+ }
+});
+
+
diff --git a/qx08/source/class/tr/ui/TraceTable.js b/qx08/source/class/tr/ui/TraceTable.js
new file mode 100644
index 0000000..446cbad
--- /dev/null
+++ b/qx08/source/class/tr/ui/TraceTable.js
@@ -0,0 +1,214 @@
+/* ************************************************************************
+#module(Tr)
+************************************************************************ */
+
+/**
+ * a widget showing the Tr target tree
+ */
+
+qx.Class.define('Tr.ui.TraceTable',
+{
+ extend: qx.ui.table.Table,
+
+ /*
+ *****************************************************************************
+ CONSTRUCTOR
+ *****************************************************************************
+ */
+
+
+ construct: function () {
+
+ var tableModel = new qx.ui.table.model.Simple();
+ this.__tableModel = tableModel;
+ tableModel.setColumns([ this.tr("Hop"), this.tr("Host"),this.tr("Ip"),
+ this.tr("Loss [%]"), this.tr("Sent"), this.tr("Last [ms]"), //"; help syntax highliter
+ this.tr("Avg [ms]"), this.tr("Best [ms]"), this.tr("Worst [ms]"), this.tr("StDev [ms]") ]);
+ var custom = {
+ tableColumnModel: function(obj) {
+ return new qx.ui.table.columnmodel.Resize(obj);
+ }
+ };
+ with(this){
+ base(arguments,tableModel,custom);
+ set({
+ width: '100%',
+ height: '1*',
+ border: 'dark-shadow',
+ showCellFocusIndicator: false,
+ statusBarVisible: false
+ });
+ };
+ var tcm = this.getTableColumnModel();
+ this.__tcm = tcm;
+
+ tcm.setDataCellRenderer(0, new Tr.ui.Cellrenderer(1));
+ tcm.setDataCellRenderer(3, new Tr.ui.Cellrenderer(0,' %'));
+ tcm.setDataCellRenderer(4, new Tr.ui.Cellrenderer(0));
+
+ var render_ms = new Tr.ui.Cellrenderer(1);
+
+ for (var i=5;i<10;i++){
+ tcm.setDataCellRenderer(i, render_ms);
+ }
+
+
+ // Obtain the behavior object to manipulate
+ var resizeBehavior = tcm.getBehavior();
+ // This uses the set() method to set all attriutes at once; uses flex
+ resizeBehavior.set(0, { width:"2*"});
+ resizeBehavior.set(1, { width:"9*"});
+ resizeBehavior.set(2, { width:"5*"});
+
+ for (var i=3;i<10;i++){
+ resizeBehavior.set(i, { width:"3*"});
+ }
+ qx.event.message.Bus.subscribe('tr.cmd',this.__handle_tr,this);
+ },
+
+ /*
+ *****************************************************************************
+ Statics
+ *****************************************************************************
+ */
+ members: {
+ __make_empty_row: function (){
+ return ([undefined,undefined,undefined,0,0,undefined,undefined,undefined,undefined,undefined,0,0,0]);
+ },
+ __stop_table: function (){
+ var tableModel = this.__tableModel;
+ for (var i=0;i<10;i++){
+ tableModel.setColumnSortable(i,true);
+ }
+ qx.event.message.Bus.dispatch('tr.status','stopped');
+ this.__handle = undefined;
+ },
+ __handle_tr: function(m){
+ var self = this;
+ var f_hop = 0,f_host=1,f_ip=2,f_loss=3,f_snt=4,f_last=5,f_avg=6,f_best=7,f_worst=8,f_stdev=9,f_cnt=10,f_sum=11,f_sqsum=12;
+ var fill_table;
+ fill_table = function(retval,exc,id){
+ if (exc == null){
+ if ( self.__handle == undefined ) {
+ qx.event.message.Bus.dispatch('tr.status','started');
+ }
+ self.__handle = retval['handle'];
+ var tableModel = self.__tableModel;
+ var lines = retval['output'].length;
+ var data = self.__data;
+ var sleep = 0;
+ for(var i=0;i<lines;i++){
+ sleep = 0;
+ var hop = retval['output'][i][0];
+ if (hop == 'SLEEP'){
+ sleep = retval['output'][i][1];
+ continue;
+ }
+ else if (hop == 'INFO'){
+ qx.event.message.Bus.dispatch('tr.info',retval['output'][i][1]);
+ continue;
+ }
+ var host = retval['output'][i][1];
+ var ip = retval['output'][i][2];
+ var value = retval['output'][i][3];
+ var ii = 0;
+ var max = data.length;
+ while (true){
+ if ( ii == max ) break;
+ if ( Math.floor(data[ii][0]) > hop) break;
+ if ( Math.floor(data[ii][0]) == hop ){
+ if ( ip == undefined ) break;
+ if ( ip == data[ii][2] ) break;
+ }
+ ii++;
+ }
+ if (ii == max || Math.floor(data[ii][0]) > hop ){
+ if (ii > 0 && Math.floor(data[ii-1][0]) == hop ){
+ hop = data[ii-1][0] + 0.1;
+ }
+ data.splice(ii,0,self.__make_empty_row());
+ data[ii][0] = hop;
+ }
+
+ var drow = data[ii];
+ if (drow[f_host] == undefined && host != undefined){
+ drow[f_host] = host;
+ }
+ if (drow[f_ip] == undefined && ip != undefined){
+ drow[f_ip] = ip;
+ }
+ drow[f_snt]++;
+ drow[f_last] = value;
+
+
+ if (value != undefined){
+ var best = drow[f_best];
+ if (best == undefined || best > value){
+ drow[f_best] = value;
+ }
+ var worst = drow[f_worst];
+ if (worst == undefined || worst < value){
+ drow[f_worst] = value;
+ }
+ drow[f_sum] += value;
+ var sum = drow[f_sum];
+ drow[f_cnt] ++;
+ var cnt = drow[f_cnt];
+ var sqsum = drow[f_sqsum]+value*value;
+ drow[f_sqsum] = sqsum;
+ drow[f_avg] = drow[f_sum]/drow[f_cnt];
+ drow[f_stdev] = Math.sqrt((cnt*sqsum-sum*sum)/(cnt*(cnt-1)))
+ }
+ drow[f_loss] = ((drow[f_snt]-drow[f_cnt])/drow[f_snt])*100;
+ }
+
+ tableModel.setData(data);
+ if (retval['again']){
+ var next_round = function (){Tr.Server.getInstance().callAsync(
+ fill_table,'run_tr',{ handle: retval['handle'],
+ point: retval['point']})};
+ qx.client.Timer.once(next_round,self,sleep*1000);
+ }
+ else {
+ self.__stop_table();
+ }
+ }
+ else {
+ alert(exc);
+ self.__stop_table();
+ }
+ };
+
+ var stop_handler = function (data,exc,id){
+ if (exc == null){
+ qx.event.message.Bus.dispatch('tr.status','stopped');
+ }
+ else {
+ alert(exc);
+ }
+ };
+
+ var cmd = m.getData();
+ switch(cmd['action']){
+ case 'stop':
+ qx.event.message.Bus.dispatch('tr.status','stopping');
+ Tr.Server.getInstance().callAsync(stop_handler,'stop_tr',this.__handle);
+ break;
+ case 'go':
+ this.__data = [];
+ this.__tableModel.setData(this.__data);
+ this.__delay = cmd['delay'];
+ for (var i=0;i<10;i++){
+ this.__tableModel.setColumnSortable(i,false);
+ }
+ qx.event.message.Bus.dispatch('tr.status','starting');
+ Tr.Server.getInstance().callAsync(fill_table,'run_tr',{host: cmd['host'], rounds: cmd['rounds'], delay: cmd['delay']});
+ break;
+ default:
+ alert('Unknown Command '+cmd['action']);
+ }
+ }
+ }
+});
+
+
diff --git a/qx08/source/index.html b/qx08/source/index.html
new file mode 100644
index 0000000..c935564
--- /dev/null
+++ b/qx08/source/index.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>qx08</title>
+ <script type="text/javascript" src="script/tr.js"></script>
+</head>
+</html>
diff --git a/qx08/source/resource/tr/test.png b/qx08/source/resource/tr/test.png
new file mode 100644
index 0000000..ef360cd
--- /dev/null
+++ b/qx08/source/resource/tr/test.png
Binary files differ