diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index 72688390ba..3e3d8b6dab 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -164,6 +164,7 @@
raw-tool-clipart.js
raw-wb-board.js
raw-wb-area.js
+ dweet-io.js
wb.js
../generated-sources/main/java/org/apache/openmeetings/web/room/wb/
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
old mode 100644
new mode 100755
index e4217a593f..859591bbab
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.properties.xml
@@ -960,4 +960,8 @@ see https://openmeetings.apache.org/LanguageEditor.html for Details
+
+
+
+
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
old mode 100644
new mode 100755
index 08b390cdfa..f176e9af6c
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.html
@@ -155,6 +155,7 @@
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/dweet-io.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/dweet-io.js
new file mode 100644
index 0000000000..f736cb46e3
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/dweet-io.js
@@ -0,0 +1,520 @@
+// dweet.io.js
+// http://dweet.io
+// (c) 2014 Jim Heising and Bug Labs, Inc.
+// dweet.io.js may be freely distributed under the MIT license.
+(function () {
+
+ var isNode = true;
+
+ // Is this loading into node.js?
+ try {
+ isNode = (require);
+ }
+ catch (e) {
+ isNode = false;
+ }
+
+ var io;
+ var request;
+
+ var LAST_THING_NAME = "last-thing.dat";
+ var DWEET_SERVER = "https://dweet.io:443";
+ var STRICT_SSL = true;
+ var REQUEST_TIMEOUT = 5000;
+ var lastThing;
+
+ if (isNode) {
+ io = require("socket.io-client");
+ request = require("request");
+
+ if (require("fs").existsSync(LAST_THING_NAME)) {
+ try {
+ lastThing = require("fs").readFileSync(LAST_THING_NAME).toString();
+ }
+ catch (e) {
+ }
+ }
+ }
+ else {
+ request = function (options, callback) {
+ var self = this;
+ var src = options.url + (options.url.indexOf("?") + 1 ? "&" : "?");
+ var params = [];
+ var param_name = "";
+
+ for (param_name in options.json) {
+ params.push(param_name + "=" + encodeURIComponent(options.json[param_name]));
+ }
+
+ // Generate a unique callbackname
+ var callbackName = "callback";
+ var index = 0;
+ while (window.dweetCallback[callbackName + index]) {
+ index++;
+ }
+
+ callbackName = callbackName + index;
+ window.dweetCallback[callbackName] = function (data) {
+ callback(null, data, data);
+ };
+
+ // We're going to load everything with JSONP.
+ params.push("callback=dweetCallback." + callbackName);
+ params.push("_" + "=" + Date.now());
+
+ src += params.join("&");
+
+ dweet_script_loader(src, function (script) {
+ script.parentNode.removeChild(script);
+ window.dweetCallback[callbackName] = undefined;
+ delete window.dweetCallback[callbackName];
+ });
+ };
+
+ window.dweetCallback = {};
+
+ (function () {
+ var re = /ded|co/;
+ var onload = 'onload';
+ var onreadystatechange = 'onreadystatechange';
+ var readyState = 'readyState';
+
+ var load = function (src, fn) {
+ var script = document.createElement('script');
+ script[onload] = script[onreadystatechange] = function () {
+ if (!this[readyState] || re.test(this[readyState])) {
+ script[onload] = script[onreadystatechange] = null;
+ fn && fn(script);
+ script = null;
+ }
+ };
+ script.async = true;
+ script.src = src;
+ document.body.appendChild(script);
+ };
+ window.dweet_script_loader = function (srces, fn) {
+ if (typeof srces == 'string') {
+ load(srces, fn);
+ return;
+ }
+ var src = srces.shift();
+ load(src, function (script) {
+ if (srces.length) {
+ window.dweet_script_loader(srces, fn);
+ }
+ else {
+ fn && fn(script);
+ }
+ });
+ };
+ })();
+ }
+
+ function isArray(obj) {
+ return Object.prototype.toString.call( obj ) === '[object Array]'
+ }
+
+ function isFunction(obj) {
+ return typeof obj === 'function';
+ }
+
+ var dweetioClient = function () {
+ var self = this;
+ var socket;
+ var listenCallbacks = {};
+ var currentThing = lastThing;
+
+ function normalizeDweet(dweet) {
+ if (dweet.created) {
+ dweet.created = new Date(dweet.created);
+ }
+
+ return dweet;
+ }
+
+ function normalizeDweets(dweets) {
+ if (dweets instanceof Array) {
+ for (var index = 0; index < dweets.length; index++) {
+ var dweet = dweets[index];
+ normalizeDweet(dweet);
+ }
+ }
+ else {
+ normalizeDweet(dweets);
+ }
+
+ return dweets;
+ }
+
+ function parseBody(body) {
+ var responseData;
+
+ try {
+ if (typeof body == 'string' || body instanceof String) {
+ responseData = JSON.parse(body);
+ }
+ else {
+ responseData = body;
+ }
+ }
+ catch (e) {
+ }
+
+ return responseData;
+ }
+
+ function processResponse(body) {
+ var err;
+
+ var responseData = parseBody(body);
+
+ if (!responseData) {
+ err = new Error("server returned an invalid response");
+ }
+ else if (responseData["this"] == "failed") {
+ err = new Error(responseData["because"]);
+ }
+
+ return err;
+ }
+
+ function createKeyedURL(url, key) {
+ if (key) {
+ return url + (url.indexOf("?") + 1 ? "&" : "?") + "key=" + encodeURIComponent(key);
+ }
+
+ return url;
+ }
+
+ function processDweetResponse(err, callback, body) {
+ var responseData = parseBody(body);
+
+ if (!err) {
+ err = processResponse(responseData);
+ }
+
+ if (responseData && responseData["with"]) {
+ if (callback) callback(err, normalizeDweets(responseData["with"]));
+ }
+ else {
+ if (callback) callback("no response from server", undefined);
+ }
+ }
+
+ self.set_server = function (server, strictSSL) {
+ DWEET_SERVER = server;
+ STRICT_SSL = strictSSL;
+
+ if (isNode) {
+ if (strictSSL)
+ require('https').globalAgent.options.rejectUnauthorized = true;
+ else
+ require('https').globalAgent.options.rejectUnauthorized = false;
+ }
+ }
+
+ self.dweet = function (data, callback) {
+ if (currentThing) {
+ self.dweet_for(currentThing, data, callback);
+ }
+ else {
+ request({
+ url: DWEET_SERVER + "/dweet",
+ jar: true,
+ method: "POST",
+ followAllRedirects: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL,
+ json: data
+ }, function (err, response, body) {
+ var responseData = parseBody(body);
+
+ if (responseData["with"] && responseData["with"].thing != currentThing) {
+ currentThing = responseData["with"].thing;
+
+ if (isNode) {
+ require("fs").writeFile(LAST_THING_NAME, currentThing);
+ }
+ }
+
+ processDweetResponse(err, callback, responseData);
+ });
+ }
+ };
+
+ self.dweet_for = function (thing, data, key, callback) {
+ if (isFunction(key)) {
+ callback = key;
+ key = null;
+ }
+
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/dweet/for/" + thing, key),
+ jar: true,
+ method: "POST",
+ followAllRedirects: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL,
+ json: data
+ }, function (err, response, body) {
+ processDweetResponse(err, callback, body);
+ });
+ }
+
+ self.get_latest_dweet_for = function (thing, key, callback) {
+ if (isFunction(key)) {
+ callback = key;
+ key = null;
+ }
+
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/get/latest/dweet/for/" + thing, key),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ processDweetResponse(err, callback, body);
+ });
+ }
+
+ self.get_all_dweets_for = function (thing, key, callback) {
+ if (isFunction(key)) {
+ callback = key;
+ key = null;
+ }
+
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/get/dweets/for/" + thing, key),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ processDweetResponse(err, callback, body);
+ });
+ }
+
+ self.get_key_for = function (account, thingname, callback) {
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/get/key/for/" + account + '/' + thingname),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ processDweetResponse(err, callback, body);
+ });
+ }
+
+ self.create_key_for = function (account, thingname, callback) {
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/create/key/for/" + account + '/' + thingname),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ processDweetResponse(err, callback, body);
+ });
+ }
+
+ self.get_keys_for_account = function (account, callback) {
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/get/keys/for/" + account),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ processDweetResponse(err, callback, body);
+ });
+ }
+
+ self.get_keys_for_account = function (account, startPosition, endPosition, callback) {
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/get/keys/for/" + account + '?startPosition=' + startPosition + '&endPosition=' + endPosition),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ processDweetResponse(err, callback, body);
+ });
+ }
+
+
+ self.listen_for = function (thing, key, callback) {
+ if (isFunction(key)) {
+ callback = key;
+ key = null;
+ }
+
+ // Initialize our callback list
+ if (!listenCallbacks[thing]) {
+ listenCallbacks[thing] = [];
+ }
+
+ // Add this to our callbacks
+ if (listenCallbacks[thing].indexOf(callback) == -1) {
+ listenCallbacks[thing].push(callback);
+ }
+
+ function createSocket() {
+ socket = io.connect(DWEET_SERVER + "/stream");
+
+ socket.on("connect", function () {
+ // Subscribe to all of the things that we might have asked for before connecting
+ for (var id in listenCallbacks) {
+ socket.emit("subscribe", {thing: id, key: key});
+ }
+ });
+
+ socket.on("new_dweet", function (msg) {
+ if (listenCallbacks[msg.thing]) {
+ normalizeDweets(msg);
+
+ var callbacks = listenCallbacks[msg.thing];
+ for (var index = 0; index < callbacks.length; index++) {
+ callbacks[index](msg);
+ }
+ }
+ });
+ }
+
+ if (!socket) {
+ if (isNode) {
+ createSocket();
+ }
+ else {
+ dweet_script_loader([DWEET_SERVER + "/socket.io/socket.io.js"], function () {
+ io = window.io;
+ createSocket();
+ });
+ }
+ }
+ if (socket) {
+ socket.emit("subscribe", {thing: thing, key: key});
+ }
+ }
+
+ self.stop_listening = function () {
+ listenCallbacks = {};
+
+ if (socket) {
+ socket.disconnect();
+ socket = undefined;
+ }
+ }
+
+ self.stop_listening_for = function (thing) {
+ listenCallbacks[thing] = undefined;
+ delete listenCallbacks[thing];
+
+ if (socket) {
+ socket.emit("unsubscribe", {thing: thing});
+ }
+ }
+
+ self.lock = function (thing, lock, key, callback) {
+ request({
+ url: DWEET_SERVER + "/lock/" + thing + "?lock=" + lock + "&key=" + key,
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ if (!err) {
+ err = processResponse(body);
+ }
+
+ if (callback) callback(err);
+ });
+ }
+
+ self.unlock = function (thing, key, callback) {
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/unlock/" + thing, key),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ if (!err) {
+ err = processResponse(body);
+ }
+
+ if (callback) callback(err);
+ });
+ }
+
+ self.remove_lock = function (lock, key, callback) {
+ request({
+ url: DWEET_SERVER + "/remove/lock/" + lock + "?key=" + key,
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ if (!err) {
+ err = processResponse(body);
+ }
+
+ if (callback) callback(err);
+ });
+ }
+
+ self.set_alert = function(thing, recipients, condition, key, callback)
+ {
+ if(isArray(recipients))
+ {
+ recipients = recipients.join();
+ }
+
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/alert/" + encodeURIComponent(recipients) + "/when/" + thing + "/" + encodeURIComponent(condition), key),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ if (!err) {
+ err = processResponse(body);
+ }
+
+ if (callback) callback(err);
+ });
+ }
+
+ self.get_alert = function(thing, key, callback)
+ {
+ request({
+ url: createKeyedURL(DWEET_SERVER + "/get/alert/for/" + thing, key),
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ if (!err) {
+ err = processResponse(body);
+ }
+
+ var responseData = parseBody(body);
+
+ if (callback) callback(err, responseData["with"]);
+ });
+ }
+
+ self.remove_alert = function(thing, key, callback)
+ {
+ request({
+ url: DWEET_SERVER + "/remove/alert/for/" + thing + "?key=" + key,
+ jar: true,
+ timeout: REQUEST_TIMEOUT,
+ strictSSL: STRICT_SSL
+ }, function (err, response, body) {
+ if (!err) {
+ err = processResponse(body);
+ }
+
+ if (callback) callback(err);
+ });
+ }
+ };
+
+ if (isNode) {
+ module.exports = dweetioClient;
+ }
+ else {
+ window.dweetio = new dweetioClient();
+ }
+})();
\ No newline at end of file
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js
old mode 100644
new mode 100755
index 65bada2bec..dd7ab03f02
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/raw-wb-board.js
@@ -260,6 +260,7 @@ var Wb = function() {
const clearAll = tools.find('.om-icon.clear-all')
, sBtn = tools.find('.om-icon.settings');
let _firstToolItem = true;
+ let dweetIDCount = 1;
switch (role) {
case PRESENTER:
clearAll.confirmation({
@@ -369,6 +370,48 @@ var Wb = function() {
minHeight: 140
, minWidth: 255
});
+ tools.find('.om-icon.dweet').click(function(){
+ dweet = OmUtil.tmpl('#wb-dweet', 'wb-dweet'+dweetIDCount++);
+ dweet.find('.ui-dialog-titlebar-close').click(function() {
+ let id = $(this.parentElement).attr('id');
+ $("#"+id).remove();
+ });
+ dweet.draggable({
+ scroll: false
+ , handle: '.ui-dialog-titlebar'
+ , containment: 'body'
+ , start: function() {
+ if (!!$(this).css('bottom')) {
+ $(this).css('bottom', '').css(Settings.isRtl ? 'left' : 'right', '');
+ }
+ }
+ , drag: function() {
+ if ($(this).position().x + $(this).width() >= $(this).parent().width()) {
+ return false;
+ }
+ }
+ });
+ dweet.resizable({
+ minHeight: 140
+ , minWidth: 255
+ });
+ dweet.find('.update-btn').button().click(function() {
+ let id = $(this.parentElement.parentElement).attr('id');
+ let thing = $("#" + id).find('input').val();
+ let content = $("#" + id).find('textarea').val();
+ content = JSON.parse(content);
+ if(thing){
+ dweetio.dweet_for(thing, content, function(err, dweet){
+ let responseContainer = $("#"+id).find('.dweet-response');
+ if(err){
+ responseContainer.val(dweet.thing +"\n"+dweet.content+"\n"+dweet.created);
+ }
+ responseContainer.val(dweet.thing +"\n"+dweet.content+"\n"+dweet.created);
+ });
+ }
+ });
+ wbEl.append(dweet);
+ });
case NONE:
_updateZoomPanel();
zoomBar.find('.zoom-out').click(function() {
@@ -788,7 +831,11 @@ var Wb = function() {
math[0].style.display = 'none';
math[0].style.bottom = '100px';
math[0].style[(Settings.isRtl ? 'left' : 'right')] = '100px';
- wbEl.append(settings, math);
+ dweet = OmUtil.tmpl('#wb-dweet');
+ dweet[0].style.display = 'none';
+ dweet[0].style.bottom = '100px';
+ dweet[0].style[(Settings.isRtl ? 'left' : 'right')] = '100px';
+ wbEl.append(settings, math, dweet);
sc.on('scroll', scrollHandler);
}
wbEl.find('.tools').append(tools);
diff --git a/openmeetings-web/src/main/webapp/css/raw-wb.css b/openmeetings-web/src/main/webapp/css/raw-wb.css
index 286dfad0c6..51559bee82 100644
--- a/openmeetings-web/src/main/webapp/css/raw-wb.css
+++ b/openmeetings-web/src/main/webapp/css/raw-wb.css
@@ -158,6 +158,10 @@ html[dir="rtl"] .room-block .sb-wb .wb-block {
.room-block .sb-wb .wb-block .om-icon.big.prev::before {
content: '\f053';
}
+.room-block .sb-wb .wb-block .tools .om-icon.big.dweet::before {
+ content: '\f1e6';
+ color: red;
+}
.wb-tool-settings, .wb-formula {
position: absolute;
z-index: 95;
@@ -354,6 +358,27 @@ html[dir="rtl"] .room-block .sb-wb .wb-block {
#wb-rename-menu {
display: none;
}
+.dweet-btn {
+ float: right;
+ margin: 10px;
+}
+.dweet-input {
+ resize: vertical !important;
+}
+
+.dweet-inline {
+ display: inline-block;
+}
+
+.dweet-inline.input {
+ padding-left: 1rem;
+}
+
+.wb-dweet {
+ bottom: 100px;
+ right: 100px;
+ position: absolute !important;
+}
/* MathJax*/
.MathJax_SVG_Display {text-align: center; margin: 1em 0em; position: relative; display: block!important; text-indent: 0; max-width: none; max-height: none; min-width: 0; min-height: 0; width: 100%}
.MathJax_SVG .MJX-monospace {font-family: monospace}