summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-09-25 13:24:11 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-09-25 13:24:11 (GMT)
commit567df9318fff6d1bb570191c33ea68cd6ef88bee (patch)
tree72e310c422cea117abe859a6bddf0b613a2db589
parentbe3c180fec71866a91b5f9297708d581bc1d6435 (diff)
downloaduscxml-567df9318fff6d1bb570191c33ea68cd6ef88bee.zip
uscxml-567df9318fff6d1bb570191c33ea68cd6ef88bee.tar.gz
uscxml-567df9318fff6d1bb570191c33ea68cd6ef88bee.tar.bz2
More work on IMInvoker, renamed Blob attributes and some XPath datamodel fixes
-rw-r--r--apps/samples/map/SpatialMapTicker.java160
-rw-r--r--apps/samples/map/click.wavbin0 -> 5058 bytes
-rw-r--r--apps/samples/map/map.html30
-rw-r--r--apps/samples/map/map.js158
-rw-r--r--apps/samples/map/spoken-map-ticker.scxml78
-rw-r--r--apps/samples/map/spoken-map-ticker.xhtml240
-rw-r--r--src/uscxml/Convenience.h33
-rw-r--r--src/uscxml/Factory.h26
-rw-r--r--src/uscxml/Message.cpp24
-rw-r--r--src/uscxml/Message.h16
-rw-r--r--src/uscxml/interpreter/InterpreterDraft6.cpp3
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp28
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/TypedArray.h20
-rw-r--r--src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp62
-rw-r--r--src/uscxml/plugins/element/file/FileElement.cpp4
-rw-r--r--src/uscxml/plugins/element/respond/RespondElement.cpp2
-rw-r--r--src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp12
-rw-r--r--src/uscxml/plugins/invoker/im/IMInvoker.cpp697
-rw-r--r--src/uscxml/plugins/invoker/im/IMInvoker.h179
-rw-r--r--test/samples/uscxml/applications/3dviewer.scxml84
-rw-r--r--test/samples/uscxml/test-instant-messaging.scxml18
21 files changed, 1409 insertions, 465 deletions
diff --git a/apps/samples/map/SpatialMapTicker.java b/apps/samples/map/SpatialMapTicker.java
new file mode 100644
index 0000000..9947ffb
--- /dev/null
+++ b/apps/samples/map/SpatialMapTicker.java
@@ -0,0 +1,160 @@
+/**
+ * Compile as:
+ * $ javac -cp .:/usr/local/share/umundo/java/umundo.jar SpatialMapTicker.java
+ *
+ * Run as:
+ * $ java -cp .:/usr/local/share/umundo/java/umundo.jar SpatialMapTicker
+ */
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.Random;
+
+import org.umundo.core.Greeter;
+import org.umundo.core.Message;
+import org.umundo.core.Node;
+import org.umundo.core.Publisher;
+
+public class SpatialMapTicker {
+
+ Node node;
+ Publisher pub;
+ Greeter greeter;
+ ArrayList<Sensor> sensors = new ArrayList<Sensor>();
+ static ArrayList<SensorMessage> messages = new ArrayList<SensorMessage>();
+ static Random random = new Random(System.currentTimeMillis());
+
+ public class SensorMessage {
+ public String message;
+ public int severity;
+ public SensorMessage(String message, int severity) {
+ this.message = message;
+ this.severity = severity;
+ }
+ public SensorMessage(String message) {
+ this.message = message;
+ this.severity = 3;
+ }
+ }
+
+ public class Sensor {
+ @Override
+ public String toString() {
+ return "Sensor [id=" + id + ", lat=" + lat + ", lon=" + lon
+ + ", html=" + getHTML() + "]";
+ }
+ public String id = "";
+ public Double lat = new Double(0);
+ public Double lon = new Double(0);
+ LinkedList<SensorMessage> messages = new LinkedList<SensorMessage>();
+
+ public void addMessage(SensorMessage message) {
+ if (messages.size() > 15)
+ messages.removeLast();
+ messages.addFirst(message);
+ }
+
+ public String getHTML() {
+ StringBuilder sb = new StringBuilder();
+ for (SensorMessage message : messages) {
+ sb.append(message.severity);
+ sb.append(": ");
+ sb.append(message.message);
+ sb.append("<br />");
+ }
+ return sb.toString();
+ }
+ }
+
+ public class SensorGreeter extends Greeter {
+ public void welcome(Publisher publisher, String nodeId, String subId) {
+ // send all sensors to new subscribers
+ for (Sensor sensor : sensors) {
+ Message msg = new Message(); //Message.toSubscriber(subId);
+ msg.putMeta("id", sensor.id);
+ msg.putMeta("lat", sensor.lat.toString());
+ msg.putMeta("lon", sensor.lon.toString());
+ msg.putMeta("html", sensor.getHTML());
+ pub.send(msg);
+ }
+ }
+
+ @Override
+ public void farewell(Publisher arg0, String nodeId, String subId) {
+ }
+
+ }
+
+ public SpatialMapTicker() {
+ node = new Node();
+ pub = new Publisher("map/tick");
+ greeter = new SensorGreeter();
+ pub.setGreeter(greeter);
+ node.addPublisher(pub);
+
+ double latCenter = 59.32;
+ double lonCenter = 18.08;
+
+ int nrSensors = 15; //(int) (Math.random() * 20);
+ for (int i = 0; i < nrSensors; i++) {
+ Sensor sensor = new Sensor();
+ double latOffset = (Math.random() - 0.5) * 0.3;
+ double lonOffset = (Math.random() - 0.5) * 0.3;
+
+ sensor.id = "Sensor #" + i;
+ sensor.lat = latCenter + latOffset;
+ sensor.lon = lonCenter + lonOffset;
+ sensors.add(sensor);
+ }
+ }
+
+ public static void main(String[] args) {
+ SpatialMapTicker ticker = new SpatialMapTicker();
+ ticker.run();
+ }
+
+ private void run() {
+ messages.add(new SensorMessage("Oil pressure threshold exceeded"));
+ messages.add(new SensorMessage("Equipment is on fire"));
+ messages.add(new SensorMessage("Error #32 in diagnostics unit"));
+ messages.add(new SensorMessage("Unauthorized startup"));
+ messages.add(new SensorMessage("Tire pressure too low"));
+ messages.add(new SensorMessage("Error #145 in diagnostics unit"));
+ messages.add(new SensorMessage("Unit was moved out of construction site area"));
+ messages.add(new SensorMessage("Hydraulic pressure exceeding safety limits"));
+ messages.add(new SensorMessage("Drivers seat belts are not fastened!"));
+ messages.add(new SensorMessage("Battery recharge cycles exceeded"));
+ messages.add(new SensorMessage("Unit operated outside recommended paramters"));
+
+ while (true) {
+ try {
+ Thread.sleep((long) (Math.random() * 300) + 100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ Sensor sensor = sensors.get(random.nextInt(sensors.size()));
+ SensorMessage fault = messages.get(random.nextInt(messages.size()));
+
+ Date now = new Date();
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss z");
+ String nowString = sdf.format(now);
+ sensor.addMessage(fault);
+
+// System.out.println("Publishing " + sensor);
+
+ Message msg = new Message();
+ msg.putMeta("id", sensor.id);
+ msg.putMeta("lat", sensor.lat.toString());
+ msg.putMeta("lon", sensor.lon.toString());
+ msg.putMeta("html", sensor.getHTML());
+ msg.putMeta("time", nowString);
+ msg.putMeta("timeStamp", Long.toString(now.getTime()));
+ msg.putMeta("message", sensor.messages.getFirst().message);
+ msg.putMeta("severity", Integer.toString(random.nextInt(10)));
+ pub.send(msg);
+ }
+ }
+
+}
diff --git a/apps/samples/map/click.wav b/apps/samples/map/click.wav
new file mode 100644
index 0000000..e11b0b7
--- /dev/null
+++ b/apps/samples/map/click.wav
Binary files differ
diff --git a/apps/samples/map/map.html b/apps/samples/map/map.html
deleted file mode 100644
index 01dc92b..0000000
--- a/apps/samples/map/map.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
- <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dijit/themes/tundra/tundra.css">
- <!-- link rel="stylesheet" href="map.css" -->
- <script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>
- <style type="text/css">
- </style>
-
- <script type="text/javascript">
- </script>
-
- <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script>
- <script type="text/javascript" src="map.js"></script>
-
- <script type="text/javascript">
- require(["dojo/domReady!", "dojo"], function(dom, dojo) {
- var map = new Map("map");
- });
- </script>
- </head>
- <body class="tundra">
- <table>
- <tr><td>
- <div id="map" />
- </td></tr>
- </table>
- </body>
-</html>
diff --git a/apps/samples/map/map.js b/apps/samples/map/map.js
deleted file mode 100644
index 6794875..0000000
--- a/apps/samples/map/map.js
+++ /dev/null
@@ -1,158 +0,0 @@
-function Map(element) {
-
- // private attributes
- var self = this;
-
- // private instanceId
- if (!Map.instances)
- Map.instances = 0;
- var instanceId = Map.instances++;
-
- // public attributes
- this.coords = {};
-
-
- require([
- "dojo/ready",
- "dojo/dom-construct",
- "dojo/_base/window",
- "dojo/dom",
- "dojox/geo/openlayers/Map",
- "dojox/geo/openlayers/GfxLayer",
- "dojox/geo/openlayers/Layer",
- "dojox/geo/openlayers/GeometryFeature",
- "dojox/geo/openlayers/Point",
- "dojox/geo/openlayers/LineString",
- "dojox/geo/openlayers/WidgetFeature",
- "dojox/charting/widget/Chart",
- "dojox/charting/widget/Chart2D",
- "dojox/charting/plot2d/Pie",
- "dojox/charting/themes/PlotKit/blue"
- ], function(
- ready,
- domConstruct,
- win,
- dom,
- Map,
- GfxLayer,
- Layer,
- GeometryFeature,
- Point,
- LineString,
- WidgetFeature,
- Chart,
- Chart2D,
- Pie,
- blue
- ) {
- ready(function(){
-
- if (typeof(element) === 'string') {
- element = dom.byId(element);
- }
-
- element.style.width = "450px";
- element.style.height = "350px";
-
- var options = {
- baseLayerName: "WorldMap",
- baseLayerType : dojox.geo.openlayers.BaseLayerType.OSM,
- //baseLayerUrl: "http://localhost/mapserver/mapserv.cgi?map=./world.map",
- baseLayerOptions: {
- layers: ['contry','state','city','town','highway']
- },
- touchHandler: false,
- accessible: true
- };
-
- // Available base layers: http://dojotoolkit.org/reference-guide/1.7/dojox/geo/openlayers.html#id5
- self.map = new Map(element, options);
-
- // This is New York location
- var ny = {
- latitude : 49.877648,
- longitude : 8.654762
- };
-
- var people = [ {
- name : 'Dirk',
- y : 49.877848,
- x : 8.653762
- }, {
- name : 'Stefan',
- y : 49.877348,
- x : 8.655462
- } ];
-
- var div = domConstruct.create("div", {}, win.body());
- //var div = domConstruct.create("div", {});
- var chart = new Chart({
- margins : {
- l : 0,
- r : 0,
- t : 0,
- b : 0
- }
- }, div);
-
- var c = chart.chart;
- c.addPlot("default", {
- type : "Pie",
- radius : 50,
- labelOffset : 100,
- fontColor : "black",
- fontSize : 20
- });
-
- var ser = [ 2, 8, 12, 3 ];
- c.addSeries("Series", ser);
- c.setTheme(blue);
- c.render();
- c.theme.plotarea.fill = undefined;
-
- var descr = {
- longitude : ny.longitude,
- latitude : ny.latitude,
- widget : chart,
- width : 120,
- height : 120
- };
- feat3 = new WidgetFeature(descr);
-
- // create a GfxLayer
- var layer = new GfxLayer();
-
- var point = new Point({
- x:ny.longitude,
- y:ny.latitude
- });
- // create a GeometryFeature
- var feat = new GeometryFeature(point);
- // set the shape properties, fill and stroke
- feat.setFill([ 0, 128, 128 ]);
- feat.setStroke([ 0, 0, 0 ]);
- feat.setShapeProperties({
- r : 8
- });
-
- var pts = new LineString(people);
- // create a GeometryFeature
- var feat2 = new GeometryFeature(pts);
- // set the shape stroke property
- feat2.setStroke([ 0, 0, 0 ]);
-
- // add the feature to the layer
- layer.addFeature(feat);
- layer.addFeature(feat2);
- layer.addFeature(feat3);
- // add layer to the map
- self.map.addLayer(layer);
-
- // fit to New York with 0.1 degrees extent
- self.map.fitTo({
- position : [ ny.longitude, ny.latitude ],
- extent : 0.001
- });
- });
- });
-}
diff --git a/apps/samples/map/spoken-map-ticker.scxml b/apps/samples/map/spoken-map-ticker.scxml
new file mode 100644
index 0000000..147f890
--- /dev/null
+++ b/apps/samples/map/spoken-map-ticker.scxml
@@ -0,0 +1,78 @@
+<scxml datamodel="ecmascript" name="mapticker"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:vxml="http://www.w3.org/2001/vxml"
+ xmlns="http://www.w3.org/2005/07/scxml">
+
+ <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
+
+ <state id="start">
+ <!--
+ Invoke an external HTML browser
+ -->
+ <invoke type="xhtml" id="xhtml1">
+ <content src="spoken-map-ticker.xhtml" />
+ <finalize>
+ <!-- <script>dump(_event);</script> -->
+ </finalize>
+ </invoke>
+
+ <!--
+ Invoke a VoiceXML browser
+ -->
+ <!-- invoke type="vxml" id="vxml" / -->
+
+ <!--
+ Invoke a uMundo component for pub/sub ticker subscription
+ -->
+ <invoke type="umundo" id="umundo">
+ <param name="channel" expr="'map/tick'" />
+ <finalize>
+ <script>dump(_event);</script>
+ </finalize>
+ </invoke>
+
+ <invoke type="openal" id="audio"></invoke>
+
+ <state id="idle">
+ <!--
+ Map in XHTML invoker was moved -> update OpenAL listener position
+ -->
+ <transition event="map.center" target="idle">
+ <send event="move.listener" target="#_audio">
+ <param name="x" expr="_event.data.lon" />
+ <param name="y" expr="1" />
+ <param name="z" expr="_event.data.lat" />
+ </send>
+ </transition>
+
+ <!--
+ Ticker message was received
+ -> Send to XHTML invoker to display
+ -> Play clicking noise from respective direction
+ -> Speak content if severity exceeds a given threshold
+ -->
+ <transition target="idle" event="umundo.rcvd">
+ <send target="#_xhtml1">
+ <content>${_event.data}</content>
+ </send>
+ <send target="#_audio" event="play" delay="0ms">
+ <param name="src" expr="'click.wav'" />
+ <param name="x" expr="_event.data.lon" />
+ <param name="y" expr="0" />
+ <param name="z" expr="_event.data.lat" />
+ </send>
+
+ <if cond="_event.data.message &amp;&amp; _event.data.severity &gt; 8">
+ <send target="#_vxml">
+ <content>
+ <vxml:prompt>
+ ${_event.data.message}
+ </vxml:prompt>
+ </content>
+ </send>
+ </if>
+
+ </transition>
+ </state>
+ </state>
+</scxml> \ No newline at end of file
diff --git a/apps/samples/map/spoken-map-ticker.xhtml b/apps/samples/map/spoken-map-ticker.xhtml
new file mode 100644
index 0000000..ace8724
--- /dev/null
+++ b/apps/samples/map/spoken-map-ticker.xhtml
@@ -0,0 +1,240 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <!-- space in between script is required as <script/> is not valid HTML to most browsers -->
+ <script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.js"> </script>
+ <script type="text/javascript" src="http://www.openlayers.org/api/OpenLayers.js"> </script>
+ <script type="text/javascript" src="http://epikur.local/~sradomski/CometSession.js"> </script>
+ <script type="text/javascript" src="https://raw.github.com/jchavannes/jquery-timer/master/jquery.timer.js"> </script>
+
+ <style type="text/css">
+ html, body, #map {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ }
+ .olPopup {
+ border-radius: 8px;
+ }
+ div.olControlOverviewMapElement {
+ background-color: #666;
+ }
+ div.olMap {
+ width: 180px;
+ height: 120px;
+ margin: 0;
+ }
+ div.timeRuler {
+ font-family:Courier,Arial,Helvetica,sans-serif;
+ font-size: 1.2em;
+ font-weight: bold;
+ background-color: #eee;
+ }
+ div#message {
+ overflow: auto;
+ height: 100%;
+ font-family:Courier,Arial,Helvetica,sans-serif;
+ font-size: 0.6em;
+ }
+
+ div#message p {
+ padding: 0px;
+ margin: 4px 2px;
+ }
+
+ </style>
+
+ <!-- script type="text/javascript" src="http://localhost/~sradomski/CometSession.js"></script -->
+ <script type="text/javascript">
+//<![CDATA[
+ $(document).ready(function() {
+ var scxml = new CometSession({
+ server: document.URL,
+ onEvent: function(response, message) {
+ if (message) {
+ console.log(message);
+
+ if (!markers[message.id]) {
+ addMarker(message);
+ } else {
+ for (i = 0; i < map.popups.length; i++) {
+ if (map.popups[i].feature.id === message.id) {
+ map.popups[i].setContentHTML("<h3>" + message.id + "</h3>" + message.html);
+ }
+ }
+ markers[message.id].marker.opacity = 1.0;
+ markers[message.id].marker.setOpacity(1.0);
+
+ markers[message.id].marker.timer.play();
+ markers[message.id].feature.popupContentHTML = "<h3>" + message.id + "</h3>" + message.html;
+ }
+
+ if (message.message && message.message.length > 0) {
+ var messageDiv = $("#message")[0];
+ if (message.timeStamp > lastUpdateTime + 3000) {
+ var timeRuler = document.createElement("div");
+ timeRuler.setAttribute("class", "timeRuler");
+ timeRuler.innerHTML = message.time; // + messageDiv.innerHTML;
+ messageDiv.appendChild(timeRuler);
+ lastUpdateTime = message.timeStamp;
+ }
+
+ console.log(message.timeStamp);
+ console.log(lastUpdateTime);
+
+ var messagePara = document.createElement("p");
+ if (message.severity > 7) {
+ messagePara.setAttribute("style", "color:red; font-weight:bold;");
+ }
+ messagePara.innerHTML += message.message; // + messageDiv.innerHTML;
+
+ messageDiv.appendChild(messagePara);
+ messagePara.addEventListener("mousedown", function() {
+ var marker = markers[message.id].marker;
+ marker.events.triggerEvent("mousedown");
+ });
+ messagePara.addEventListener("mouseover", function() {
+ messagePara.style.backgroundColor = "#eee";
+ });
+ messagePara.addEventListener("mouseout", function() {
+ messagePara.style.backgroundColor = "#fff";
+ });
+
+ if (!stopScrolling) {
+ messageDiv.scrollTop = messageDiv.scrollHeight;
+ }
+ }
+ }
+ }
+ });
+
+ // var map = new OpenLayers.Map("demoMap");
+ // map.addLayer(new OpenLayers.Layer.OSM());
+ // map.zoomToMaxExtent();
+
+ var markers = {};
+ var lastUpdateTime = 0;
+ var stopScrolling = false;
+
+ var fromProjection = new OpenLayers.Projection("EPSG:4326"); // Transform from WGS 1984
+ var toProjection = new OpenLayers.Projection("EPSG:900913"); // to Spherical Mercator Projection
+
+ function onMoveEvent() {
+ var position = map.getCenter().transform(toProjection, fromProjection);
+ if (Math.random() > 0.7) { // do not publish every move event
+ scxml.send('map.center', {
+ 'zoom': map.getZoom(),
+ 'lon': position.lon,
+ 'lat': position.lat });
+ }
+ }
+
+ function onMoveEndEvent() {
+ var position = map.getCenter().transform(toProjection, fromProjection);
+ scxml.send('map.center', {
+ 'zoom': map.getZoom(),
+ 'lon': position.lon,
+ 'lat': position.lat });
+ }
+
+ function addMarker(message) {
+ var feature = new OpenLayers.Feature(
+ markerLayer,
+ new OpenLayers.LonLat(message.lon, message.lat).transform(fromProjection, toProjection));
+ feature.closeBox = true;
+ feature.popupClass =  OpenLayers.Class(OpenLayers.Popup.Anchored, {
+ 'autoSize': true,
+// 'maxSize': new OpenLayers.Size(300,200)
+ });
+ feature.data.popupContentHTML = "<h3>" + message.id + "</h3>" + message.html;
+ feature.data.overflow = "auto";
+ feature.data.id = message.id;
+ var marker = feature.createMarker();
+
+ marker.timer = $.timer(function() {
+ if (marker.opacity > 0.5) {
+ marker.opacity -= 0.02;
+ marker.setOpacity(marker.opacity);
+ } else {
+ marker.timer.stop();
+ }
+ }, 100);
+
+ marker.opacity = 1.0;
+ marker.setOpacity(marker.opacity);
+ marker.timer.play();
+
+ markerClick = function(evt) {
+ if (this.popup == null) {
+ this.popup = this.createPopup(this.closeBox);
+ map.addPopup(this.popup);
+ this.popup.show();
+ } else {
+ this.popup.toggle();
+ }
+ currentPopup = this.popup;
+ OpenLayers.Event.stop(evt);
+ };
+
+ marker.events.register("mousedown", feature, markerClick);
+ markers[message.id] = {
+ 'marker': marker,
+ 'feature': feature
+ };
+ markerLayer.addMarker(marker);
+ }
+ // see also: http://php-baustelle.de/openlayers/step-by-step/
+ var map = new OpenLayers.Map('map', {
+ eventListeners: {
+ "move": onMoveEvent,
+ "moveend": onMoveEndEvent,
+ }
+ });
+
+ var mapnik = new OpenLayers.Layer.OSM();
+ var markerLayer = new OpenLayers.Layer.Markers("Markers");
+// var attribution = new OpenLayers.Layer.Vector("Attribution", {attribution:"for Smart Vortex"});
+
+ map.addLayer(mapnik);
+// map.addLayer(attribution);
+ map.addLayer(markerLayer); // has to be the last layer to receive events!
+
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ // see also http://dev.openlayers.org/releases/OpenLayers-2.12/examples/mousewheel-interval.html
+ map.addControl(new OpenLayers.Control.Navigation(
+ {dragPanOptions: {enableKinetic: true}}
+ ));
+
+ var overview = new OpenLayers.Control.OverviewMap({
+ maximized: true,
+ minRatio: 200,
+ maxRatio: 300,
+ });
+ map.addControl(overview);
+
+ // stockholm
+ var position = new OpenLayers.LonLat(18.08,59.32).transform(fromProjection, toProjection);
+// var position = new OpenLayers.LonLat(0,0).transform(fromProjection, toProjection);
+ var zoom = 11;
+ map.setCenter(position, zoom);
+
+ // var message = {
+ // 'id': "12",
+ // 'lat': 59.454441065280534,
+ // 'lon': 18.062356332194543,
+ // 'html': '<p>asdf</p>'
+ // };
+
+// addMarker(message);
+ });
+//]]>
+ </script>
+ </head>
+ <body>
+ <table width="100%" height="100%">
+ <tr>
+ <td width="20%" height="100%"><div id="message" onmouseover="stopScrolling = true" onmouseout="stopScrolling = false"></div></td>
+ <td width="80%" height="100%"><div id="map"></div></td>
+ </tr>
+ </table>
+ </body>
+</html> \ No newline at end of file
diff --git a/src/uscxml/Convenience.h b/src/uscxml/Convenience.h
new file mode 100644
index 0000000..ad45e2c
--- /dev/null
+++ b/src/uscxml/Convenience.h
@@ -0,0 +1,33 @@
+#ifndef CONVENIENCE_H_LU7GZ6CB
+#define CONVENIENCE_H_LU7GZ6CB
+
+namespace uscxml {
+ inline bool isnan(double x) {
+ return x != x;
+ }
+
+ // see http://stackoverflow.com/questions/228005/alternative-to-itoa-for-converting-integer-to-string-c
+ template <typename T> std::string toStr(T tmp) {
+ std::ostringstream out;
+ out.precision(std::numeric_limits<double>::digits10 + 1);
+ out << tmp;
+ return out.str();
+ }
+
+ template <typename T> T strTo(std::string tmp) {
+ T output;
+ std::istringstream in(tmp);
+ in >> output;
+ return output;
+ }
+
+ inline bool isNumeric( const char* pszInput, int nNumberBase) {
+ std::string base = ".-0123456789ABCDEF";
+ std::string input = pszInput;
+ return (input.find_first_not_of(base.substr(0, nNumberBase + 2)) == std::string::npos);
+ }
+
+}
+
+
+#endif /* end of include guard: CONVENIENCE_H_LU7GZ6CB */
diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h
index b815712..617e461 100644
--- a/src/uscxml/Factory.h
+++ b/src/uscxml/Factory.h
@@ -2,6 +2,7 @@
#define FACTORY_H_5WKLGPRB
#include "uscxml/Message.h"
+#include "uscxml/Convenience.h"
#ifdef BUILD_AS_PLUGINS
#include "Pluma/Pluma.hpp"
@@ -14,31 +15,6 @@
namespace uscxml {
-inline bool isnan(double x) {
- return x != x;
-}
-
-// see http://stackoverflow.com/questions/228005/alternative-to-itoa-for-converting-integer-to-string-c
-template <typename T> std::string toStr(T tmp) {
- std::ostringstream out;
- out.precision(std::numeric_limits<double>::digits10 + 1);
- out << tmp;
- return out.str();
-}
-
-template <typename T> T strTo(std::string tmp) {
- T output;
- std::istringstream in(tmp);
- in >> output;
- return output;
-}
-
-inline bool isNumeric( const char* pszInput, int nNumberBase) {
- std::string base = ".-0123456789ABCDEF";
- std::string input = pszInput;
- return (input.find_first_not_of(base.substr(0, nNumberBase + 2)) == std::string::npos);
-}
-
class InterpreterImpl;
class ExecutableContentImpl {
diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp
index ef542d7..0a686d5 100644
--- a/src/uscxml/Message.cpp
+++ b/src/uscxml/Message.cpp
@@ -22,27 +22,27 @@ namespace uscxml {
static int _dataIndentation = 1;
Blob::~Blob() {
- free(_data);
+ free(data);
}
-Blob::Blob(size_t size) {
- _data = (char*)malloc(size);
- memset(_data, 0, size);
- _size = size;
+Blob::Blob(size_t _size) {
+ data = (char*)malloc(_size);
+ memset(data, 0, _size);
+ size = _size;
}
-Blob::Blob(void* data, size_t size, bool adopt) {
+Blob::Blob(void* _data, size_t _size, bool adopt) {
if (adopt) {
- _data = (char*)data;
+ data = (char*)_data;
} else {
- _data = (char*)malloc(size);
- memcpy(_data, data, size);
+ data = (char*)malloc(_size);
+ memcpy(data, _data, _size);
}
- _size = size;
+ size = _size;
}
-Data::Data(const char* data, size_t size, bool adopt) {
- binary = boost::shared_ptr<Blob>(new Blob((void*)data, size, adopt));
+Data::Data(const char* _data, size_t _size, bool adopt) {
+ binary = boost::shared_ptr<Blob>(new Blob((void*)_data, _size, adopt));
}
Data::Data(const Arabica::DOM::Node<std::string>& dom) {
diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h
index 9ae6ea1..bc92180 100644
--- a/src/uscxml/Message.h
+++ b/src/uscxml/Message.h
@@ -14,6 +14,8 @@
#include <boost/lexical_cast.hpp>
#include <inttypes.h>
+#include "uscxml/Convenience.h"
+
#define TAGNAME(elem) ((Arabica::DOM::Element<std::string>)elem).getTagName()
#define LOCALNAME(elem) ((Arabica::DOM::Element<std::string>)elem).getLocalName()
#define ATTR(elem, attr) ((Arabica::DOM::Element<std::string>)elem).getAttribute(attr)
@@ -26,8 +28,9 @@ public:
~Blob();
Blob(size_t size);
Blob(void* data, size_t size, bool adopt = false);
- char* _data;
- size_t _size;
+ char* data;
+ size_t size;
+ std::string mimetype;
};
class Data {
@@ -40,6 +43,15 @@ public:
Data() : type(INTERPRETED) {}
Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {}
Data(const char* data, size_t size, bool adopt);
+ Data(bool atom_) : type(INTERPRETED) {
+ if (atom_) {
+ atom = "true";
+ } else {
+ atom = "false";
+ }
+ }
+ template <typename T> Data(T value) : atom(toStr(value)), type(INTERPRETED) {}
+
explicit Data(const Arabica::DOM::Node<std::string>& dom);
virtual ~Data() {}
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp
index 62c61e0..176bddf 100644
--- a/src/uscxml/interpreter/InterpreterDraft6.cpp
+++ b/src/uscxml/interpreter/InterpreterDraft6.cpp
@@ -56,8 +56,9 @@ void InterpreterDraft6::interpret() {
for (unsigned int i = 0; i < dataElems.size(); i++) {
// do not process data elements of nested documents from invokers
if (!getAncestorElement(dataElems[i], _xmlNSPrefix + "invoke"))
- if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE)
+ if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) {
initializeData(Element<std::string>(dataElems[i]));
+ }
}
} else if(_dataModel) {
// initialize current data elements
diff --git a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp
index d24639a..8d126e9 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp
@@ -4,14 +4,14 @@
#define DATAVIEW_TYPED_GET(type) \
type retVal;\
-if (index + _start + sizeof(type) > _buffer->_size)\
+if (index + _start + sizeof(type) > _buffer->size)\
return 0;\
-memcpy(&retVal, _buffer->_data + (_start + index), sizeof(type));
+memcpy(&retVal, _buffer->data + (_start + index), sizeof(type));
#define DATAVIEW_TYPED_SET(type) \
-if (index + _start + sizeof(type) > _buffer->_size)\
+if (index + _start + sizeof(type) > _buffer->size)\
return;\
-memcpy(_buffer->_data + (_start + index), &value, sizeof(type));
+memcpy(_buffer->data + (_start + index), &value, sizeof(type));
namespace uscxml {
@@ -29,26 +29,26 @@ ArrayBuffer::ArrayBuffer(void* data, unsigned int size) {
unsigned long ArrayBuffer::getByteLength() {
if (!_buffer)
return 0;
- return _buffer->_size;
+ return _buffer->size;
}
ArrayBuffer ArrayBuffer::slice(long begin, long end) {
if (!_buffer) {
return ArrayBuffer(0);
}
- unsigned int realBegin = (begin + _buffer->_size) % _buffer->_size;
- unsigned int realEnd = (end + _buffer->_size) % _buffer->_size;
+ unsigned int realBegin = (begin + _buffer->size) % _buffer->size;
+ unsigned int realEnd = (end + _buffer->size) % _buffer->size;
if (realEnd < realBegin) {
return ArrayBuffer(0);
}
ArrayBuffer arrBuffer(realEnd - realBegin);
- memcpy(arrBuffer._buffer->_data, _buffer->_data + realBegin, realEnd - realBegin);
+ memcpy(arrBuffer._buffer->data, _buffer->data + realBegin, realEnd - realBegin);
return arrBuffer;
}
ArrayBuffer ArrayBuffer::slice(long begin) {
- return slice(begin, _buffer->_size);
+ return slice(begin, _buffer->size);
}
bool ArrayBuffer::isView(void*) {
@@ -65,25 +65,25 @@ ArrayBuffer ArrayBufferView::getBuffer() {
DataView::DataView(ArrayBuffer* buffer, unsigned long byteOffset, unsigned long byteLength) {
_start = byteOffset;
- if (_start > buffer->_buffer->_size)
+ if (_start > buffer->_buffer->size)
return;
_end = _start + byteLength;
- if (_end > buffer->_buffer->_size)
+ if (_end > buffer->_buffer->size)
return;
_buffer = buffer->_buffer;
}
DataView::DataView(ArrayBuffer* buffer , unsigned long byteOffset) {
_start = byteOffset;
- _end = buffer->_buffer->_size;
- if (_start > buffer->_buffer->_size)
+ _end = buffer->_buffer->size;
+ if (_start > buffer->_buffer->size)
return;
_buffer = buffer->_buffer;
}
DataView::DataView(ArrayBuffer* buffer) {
_start = 0;
- _end = (buffer->_buffer->_size);
+ _end = (buffer->_buffer->size);
_buffer = buffer->_buffer;
}
diff --git a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h
index 5584721..460afd4 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h
+++ b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h
@@ -199,7 +199,7 @@ public:
_start = byteOffset / sizeof(S);
_end = _start + length;
- if (_end > buffer->_buffer->_size / sizeof(S))
+ if (_end > buffer->_buffer->size / sizeof(S))
return;
_buffer = buffer->_buffer;
@@ -209,12 +209,12 @@ public:
return;
_start = byteOffset / sizeof(S);
- _end = buffer->_buffer->_size / sizeof(S);
+ _end = buffer->_buffer->size / sizeof(S);
_buffer = buffer->_buffer;
}
TypedArray(uscxml::ArrayBuffer* buffer) {
_start = 0;
- _end = (buffer->_buffer->_size) / sizeof(S);
+ _end = (buffer->_buffer->size) / sizeof(S);
_buffer = buffer->_buffer;
}
@@ -225,7 +225,7 @@ public:
_start = byteOffset / sizeof(S);
_end = _start + length;
- if (_end > buffer->_size / sizeof(S))
+ if (_end > buffer->size / sizeof(S))
return;
_buffer = buffer;
@@ -271,7 +271,7 @@ public:
if (index >= getLength())
return static_cast<T>(0);
S retVal;
- memcpy(&retVal, _buffer->_data + (_start + index) * sizeof(S), sizeof(S));
+ memcpy(&retVal, _buffer->data + (_start + index) * sizeof(S), sizeof(S));
return retVal;
}
@@ -280,7 +280,7 @@ public:
* Sets the element at the given numeric index to the given value.
*/
void set(unsigned long index, T value) {
- memcpy(_buffer->_data + (_start + index) * sizeof(S), &value, sizeof(S));
+ memcpy(_buffer->data + (_start + index) * sizeof(S), &value, sizeof(S));
}
/**
@@ -298,13 +298,13 @@ public:
void set(TypedArray<T, S>* value, unsigned long offset) {
if (!_buffer)
return;
- if (offset * sizeof(S) + value->getByteLength() > _buffer->_size)
+ if (offset * sizeof(S) + value->getByteLength() > _buffer->size)
return;
unsigned long otherOffset = value->_start * sizeof(S);
// will this work if we use the same buffer?
- memcpy(_buffer->_data + (_start + offset) * sizeof(S), value->_buffer->_data + otherOffset, value->getByteLength());
+ memcpy(_buffer->data + (_start + offset) * sizeof(S), value->_buffer->data + otherOffset, value->getByteLength());
}
void set(TypedArray<T, S>* value) {
@@ -330,7 +330,7 @@ public:
return;
if (sizeof(T) == sizeof(S)) {
- memcpy(_buffer->_data + offset, (void*)&data[0], data.size() * sizeof(S));
+ memcpy(_buffer->data + offset, (void*)&data[0], data.size() * sizeof(S));
} else {
S* buffer = (S*)malloc(data.size() * sizeof(S));
typename std::vector<T>::const_iterator dataIter = data.begin();
@@ -340,7 +340,7 @@ public:
dataIter++;
i++;
}
- memcpy(_buffer->_data + offset, buffer, data.size() * sizeof(S));
+ memcpy(_buffer->data + offset, buffer, data.size() * sizeof(S));
free (buffer);
}
}
diff --git a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
index f863bb7..ee74a5f 100644
--- a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
@@ -204,6 +204,33 @@ void XPathDataModel::setEvent(const Event& event) {
Data XPathDataModel::getStringAsData(const std::string& content) {
Data data;
+ XPathValue<std::string> result = _xpath.evaluate_expr(content, _doc);
+
+ std::stringstream ss;
+
+ switch (result.type()) {
+ case ANY:
+ break;
+ case BOOL:
+ ss << result.asBool();
+ break;
+ case NUMBER:
+ ss << result.asNumber();
+ break;
+ case STRING:
+ ss << result.asString();
+ break;
+ case NODE_SET:
+ NodeSet<std::string> ns = result.asNodeSet();
+ for (int i = 0; i < ns.size(); i++) {
+ ss << ns[i];
+ }
+ ss << result;
+ break;
+ }
+
+ data.atom = ss.str();
+ data.type = Data::VERBATIM;
return data;
}
@@ -308,6 +335,7 @@ void XPathDataModel::eval(const Arabica::DOM::Element<std::string>& scriptElem,
}
bool XPathDataModel::isDeclared(const std::string& expr) {
+ return true;
try {
return _varResolver.isDeclared(expr) || evalAsBool(expr);
} catch(...) {
@@ -461,14 +489,13 @@ void XPathDataModel::init(const Element<std::string>& dataElem,
Element<std::string> container = _doc.createElement("data");
container.setAttribute("id", location);
+
if (node) {
- if (node) {
- Node<std::string> data = node;
- while (data) {
- Node<std::string> dataClone = _doc.importNode(data, true);
- container.appendChild(dataClone);
- data = data.getNextSibling();
- }
+ Node<std::string> data = node;
+ while (data) {
+ Node<std::string> dataClone = _doc.importNode(data, true);
+ container.appendChild(dataClone);
+ data = data.getNextSibling();
}
} else if (content.length() > 0) {
Text<std::string> textNode = _doc.createTextNode(Interpreter::spaceNormalize(content));
@@ -497,8 +524,6 @@ void XPathDataModel::init(const Element<std::string>& dataElem,
} catch (SyntaxException e) {
throw Event("error.execution", Event::PLATFORM);
}
- } else {
- LOG(ERROR) << "data element has no content";
}
_datamodel.appendChild(container);
@@ -740,7 +765,7 @@ NodeSetVariableResolver::resolveVariable(const std::string& namepaceUri,
}
void NodeSetVariableResolver::setVariable(const std::string& name, const NodeSet<std::string>& value) {
-#if VERBOSE
+#if 0
std::cout << std::endl << "Setting " << name << ":" << std::endl;
for (int i = 0; i < value.size(); i++) {
std::cout << value[i].getNodeType() << " | " << value[i] << std::endl;
@@ -748,9 +773,26 @@ void NodeSetVariableResolver::setVariable(const std::string& name, const NodeSet
std::cout << std::endl;
#endif
_variables[name] = value;
+#if 0
+ std::map<std::string, Arabica::XPath::NodeSet<std::string> >::iterator varIter = _variables.begin();
+ while (varIter != _variables.end()) {
+ std::cout << varIter->first << ":" << std::endl;
+ for (int i = 0; i < varIter->second.size(); i++) {
+ std::cout << varIter->second[i].getNodeType() << " | " << varIter->second[i] << std::endl;
+ }
+ varIter++;
+ }
+#endif
}
bool NodeSetVariableResolver::isDeclared(const std::string& name) {
+#if 0
+ std::map<std::string, Arabica::XPath::NodeSet<std::string> >::iterator varIter = _variables.begin();
+ while (varIter != _variables.end()) {
+ std::cout << varIter->first << std::endl;
+ varIter++;
+ }
+#endif
return _variables.find(name) != _variables.end();
}
diff --git a/src/uscxml/plugins/element/file/FileElement.cpp b/src/uscxml/plugins/element/file/FileElement.cpp
index 2b48aba..e551ad7 100644
--- a/src/uscxml/plugins/element/file/FileElement.cpp
+++ b/src/uscxml/plugins/element/file/FileElement.cpp
@@ -78,8 +78,8 @@ void FileElement::enterElement(const Arabica::DOM::Node<std::string>& node) {
} else {
Data data = _interpreter->getDataModel().getStringAsData(ATTR(node, "contentexpr"));
if (data.binary) {
- content = data.binary->_data;
- contentSize = data.binary->_size;
+ content = data.binary->data;
+ contentSize = data.binary->size;
} else if (data.atom.length() > 0) {
contentStr = data.atom;
}
diff --git a/src/uscxml/plugins/element/respond/RespondElement.cpp b/src/uscxml/plugins/element/respond/RespondElement.cpp
index 9da3002..699a3a0 100644
--- a/src/uscxml/plugins/element/respond/RespondElement.cpp
+++ b/src/uscxml/plugins/element/respond/RespondElement.cpp
@@ -67,7 +67,7 @@ void RespondElement::enterElement(const Arabica::DOM::Node<std::string>& node) {
if (contentData.atom.length() > 0) {
httpReply.content = contentData.atom;
} else if (contentData.binary) {
- httpReply.content = std::string(contentData.binary->_data, contentData.binary->_size);
+ httpReply.content = std::string(contentData.binary->data, contentData.binary->size);
} else {
httpReply.content = Data::toJSON(contentData);
}
diff --git a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp
index e637f8b..36fcc1b 100644
--- a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp
+++ b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp
@@ -370,14 +370,14 @@ void FFMPEGInvoker::writeVideoFrame(EncodingContext* ctx, AVFormatContext *oc, A
}
uint32_t headerOffset = 0;
- headerOffset += image->_data[10] << 0;
- headerOffset += image->_data[11] << 8;
- headerOffset += image->_data[12] << 16;
- headerOffset += image->_data[13] << 24;
+ headerOffset += image->data[10] << 0;
+ headerOffset += image->data[11] << 8;
+ headerOffset += image->data[12] << 16;
+ headerOffset += image->data[13] << 24;
// std::cout << headerOffset + (c->width * c->height) << " / " << image->_size << std::endl;
- ret = avpicture_fill(&ctx->src_picture, (uint8_t*)(image->_data + headerOffset), BMP_FORMAT, c->width, c->height);
+ ret = avpicture_fill(&ctx->src_picture, (uint8_t*)(image->data + headerOffset), BMP_FORMAT, c->width, c->height);
if (ret < 0) {
fprintf(stderr,
"Could not fill image from given bitmap\n");
@@ -386,7 +386,7 @@ void FFMPEGInvoker::writeVideoFrame(EncodingContext* ctx, AVFormatContext *oc, A
(const uint8_t * const *)ctx->src_picture.data, ctx->src_picture.linesize,
0, c->height, ctx->dst_picture.data, ctx->dst_picture.linesize);
} else {
- avpicture_fill(&ctx->dst_picture, (uint8_t*)image->_data, c->pix_fmt, c->width, c->height);
+ avpicture_fill(&ctx->dst_picture, (uint8_t*)image->data, c->pix_fmt, c->width, c->height);
}
if (oc->oformat->flags & AVFMT_RAWPICTURE) {
diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.cpp b/src/uscxml/plugins/invoker/im/IMInvoker.cpp
index a7fd146..cff9b5f 100644
--- a/src/uscxml/plugins/invoker/im/IMInvoker.cpp
+++ b/src/uscxml/plugins/invoker/im/IMInvoker.cpp
@@ -1,12 +1,22 @@
#include "IMInvoker.h"
#include <glog/logging.h>
+#include "uscxml/UUID.h"
#ifdef BUILD_AS_PLUGINS
#include <Pluma/Connector.hpp>
#endif
-namespace uscxml {
+#define GET_INSTANCE_IN_CALLBACK(account) \
+tthread::lock_guard<tthread::mutex> lock(_accountMutex); \
+if (_accountInstances.find(account) == _accountInstances.end()) { \
+ LOG(ERROR) << "Callback for unknown account called"; \
+ return; \
+} \
+IMInvoker* inst = _accountInstances[account];\
+
+namespace uscxml {
+
#ifdef BUILD_AS_PLUGINS
PLUMA_CONNECTOR
bool connect(pluma::Host& host) {
@@ -25,16 +35,14 @@ PurpleEventLoopUiOps IMInvoker::_uiEventLoopOps =
purpleEventTimeoutRemove,
purpleEventInputAdd,
purpleEventInputRemove,
- purpleEventInputGetError,
+ NULL, //purpleEventInputGetError,
purpleEventTimeoutAddSec,
-
- /* padding */
NULL,
NULL,
NULL
};
-PurpleAccountUiOps IMInvoker::_accountUIOps = {
+PurpleAccountUiOps IMInvoker::_uiAccountOps = {
accountNotifyAdded,
accountStatusChanged,
accountRequestAdd,
@@ -44,66 +52,139 @@ PurpleAccountUiOps IMInvoker::_accountUIOps = {
NULL,
NULL
};
-
-/*** Conversation uiops ***/
-static void
-null_write_conv(PurpleConversation *conv, const char *who, const char *alias,
- const char *message, PurpleMessageFlags flags, time_t mtime)
-{
- const char *name;
- if (alias && *alias)
- name = alias;
- else if (who && *who)
- name = who;
- else
- name = NULL;
-
- printf("(%s) %s %s: %s\n", purple_conversation_get_name(conv),
- purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)),
- name, message);
-}
-static PurpleConversationUiOps null_conv_uiops =
+PurpleBlistUiOps IMInvoker::_uiBuddyOps = {
+ purpleNewList,
+ purpleNewNode,
+ purpleShow,
+ NULL, // purpleUpdate,
+ NULL, // purpleRemove,
+ NULL, // purpleDestroy,
+ NULL, // purpleSetVisible,
+ purpleRequestAddBuddy,
+ purpleRequestAddChat,
+ purpleRequestAddGroup,
+ NULL, // purpleSaveNode,
+ NULL, // purpleRemoveNode,
+ NULL, // purpleSaveAccount,
+};
+
+// file transfer
+PurpleXferUiOps IMInvoker::_uiXferOps = {
+ purpleNewXfer,
+ purpleDestroy,
+ purpleAddXfer,
+ purpleUpdateProgress,
+ purpleCancelLocal,
+ purpleCancelRemote,
+ purpleWrite,
+ purpleRead,
+ purpleDataNotSent,
+ purpleAddThumbnail
+};
+
+// connection info
+PurpleConnectionUiOps IMInvoker::_uiConnectOps = {
+ purpleConnectProgress,
+ purpleConnected,
+ purpleDisonnected,
+ purpleNotice,
+ purpleNetworkConnected,
+ purpleNetworkDisconnected,
+ purpleReportDisconnect,
+ NULL,
+ NULL,
+ NULL
+
+};
+
+//libpurple conversation operations
+PurpleConversationUiOps IMInvoker::_uiConvOps =
{
- NULL, /* create_conversation */
- NULL, /* destroy_conversation */
- NULL, /* write_chat */
- NULL, /* write_im */
- null_write_conv, /* write_conv */
- NULL, /* chat_add_users */
- NULL, /* chat_rename_user */
- NULL, /* chat_remove_users */
- NULL, /* chat_update_user */
- NULL, /* present */
- NULL, /* has_focus */
- NULL, /* custom_smiley_add */
- NULL, /* custom_smiley_write */
- NULL, /* custom_smiley_close */
- NULL, /* send_confirm */
+ NULL, //purpleCreateConversation,
+ NULL, //purpleDestroyConversation,
+ NULL, //purpleWriteChat,
+ NULL, //purpleWriteIm,
+ purpleWriteConv,
+ NULL, //purpleChatRenameUser,
+ NULL, //purpleChatRemoveUsers,
+ NULL, //purpleChatUpdateUser,
+ NULL, //purplePresentConversation,
+ NULL, //purpleHasFocus,
+ NULL, //purpleCustomSmileyAdd,
+ NULL, //purpleCustomSmileyWrite,
+ NULL, //purpleCustomSmileyClose,
+ NULL, //purpleSendConfirm,
NULL,
NULL,
NULL,
NULL
};
-static void
-null_ui_init(void)
-{
- /**
- * This should initialize the UI components for all the modules. Here we
- * just initialize the UI for conversations.
- */
- purple_conversations_set_ui_ops(&null_conv_uiops);
-}
+PurpleNotifyUiOps IMInvoker::_uiNotifyOps = {
+ purpeNotifyMessage,
+ purpeNotifyEmail,
+ purpeNotifyEmails,
+ purpeNotifyFormatted,
+ purpeNotifySearchResults,
+ purpeNotifySearchResultsNewRows,
+ purpeNotifyUserInfo,
+ purpeNotifyURI,
+ purpeNotifyClose,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
-static PurpleCoreUiOps null_core_uiops =
-{
+PurplePrivacyUiOps IMInvoker::_uiPrivacyOps = {
+ purplePermitAdded,
+ purplePermitRemoved,
+ purpleDebyAdded,
+ purpleDenyRemoved,
+ NULL,
+ NULL,
NULL,
+ NULL
+};
+
+PurpleRequestFeature IMInvoker::_features;
+PurpleRequestUiOps IMInvoker::_uiRequestOps = {
+ _features,
+ purpleRequestInput,
+ purpleRequestChoice,
+ purpleRequestAction,
+ purpleRequestWait,
+ purpleRequestWaitUpdate,
+ purpleRequestFields,
+ purpleRequestFile,
+ purpleRequestFolder,
+ purpleRequestClose,
NULL,
- null_ui_init,
NULL,
+ NULL,
+ NULL
+};
+
+PurpleWhiteboardUiOps IMInvoker::_uiWhiteboardOps = {
+ purpleCreateWB,
+ purpleDestroyWB,
+ purpleSetDimensions,
+ purpleSetBrush,
+ purpleDrawPont,
+ purpleDrawLine,
+ purpleClearWB,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
- /* padding */
+PurpleCoreUiOps IMInvoker::_uiCoreOps = {
+ NULL,
+ NULL,
+ purpleUIInit,
+ NULL,
NULL,
NULL,
NULL,
@@ -112,8 +193,22 @@ static PurpleCoreUiOps null_core_uiops =
DelayedEventQueue* IMInvoker::_eventQueue = NULL;
+tthread::mutex IMInvoker::_initMutex;
+tthread::condition_variable IMInvoker::_initCond;
+
+tthread::mutex IMInvoker::_accountMutex;
+std::map<PurpleAccount*, IMInvoker*> IMInvoker::_accountInstances;
+
void IMInvoker::initLibPurple(void *userdata, const std::string event) {
+ _initMutex.lock();
+
_uiInfo = g_hash_table_new(g_str_hash, g_str_equal);
+// g_hash_table_insert(_uiInfo, "name", (char*)"uscxml");
+// g_hash_table_insert(_uiInfo, "version", "0.0.3");
+// g_hash_table_insert(_uiInfo, "website", "http://uscxml.tk.informatik.tu-darmstadt.de");
+// g_hash_table_insert(_uiInfo, "dev_website", "http://uscxml.tk.informatik.tu-darmstadt.de");
+// g_hash_table_insert(_uiInfo, "client_type", "pc");
+
_gRand = g_rand_new();
/* Set a custom user directory (optional) */
@@ -122,7 +217,7 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) {
/* We do not want any debugging for now to keep the noise to a minimum. */
purple_debug_set_enabled(false);
- purple_core_set_ui_ops(&null_core_uiops);
+ purple_core_set_ui_ops(&_uiCoreOps);
// purple_eventloop_set_ui_ops(&glib_eventloops);
purple_eventloop_set_ui_ops(&_uiEventLoopOps);
@@ -160,63 +255,222 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) {
}
+ _initMutex.unlock();
+ _initCond.notify_all();
+
}
-void IMInvoker::send(void *userdata, const std::string event) {
- EventContext* ctx = (EventContext*)userdata;
- delete(ctx);
+void IMInvoker::buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *newstatus) {
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ GET_INSTANCE_IN_CALLBACK(account);
+
+ std::string buddyName = purple_buddy_get_name(buddy);
+ inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy);
}
-static void
-signed_on(PurpleConnection *gc, gpointer null)
-{
+void IMInvoker::buddyIdleChangeCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle) {
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ GET_INSTANCE_IN_CALLBACK(account);
+}
+
+void IMInvoker::buddyUpdateIdleCB() {
+}
+
+void IMInvoker::buddyAddedCB(PurpleBuddy* buddy) {
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ GET_INSTANCE_IN_CALLBACK(account);
+}
+
+void IMInvoker::buddyRemovedCB(PurpleBuddy* buddy) {
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ GET_INSTANCE_IN_CALLBACK(account);
+}
+
+void IMInvoker::buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps) {
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ GET_INSTANCE_IN_CALLBACK(account);
+}
+
+gboolean IMInvoker::jabberRcvdPresenceCB(PurpleConnection *gc, const char *type, const char *from, xmlnode *presence) {
PurpleAccount *account = purple_connection_get_account(gc);
- printf("Account connected: %s %s\n", purple_account_get_username(account), purple_account_get_protocol_id(account));
+ GET_INSTANCE_IN_CALLBACK(account);
}
-void IMInvoker::invoke(void *userdata, const std::string event) {
- EventContext* ctx = (EventContext*)userdata;
- IMInvoker* instance = ctx->instance;
+void IMInvoker::buddySignOnOffCB(PurpleBuddy *buddy) {
+ PurpleAccount *account = purple_buddy_get_account(buddy);
+ tthread::lock_guard<tthread::mutex> lock(_accountMutex);
+ if (_accountInstances.find(account) == _accountInstances.end()) {
+ LOG(ERROR) << "Callback for unknown account called";
+ return;
+ }
+ IMInvoker* inst = _accountInstances[account];
- std::string username;
- Event::getParam(ctx->invokeReq.params, "username", username);
- std::string protocolId;
- Event::getParam(ctx->invokeReq.params, "protocol", protocolId);
- std::string password;
- Event::getParam(ctx->invokeReq.params, "password", password);
+ std::string buddyName = purple_buddy_get_name(buddy);
+ inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy);
+}
+
+void IMInvoker::signedOnCB(PurpleConnection *gc, gpointer null) {
+ PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
+ purple_savedstatus_activate(status);
+}
- instance->_account = purple_account_new(username.c_str(), protocolId.c_str());
- purple_account_set_password(instance->_account, password.c_str(), NULL, NULL);
- purple_accounts_set_ui_ops(&_accountUIOps);
+Data IMInvoker::buddyToData(PurpleBuddy *buddy) {
+ Data data;
+ std::string buddyName = purple_buddy_get_name(buddy);
- purple_account_set_enabled(instance->_account, "uscxml", true);
+ if (purple_buddy_get_name(buddy)) data.compound["name"] = Data(purple_buddy_get_name(buddy), Data::VERBATIM);
+ if (purple_buddy_get_alias(buddy)) data.compound["alias"] = Data(purple_buddy_get_alias(buddy), Data::VERBATIM);
+ if (purple_buddy_get_alias_only(buddy)) data.compound["aliasOnly"] = Data(purple_buddy_get_alias_only(buddy), Data::VERBATIM);
+ if (purple_buddy_get_server_alias(buddy)) data.compound["server"] = Data(purple_buddy_get_server_alias(buddy), Data::VERBATIM);
- PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
- purple_savedstatus_activate(status);
-
- int handle;
- purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle,
- PURPLE_CALLBACK(signed_on), NULL);
+ PurpleGroup* group = purple_buddy_get_group(buddy);
+ if (group) {
+ if (purple_group_get_name(group)) data.compound["group"] = Data(purple_group_get_name(group), Data::VERBATIM);
+ }
- delete(ctx);
+ PurpleBuddyIcon* icon = purple_buddy_get_icon(buddy);
+ if (icon) {
+ size_t iconSize = 0;
+ gconstpointer iconData = purple_buddy_icon_get_data(icon, &iconSize);
+ data.compound["icon"] = Data((char*)iconData, iconSize, false);
+ }
+
+ PurplePresence* presence = purple_buddy_get_presence(buddy);
+
+ if (presence) {
+ GList *statusElem;
+ GList *statusList = purple_presence_get_statuses(presence);
+ PurpleStatus* status;
+
+ for(statusElem = statusList; statusElem; statusElem = statusElem->next) {
+ status = (PurpleStatus*)statusElem->data;
+ const char* statusId = purple_status_get_id(status);
+ const char* statusName = purple_status_get_name(status);
+ PurpleStatusPrimitive statusPrimitive = purple_primitive_get_type_from_id(statusId);
+
+ // only include active states
+ if(statusPrimitive == PURPLE_STATUS_UNSET || !purple_presence_is_status_primitive_active(presence, statusPrimitive))
+ continue;
+
+ if (statusName) data.compound["status"].compound[statusId] = Data(statusName, Data::VERBATIM);
+
+ PurpleStatusType* statusType = purple_status_get_type(status);
+
+ GList *statusAttrElem;
+ GList *statusAttrList = purple_status_type_get_attrs(statusType);
+ PurpleStatusAttr* statusAttr;
+ for(statusAttrElem = statusAttrList; statusAttrElem; statusAttrElem = statusAttrElem->next) {
+ statusAttr = (PurpleStatusAttr*)statusAttrElem->data;
+ const char* statusAttrId = purple_status_attr_get_id(statusAttr);
+ const char* statusAttrName = purple_status_attr_get_name(statusAttr);
+
+ PurpleValue* statusAttrValue = purple_status_attr_get_value(statusAttr);
+ if (statusAttrValue) {
+ Data purpleValue = purpleValueToData(statusAttrValue);
+ if (purpleValue) {
+ data.compound["status"].compound[statusId].compound[statusAttrId].compound["value"] = purpleValue;
+ if (statusAttrName) {
+ data.compound["status"].compound[statusId].compound[statusAttrId].compound["name"] = Data(statusAttrName, Data::VERBATIM);
+ }
+ }
+ }
+ }
+
+ data.compound["status"].compound[statusId].compound["active"] = Data((bool)purple_status_is_active(status));
+ data.compound["status"].compound[statusId].compound["available"] = Data((bool)purple_status_is_available(status));
+ data.compound["status"].compound[statusId].compound["exclusive"] = Data((bool)purple_status_is_exclusive(status));
+ data.compound["status"].compound[statusId].compound["active"] = Data((bool)purple_status_is_active(status));
+ data.compound["status"].compound[statusId].compound["independent"] = Data((bool)purple_status_is_independent(status));
+ data.compound["status"].compound[statusId].compound["online"] = Data((bool)purple_status_is_online(status));
+
+
+ PurpleValue* statusValue = purple_status_get_attr_value(status, statusId);
+ if (statusValue)
+ data.compound["status"].compound[statusId].compound["value"] = purpleValueToData(statusValue);
+
+ }
+
+ }
+ std::cout << Data::toJSON(data);
+ return data;
}
+Data IMInvoker::purpleValueToData(PurpleValue* value) {
+ Data data;
+ switch (purple_value_get_type(value)) {
+ case PURPLE_TYPE_BOOLEAN:
+ if (purple_value_get_boolean(value))
+ data = Data("true");
+ data = Data("false");
+ break;
+ case PURPLE_TYPE_STRING:
+ if (purple_value_get_string(value)) {
+ data = Data(purple_value_get_string(value), Data::VERBATIM);
+ std::cout << purple_value_get_string(value) << std::endl;
+ }
+ break;
+ case PURPLE_TYPE_CHAR:
+ Data(purple_value_get_char(value));
+ break;
+ case PURPLE_TYPE_UCHAR:
+ Data(purple_value_get_uchar(value));
+ break;
+ case PURPLE_TYPE_SHORT:
+ Data(purple_value_get_short(value));
+ break;
+ case PURPLE_TYPE_USHORT:
+ Data(purple_value_get_ushort(value));
+ break;
+ case PURPLE_TYPE_INT:
+ Data(purple_value_get_int(value));
+ break;
+ case PURPLE_TYPE_UINT:
+ Data(purple_value_get_uint(value));
+ break;
+ case PURPLE_TYPE_LONG:
+ Data(purple_value_get_long(value));
+ break;
+ case PURPLE_TYPE_ULONG:
+ Data(purple_value_get_ulong(value));
+ break;
+ case PURPLE_TYPE_INT64:
+ Data(purple_value_get_int64(value));
+ break;
+ case PURPLE_TYPE_UINT64:
+ Data(purple_value_get_uint64(value));
+ break;
+ case PURPLE_TYPE_OBJECT:
+ case PURPLE_TYPE_POINTER:
+ case PURPLE_TYPE_ENUM:
+ case PURPLE_TYPE_BOXED:
+ case PURPLE_TYPE_UNKNOWN:
+ case PURPLE_TYPE_SUBTYPE:
+ LOG(ERROR) << "purple thingy not supported";
+ break;
+ }
+ return data;
+}
+
IMInvoker::IMInvoker() {
_account = NULL;
if (!_eventQueue) {
+ tthread::lock_guard<tthread::mutex> lock(_initMutex);
_eventQueue = new DelayedEventQueue();
_eventQueue->addEvent("initLibPurple", IMInvoker::initLibPurple, 0, NULL);
_eventQueue->start();
-
-
+ // make sure to have the shebang initialized when we leave
+ _initCond.wait(_initMutex);
}
}
IMInvoker::~IMInvoker() {
if (_account) {
+ _accountMutex.lock();
+ _accountInstances.erase(_account);
purple_account_destroy(_account);
+ _accountMutex.unlock();
}
};
@@ -226,17 +480,62 @@ boost::shared_ptr<InvokerImpl> IMInvoker::create(InterpreterImpl* interpreter) {
}
Data IMInvoker::getDataModelVariables() {
- return _pluginData;
+ tthread::lock_guard<tthread::mutex> lock(_accountMutex);
+ return _dataModelVars;
}
void IMInvoker::send(const SendRequest& req) {
EventContext* ctx = new EventContext();
ctx->sendReq = req;
ctx->instance = this;
- _eventQueue->addEvent(req.sendid, IMInvoker::invoke, 0, ctx);
+
+ std::string eventId = UUID::getUUID();
+ _eventQueue->addEvent(eventId, IMInvoker::send, 0, ctx);
return;
}
+void IMInvoker::send(void *userdata, const std::string event) {
+ // we are in the thread that manages all of libpurple
+ EventContext* ctx = (EventContext*)userdata;
+#if 0
+ if (boost::iequals(ctx->sendReq.name, "im.send")) {
+ if (ctx->instance->_account) {
+ std::string receiver;
+ Event::getParam(ctx->sendReq.params, "receiver", receiver);
+
+ Data data;
+ Event::getParam(ctx->sendReq.params, "data", data);
+
+ // purple_conv_im_send
+ PurpleConversation* conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, ctx->instance->_account, receiver.c_str());
+ purple_conv_im_send(purple_conversation_get_im_data(conv), ctx->sendReq.content.c_str());
+
+#if 0
+ if (data.binary) {
+ PurpleConnection *gc = purple_account_get_connection(ctx->instance->_account);
+ PurplePlugin *prpl;
+ PurplePluginProtocolInfo *prpl_info;
+
+
+ if (gc) {
+ prpl = purple_connection_get_prpl(gc);
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+// if (prpl_info->send_file &&
+// (prpl_info->can_receive_file
+// && prpl_info->can_receive_file(gc, receiver.c_str())))
+// prpl_info->send_file(gc, receiver.c_str(), file);
+ prpl_info->send_raw(gc, data.binary->_data, data.binary->_size);
+ }
+
+ }
+#endif
+ }
+ }
+#endif
+ delete(ctx);
+}
+
void IMInvoker::cancel(const std::string sendId) {
}
@@ -249,6 +548,44 @@ void IMInvoker::invoke(const InvokeRequest& req) {
return;
}
+void IMInvoker::invoke(void *userdata, const std::string event) {
+ _accountMutex.lock();
+
+ EventContext* ctx = (EventContext*)userdata;
+ IMInvoker* instance = ctx->instance;
+
+ std::string username;
+ Event::getParam(ctx->invokeReq.params, "username", username);
+ std::string protocolId;
+ Event::getParam(ctx->invokeReq.params, "protocol", protocolId);
+ std::string password;
+ Event::getParam(ctx->invokeReq.params, "password", password);
+
+ instance->_account = purple_account_new(username.c_str(), protocolId.c_str());
+ _accountInstances[instance->_account] = instance;
+
+ purple_account_set_password(instance->_account, password.c_str(), NULL, NULL);
+
+ purple_account_set_enabled(instance->_account, "uscxml", true);
+
+ int handle;
+ purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, PURPLE_CALLBACK(signedOnCB), NULL);
+
+ purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", &handle, PURPLE_CALLBACK(buddySignOnOffCB), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", &handle, PURPLE_CALLBACK(buddySignOnOffCB), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", &handle, PURPLE_CALLBACK(buddyStatusChangedCB), NULL);
+
+ purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", &handle, PURPLE_CALLBACK(buddyIdleChangeCB), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "update-idle", &handle, PURPLE_CALLBACK(buddyUpdateIdleCB), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "buddy-added", &handle, PURPLE_CALLBACK(buddyAddedCB), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "buddy-removed", &handle, PURPLE_CALLBACK(buddyRemovedCB), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "buddy-caps-changed", &handle, PURPLE_CALLBACK(buddyCapsChangedCB), NULL);
+ purple_signal_connect(purple_blist_get_handle(), "jabber-receiving-presence", &handle, PURPLE_CALLBACK(jabberRcvdPresenceCB), NULL);
+
+ delete(ctx);
+ _accountMutex.unlock();
+}
+
guint IMInvoker::purpleEventTimeoutAdd(guint interval, GSourceFunc function, gpointer data) {
PurpleEventContext* ctx = new PurpleEventContext();
ctx->function = function;
@@ -294,6 +631,7 @@ gboolean IMInvoker::purpleEventInputRemove(guint handle) {
}
int IMInvoker::purpleEventInputGetError(int fd, int *error) {
+ // unused
std::cout << "purpleEventInputGetError" << std::endl;
return 0;
}
@@ -310,7 +648,18 @@ void IMInvoker::purpleCallback(void *userdata, const std::string event) {
void IMInvoker::purplePrefsInit(void) {}
void IMInvoker::purpleDebugInit(void) {}
-void IMInvoker::purpleUIInit(void) {}
+
+void IMInvoker::purpleUIInit(void) {
+ purple_accounts_set_ui_ops(&_uiAccountOps);
+ purple_xfers_set_ui_ops(&_uiXferOps);
+// purple_blist_set_ui_ops(&_uiBuddyOps);
+// purple_notify_set_ui_ops(&_uiNotifyOps);
+// purple_privacy_set_ui_ops(&_uiPrivacyOps);
+// purple_request_set_ui_ops(&_uiRequestOps);
+// purple_connections_set_ui_ops(&_uiConnectOps);
+// purple_whiteboard_set_ui_ops(&_uiWhiteboardOps);
+ purple_conversations_set_ui_ops(&_uiConvOps);
+}
void IMInvoker::purpleQuit(void) {}
GHashTable* IMInvoker::purpleGetUIInfo(void) {
@@ -357,5 +706,183 @@ void IMInvoker::accountCloseRequest(void *ui_handle) {
std::cout << "accountCloseRequest" << std::endl;
}
+//libpurple conversation operations
+void IMInvoker::purpleCreateConversation(PurpleConversation *conv) {}
+void IMInvoker::purpleDestroyConversation(PurpleConversation *conv) {}
+void IMInvoker::purpleWriteChat(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime) {}
+void IMInvoker::purpleWriteIm(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime) {}
+void IMInvoker::purpleWriteConv(PurpleConversation *conv, const char *name, const char *alias, const char *message, PurpleMessageFlags flags, time_t mtime) {
+ const char *who;
+ if (alias && *alias)
+ who = alias;
+ else if (name && *name)
+ who = name;
+ else
+ who = NULL;
+
+ printf("(%s) %s %s: %s\n", purple_conversation_get_name(conv),
+ purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)),
+ who, message);
+}
+void IMInvoker::purpleChatAddUsers(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals) {}
+void IMInvoker::purpleChatRenameUser(PurpleConversation *conv, const char *old_name, const char *new_name, const char *new_alias) {}
+void IMInvoker::purpleChatRemoveUsers(PurpleConversation *conv, GList *users) {}
+void IMInvoker::purpleChatUpdateUser(PurpleConversation *conv, const char *user) {}
+void IMInvoker::purplePresentConversation(PurpleConversation *conv) {}
+gboolean IMInvoker::purpleHasFocus(PurpleConversation *conv) {
+ return true;
+}
+gboolean IMInvoker::purpleCustomSmileyAdd(PurpleConversation *conv, const char *smile, gboolean remote) {
+ return true;
+}
+void IMInvoker::purpleCustomSmileyWrite(PurpleConversation *conv, const char *smile, const guchar *data, gsize size) {}
+void IMInvoker::purpleCustomSmileyClose(PurpleConversation *conv, const char *smile) {}
+void IMInvoker::purpleSendConfirm(PurpleConversation *conv, const char *message) {}
+
+// buddy operations
+void IMInvoker::purpleNewList(PurpleBuddyList *list) {
+ std::cout << "purpleNewList" << std::endl;
+}
+void IMInvoker::purpleNewNode(PurpleBlistNode *node) {
+ std::cout << "purpleNewNode" << std::endl;
+}
+void IMInvoker::purpleShow(PurpleBuddyList *list) {
+ std::cout << "purpleShow" << std::endl;
+}
+void IMInvoker::purpleUpdate(PurpleBuddyList *list, PurpleBlistNode *node) {
+}
+void IMInvoker::purpleRemove(PurpleBuddyList *list, PurpleBlistNode *node) {}
+void IMInvoker::purpleDestroy(PurpleBuddyList *list) {}
+void IMInvoker::purpleSetVisible(PurpleBuddyList *list, gboolean show) {}
+void IMInvoker::purpleRequestAddBuddy(PurpleAccount *account, const char *username, const char *group, const char *alias) {
+ std::cout << "purpleRequestAddBuddy" << std::endl;
+}
+void IMInvoker::purpleRequestAddChat(PurpleAccount *account, PurpleGroup *group, const char *alias, const char *name) {
+ std::cout << "purpleRequestAddChat" << std::endl;
+}
+void IMInvoker::purpleRequestAddGroup(void) {
+ std::cout << "purpleRequestAddGroup" << std::endl;
+}
+void IMInvoker::purpleSaveNode(PurpleBlistNode *node) {}
+void IMInvoker::purpleRemoveNode(PurpleBlistNode *node) {}
+void IMInvoker::purpleSaveAccount(PurpleAccount *account) {}
+
+// file transfer operations
+void IMInvoker::purpleNewXfer(PurpleXfer *xfer) {
+ std::cout << "purpleNewXfer" << std::endl;
+}
+void IMInvoker::purpleDestroy(PurpleXfer *xfer) {
+ std::cout << "purpleDestroy" << std::endl;
+}
+void IMInvoker::purpleAddXfer(PurpleXfer *xfer) {
+ std::cout << "purpleAddXfer" << std::endl;
+}
+void IMInvoker::purpleUpdateProgress(PurpleXfer *xfer, double percent) {
+ std::cout << "purpleUpdateProgress" << std::endl;
+}
+void IMInvoker::purpleCancelLocal(PurpleXfer *xfer) {
+ std::cout << "purpleCancelLocal" << std::endl;
+}
+void IMInvoker::purpleCancelRemote(PurpleXfer *xfer) {
+ std::cout << "purpleCancelRemote" << std::endl;
+}
+gssize IMInvoker::purpleWrite(PurpleXfer *xfer, const guchar *buffer, gssize size) {
+ std::cout << "purpleWrite" << std::endl;
+}
+gssize IMInvoker::purpleRead(PurpleXfer *xfer, guchar **buffer, gssize size) {
+ std::cout << "purpleRead" << std::endl;
+}
+void IMInvoker::purpleDataNotSent(PurpleXfer *xfer, const guchar *buffer, gsize size) {
+ std::cout << "purpleDataNotSent" << std::endl;
+}
+void IMInvoker::purpleAddThumbnail(PurpleXfer *xfer, const gchar *formats) {
+ std::cout << "purpleAddThumbnail" << std::endl;
+}
+
+// notification operations
+void* IMInvoker::purpeNotifyMessage(PurpleNotifyMsgType type, const char *title, const char *primary, const char *secondary, PurpleRequestCommonParameters *cpar) {
+ return NULL;
+}
+void* IMInvoker::purpeNotifyEmail(PurpleConnection *gc, const char *subject, const char *from, const char *to, const char *url) {
+ return NULL;
+}
+void* IMInvoker::purpeNotifyEmails(PurpleConnection *gc, size_t count, gboolean detailed, const char **subjects, const char **froms, const char **tos, const char **urls) {
+ return NULL;
+}
+void* IMInvoker::purpeNotifyFormatted(const char *title, const char *primary, const char *secondary, const char *text) {
+ return NULL;
+}
+void* IMInvoker::purpeNotifySearchResults(PurpleConnection *gc, const char *title, const char *primary, const char *secondary, PurpleNotifySearchResults *results, gpointer user_data) {
+ return NULL;
+}
+void IMInvoker::purpeNotifySearchResultsNewRows(PurpleConnection *gc, PurpleNotifySearchResults *results, void *data) {}
+void* IMInvoker::purpeNotifyUserInfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info) {
+ return NULL;
+}
+void* IMInvoker::purpeNotifyURI(const char *uri) {
+ return NULL;
+}
+void IMInvoker::purpeNotifyClose(PurpleNotifyType type, void *ui_handle) {}
+
+// privacy ui operations
+void IMInvoker::purplePermitAdded(PurpleAccount *account, const char *name) {}
+void IMInvoker::purplePermitRemoved(PurpleAccount *account, const char *name) {}
+void IMInvoker::purpleDebyAdded(PurpleAccount *account, const char *name) {}
+void IMInvoker::purpleDenyRemoved(PurpleAccount *account, const char *name) {}
+
+// request ui operations
+void* IMInvoker::purpleRequestInput(const char *title, const char *primary,
+ const char *secondary, const char *default_value,
+ gboolean multiline, gboolean masked, gchar *hint,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data) {}
+void* IMInvoker::purpleRequestChoice(const char *title, const char *primary,
+ const char *secondary, gpointer default_value,
+ const char *ok_text, GCallback ok_cb, const char *cancel_text,
+ GCallback cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data, va_list choices) {}
+void* IMInvoker::purpleRequestAction(const char *title, const char *primary,
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
+ size_t action_count, va_list actions) {}
+void* IMInvoker::purpleRequestWait(const char *title, const char *primary,
+ const char *secondary, gboolean with_progress,
+ PurpleRequestCancelCb cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data) {}
+
+void IMInvoker::purpleRequestWaitUpdate(void *ui_handle, gboolean pulse, gfloat fraction) {}
+void* IMInvoker::purpleRequestFields(const char *title, const char *primary,
+ const char *secondary, PurpleRequestFields *fields,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data) {}
+void* IMInvoker::purpleRequestFile(const char *title, const char *filename,
+ gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data) {}
+void* IMInvoker::purpleRequestFolder(const char *title, const char *dirname,
+ GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data) {}
+void IMInvoker::purpleRequestClose(PurpleRequestType type, void *ui_handle) {}
+
+
+// connection ui operations
+void IMInvoker::purpleConnectProgress(PurpleConnection *gc, const char *text, size_t step, size_t step_count) {}
+void IMInvoker::purpleConnected(PurpleConnection *gc) {}
+void IMInvoker::purpleDisonnected(PurpleConnection *gc) {}
+void IMInvoker::purpleNotice(PurpleConnection *gc, const char *text) {}
+void IMInvoker::purpleNetworkConnected(void) {}
+void IMInvoker::purpleNetworkDisconnected(void) {}
+void IMInvoker::purpleReportDisconnect(PurpleConnection *gc, PurpleConnectionError reason, const char *text) {}
+
+// whiteboard ui operations
+void IMInvoker::purpleCreateWB(PurpleWhiteboard *wb) {}
+void IMInvoker::purpleDestroyWB(PurpleWhiteboard *wb) {}
+void IMInvoker::purpleSetDimensions(PurpleWhiteboard *wb, int width, int height) {}
+void IMInvoker::purpleSetBrush(PurpleWhiteboard *wb, int size, int color) {}
+void IMInvoker::purpleDrawPont(PurpleWhiteboard *wb, int x, int y, int color, int size) {}
+void IMInvoker::purpleDrawLine(PurpleWhiteboard *wb, int x1, int y1, int x2, int y2, int color, int size) {}
+void IMInvoker::purpleClearWB(PurpleWhiteboard *wb) {}
+
} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.h b/src/uscxml/plugins/invoker/im/IMInvoker.h
index fb02e46..b8a57f6 100644
--- a/src/uscxml/plugins/invoker/im/IMInvoker.h
+++ b/src/uscxml/plugins/invoker/im/IMInvoker.h
@@ -3,9 +3,9 @@
#include <uscxml/Interpreter.h>
-//extern "C" {
+extern "C" {
#include <libpurple/purple.h>
-//}
+}
#ifdef BUILD_AS_PLUGINS
#include "uscxml/plugins/Plugins.h"
@@ -41,14 +41,46 @@ public:
protected:
static bool _libPurpleIsInitialized;
static Data _pluginData;
- static PurpleAccountUiOps _accountUIOps;
+
+ Data _dataModelVars;
+
+ static Data buddyToData(PurpleBuddy *buddy);
+ static Data purpleValueToData(PurpleValue* value);
+
+ static PurpleAccountUiOps _uiAccountOps;
static PurpleEventLoopUiOps _uiEventLoopOps;
static PurpleCoreUiOps _uiCoreOps;
+ static PurpleConversationUiOps _uiConvOps;
+ static PurpleBlistUiOps _uiBuddyOps;
+ static PurpleXferUiOps _uiXferOps;
+ static PurpleNotifyUiOps _uiNotifyOps;
+ static PurplePrivacyUiOps _uiPrivacyOps;
+ static PurpleRequestUiOps _uiRequestOps;
+ static PurpleConnectionUiOps _uiConnectOps;
+ static PurpleWhiteboardUiOps _uiWhiteboardOps;
+
+ static PurpleRequestFeature _features;
static GHashTable* _uiInfo;
static GRand* _gRand;
+ static tthread::mutex _accountMutex;
+ static std::map<PurpleAccount*, IMInvoker*> _accountInstances;
+ static tthread::mutex _initMutex;
+ static tthread::condition_variable _initCond;
static DelayedEventQueue* _eventQueue;
+ // event callbacks
+ static void signedOnCB(PurpleConnection *gc, gpointer null);
+ static void buddySignOnOffCB(PurpleBuddy *buddy);
+ static void buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *oldstatus, PurpleStatus *newstatus);
+ static void buddyIdleChangeCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle);
+ static void buddyUpdateIdleCB();
+ static void buddyAddedCB(PurpleBuddy* buddy);
+ static void buddyRemovedCB(PurpleBuddy* buddy);
+ static void buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps);
+ static gboolean jabberRcvdPresenceCB(PurpleConnection *gc, const char *type, const char *from, xmlnode *presence);
+
+
// these are only being called from the delayed queue's thread
static void initLibPurple(void *userdata, const std::string event);
static void send(void *userdata, const std::string event);
@@ -79,31 +111,130 @@ protected:
static void purpleQuit(void);
static GHashTable* purpleGetUIInfo(void);
+ //libpurple conversation operations
+ static void purpleCreateConversation(PurpleConversation *conv);
+ static void purpleDestroyConversation(PurpleConversation *conv);
+ static void purpleWriteChat(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime);
+ static void purpleWriteIm(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime);
+ static void purpleWriteConv(PurpleConversation *conv, const char *name, const char *alias, const char *message, PurpleMessageFlags flags, time_t mtime);
+ static void purpleChatAddUsers(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals);
+ static void purpleChatRenameUser(PurpleConversation *conv, const char *old_name, const char *new_name, const char *new_alias);
+ static void purpleChatRemoveUsers(PurpleConversation *conv, GList *users);
+ static void purpleChatUpdateUser(PurpleConversation *conv, const char *user);
+ static void purplePresentConversation(PurpleConversation *conv);
+ static gboolean purpleHasFocus(PurpleConversation *conv);
+ static gboolean purpleCustomSmileyAdd(PurpleConversation *conv, const char *smile, gboolean remote);
+ static void purpleCustomSmileyWrite(PurpleConversation *conv, const char *smile, const guchar *data, gsize size);
+ static void purpleCustomSmileyClose(PurpleConversation *conv, const char *smile);
+ static void purpleSendConfirm(PurpleConversation *conv, const char *message);
+
+ // buddy operations
+ static void purpleNewList(PurpleBuddyList *list);
+ static void purpleNewNode(PurpleBlistNode *node);
+ static void purpleShow(PurpleBuddyList *list);
+ static void purpleUpdate(PurpleBuddyList *list, PurpleBlistNode *node);
+ static void purpleRemove(PurpleBuddyList *list, PurpleBlistNode *node);
+ static void purpleDestroy(PurpleBuddyList *list);
+ static void purpleSetVisible(PurpleBuddyList *list, gboolean show);
+ static void purpleRequestAddBuddy(PurpleAccount *account, const char *username, const char *group, const char *alias);
+ static void purpleRequestAddChat(PurpleAccount *account, PurpleGroup *group, const char *alias, const char *name);
+ static void purpleRequestAddGroup(void);
+ static void purpleSaveNode(PurpleBlistNode *node);
+ static void purpleRemoveNode(PurpleBlistNode *node);
+ static void purpleSaveAccount(PurpleAccount *account);
+
+ // file transfer operations
+ static void purpleNewXfer(PurpleXfer *xfer);
+ static void purpleDestroy(PurpleXfer *xfer);
+ static void purpleAddXfer(PurpleXfer *xfer);
+ static void purpleUpdateProgress(PurpleXfer *xfer, double percent);
+ static void purpleCancelLocal(PurpleXfer *xfer);
+ static void purpleCancelRemote(PurpleXfer *xfer);
+ static gssize purpleWrite(PurpleXfer *xfer, const guchar *buffer, gssize size);
+ static gssize purpleRead(PurpleXfer *xfer, guchar **buffer, gssize size);
+ static void purpleDataNotSent(PurpleXfer *xfer, const guchar *buffer, gsize size);
+ static void purpleAddThumbnail(PurpleXfer *xfer, const gchar *formats);
+
+ // notification operations
+ static void* purpeNotifyMessage(PurpleNotifyMsgType type, const char *title, const char *primary, const char *secondary, PurpleRequestCommonParameters *cpar);
+ static void* purpeNotifyEmail(PurpleConnection *gc, const char *subject, const char *from, const char *to, const char *url);
+ static void* purpeNotifyEmails(PurpleConnection *gc, size_t count, gboolean detailed, const char **subjects, const char **froms, const char **tos, const char **urls);
+ static void* purpeNotifyFormatted(const char *title, const char *primary, const char *secondary, const char *text);
+ static void* purpeNotifySearchResults(PurpleConnection *gc, const char *title, const char *primary, const char *secondary, PurpleNotifySearchResults *results, gpointer user_data);
+ static void purpeNotifySearchResultsNewRows(PurpleConnection *gc, PurpleNotifySearchResults *results, void *data);
+ static void* purpeNotifyUserInfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info);
+ static void* purpeNotifyURI(const char *uri);
+ static void purpeNotifyClose(PurpleNotifyType type, void *ui_handle);
+
// account operations
- static void accountNotifyAdded(PurpleAccount *account,
- const char *remote_user,
- const char *id,
- const char *alias,
- const char *message);
- static void accountStatusChanged(PurpleAccount *account,
- PurpleStatus *status);
- static void accountRequestAdd(PurpleAccount *account,
- const char *remote_user,
- const char *id,
- const char *alias,
- const char *message);
- static void* accountRequestAuthorize(PurpleAccount *account,
- const char *remote_user,
- const char *id,
- const char *alias,
- const char *message,
- gboolean on_list,
- PurpleAccountRequestAuthorizationCb authorize_cb,
- PurpleAccountRequestAuthorizationCb deny_cb,
- void *user_data);
+ static void accountNotifyAdded(PurpleAccount *account, const char *remote_user, const char *id, const char *alias, const char *message);
+ static void accountStatusChanged(PurpleAccount *account, PurpleStatus *status);
+ static void accountRequestAdd(PurpleAccount *account, const char *remote_user, const char *id, const char *alias, const char *message);
+ static void* accountRequestAuthorize(PurpleAccount *account, const char *remote_user, const char *id, const char *alias, const char *message, gboolean on_list, PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data);
static void accountCloseRequest(void *ui_handle);
+ // privacy ui operations
+ static void purplePermitAdded(PurpleAccount *account, const char *name);
+ static void purplePermitRemoved(PurpleAccount *account, const char *name);
+ static void purpleDebyAdded(PurpleAccount *account, const char *name);
+ static void purpleDenyRemoved(PurpleAccount *account, const char *name);
+
+ // request ui operations
+ static void* purpleRequestInput(const char *title, const char *primary,
+ const char *secondary, const char *default_value,
+ gboolean multiline, gboolean masked, gchar *hint,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
+ static void* purpleRequestChoice(const char *title, const char *primary,
+ const char *secondary, gpointer default_value,
+ const char *ok_text, GCallback ok_cb, const char *cancel_text,
+ GCallback cancel_cb, PurpleRequestCommonParameters *cpar,
+ void *user_data, va_list choices);
+ static void* purpleRequestAction(const char *title, const char *primary,
+ const char *secondary, int default_action,
+ PurpleRequestCommonParameters *cpar, void *user_data,
+ size_t action_count, va_list actions);
+ static void* purpleRequestWait(const char *title, const char *primary,
+ const char *secondary, gboolean with_progress,
+ PurpleRequestCancelCb cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
+
+ static void purpleRequestWaitUpdate(void *ui_handle, gboolean pulse, gfloat fraction);
+ static void* purpleRequestFields(const char *title, const char *primary,
+ const char *secondary, PurpleRequestFields *fields,
+ const char *ok_text, GCallback ok_cb,
+ const char *cancel_text, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
+ static void* purpleRequestFile(const char *title, const char *filename,
+ gboolean savedialog, GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
+ static void* purpleRequestFolder(const char *title, const char *dirname,
+ GCallback ok_cb, GCallback cancel_cb,
+ PurpleRequestCommonParameters *cpar, void *user_data);
+ static void purpleRequestClose(PurpleRequestType type, void *ui_handle);
+
+ // connection ui operations
+ static void purpleConnectProgress(PurpleConnection *gc, const char *text, size_t step, size_t step_count);
+ static void purpleConnected(PurpleConnection *gc);
+ static void purpleDisonnected(PurpleConnection *gc);
+ static void purpleNotice(PurpleConnection *gc, const char *text);
+ static void purpleNetworkConnected(void);
+ static void purpleNetworkDisconnected(void);
+ static void purpleReportDisconnect(PurpleConnection *gc, PurpleConnectionError reason, const char *text);
+
+
+ // whiteboard ui operations
+ static void purpleCreateWB(PurpleWhiteboard *wb);
+ static void purpleDestroyWB(PurpleWhiteboard *wb);
+ static void purpleSetDimensions(PurpleWhiteboard *wb, int width, int height);
+ static void purpleSetBrush(PurpleWhiteboard *wb, int size, int color);
+ static void purpleDrawPont(PurpleWhiteboard *wb, int x, int y, int color, int size);
+ static void purpleDrawLine(PurpleWhiteboard *wb, int x1, int y1, int x2, int y2, int color, int size);
+ static void purpleClearWB(PurpleWhiteboard *wb);
+
+
PurpleAccount* _account;
};
diff --git a/test/samples/uscxml/applications/3dviewer.scxml b/test/samples/uscxml/applications/3dviewer.scxml
deleted file mode 100644
index 90970d3..0000000
--- a/test/samples/uscxml/applications/3dviewer.scxml
+++ /dev/null
@@ -1,84 +0,0 @@
-<scxml
- datamodel="ecmascript"
- xmlns="http://www.w3.org/2005/07/scxml"
- xmlns:scenegraph="http://uscxml.tk.informatik.tu-darmstadt.de/scenegraph.xsd">
- <datamodel>
- <data id="localCam" />
- <data id="foo" />
- </datamodel>
- <state id="start">
- <!-- setup all invokers -->
-
- <!-- 20ms heartbeat -->
- <invoke type="heartbeat" id="heartbeat.20ms">
- <param name="interval" expr="'20ms'" />
- </invoke>
-
- <!-- receive files -->
- <invoke type="umundo" id="umundo.files">
- <param name="domain" expr="''" />
- <param name="channel" expr="'umundo.files'" />
- <param name="types" expr="'../proto/3DViewer'" />
- </invoke>
-
- <!-- publish / subscribe to remote pose -->
- <invoke type="umundo" id="umundo.pose">
- <param name="domain" expr="''" />
- <param name="channel" expr="'umundo.pose'" />
- <param name="types" expr="'../proto/3DViewer'" />
- </invoke>
-
- <!-- 3D viewer
- This is only a template for nodes inserted per javascript
- -->
- <invoke type="scenegraph">
- <content>
- <scenegraph:display id="localDisplay" width="50%" height="50%">
- <scenegraph:viewport>
- <scenegraph:camera id="localCam">
- </scenegraph:camera>
- <scenegraph:translation>
- <scenegraph:rotation>
- <scenegraph:scale>
- <scenegraph:node />
- </scenegraph:scale>
- </scenegraph:rotation>
- </scenegraph:translation>
- </scenegraph:viewport>
- </scenegraph:display>
- </content>
- </invoke>
- <state id="sendPose">
- <transition target="sendPose" event="heartbeat.20ms" />
- <onentry>
- <script>
- localCam = document.evaluate("//scenegraph:display[@id='localDisplay']").asNodeSet();
- print(localCam[0].getAttribute("pitch"));
- </script>
- <send target="#_umundo.pose" event="umundo.pose">
- <param name="type" expr="'SceneSetup'" />
- <content>
-({
- 'camPose': {
- 'euler': {
- 'pitch': localCam.getAttribute("pitch"),
- 'roll': localCam.getAttribute("roll"),
- 'yaw': localCam.getAttribute("yaw"),
- }
- },
- 'modelPose': {
- 'euler': {
- 'pitch': localCam.getAttribute("pitch"),
- 'roll': localCam.getAttribute("roll"),
- 'yaw': localCam.getAttribute("yaw"),
- }
- },
- 'modelName': "foo",
- 'bandName': "foo"
-})
- </content>
- </send>
- </onentry>
- </state>
- </state>
-</scxml> \ No newline at end of file
diff --git a/test/samples/uscxml/test-instant-messaging.scxml b/test/samples/uscxml/test-instant-messaging.scxml
index 569ec59..6ca39d4 100644
--- a/test/samples/uscxml/test-instant-messaging.scxml
+++ b/test/samples/uscxml/test-instant-messaging.scxml
@@ -1,4 +1,14 @@
<scxml datamodel="ecmascript">
+ <script>
+//<![CDATA[
+ someBinaryData = new ArrayBuffer(256);
+ var view = new Uint8Array(someBinaryData);
+ for (var i = 0; i < view.length; i++) {
+ view[i] = i;
+ }
+//]]>
+ </script>
+
<script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" />
<state id="idle">
<onentry>
@@ -13,7 +23,13 @@
<state id="dump">
<transition event="dump" target="dump">
- <script>dump(_invokers['im']);</script>
+ <send event="dump" delay="1000ms" />
+ <script>//dump(_invokers['im']);</script>
+ <send target="#_im" event="im.send">
+ <param name="receiver" expr="'sradomski@localhost'" />
+ <param name="data" expr="someBinaryData" />
+ <content>Hey There!</content>
+ </send>
</transition>
</state>
</state>