This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 4cb9d89  [OPENMEETINGS-2000] moving JS code to npm (incomplete, broken)
4cb9d89 is described below

commit 4cb9d891257798f04e006135de6865410e053361
Author: Maxim Solodovnik <[email protected]>
AuthorDate: Sat Dec 26 01:19:32 2020 +0700

    [OPENMEETINGS-2000] moving JS code to npm (incomplete, broken)
---
 openmeetings-web/src/main/front/wb/package.json    |   2 +-
 .../src/main/front/wb/src/raw-tool-ellipse.js      |  27 -----
 .../src/main/front/wb/src/raw-tool-line.js         |  21 ----
 .../src/main/front/wb/src/raw-tool-paint.js        |  20 ----
 .../src/main/front/wb/src/raw-tool-rect.js         |  34 ------
 .../src/main/front/wb/src/raw-tool-shape-base.js   |   8 --
 .../src/main/front/wb/src/raw-tool-shape.js        |  61 ----------
 .../src/main/front/wb/src/raw-tool-text.js         | 110 -----------------
 .../src/main/front/wb/src/raw-tool-textbox.js      |  42 -------
 .../src/main/front/wb/src/raw-tool-uline.js        |   7 --
 .../src/main/front/wb/src/raw-tool-whiteout.js     |  23 ----
 openmeetings-web/src/main/front/wb/src/wb-area.js  | 104 ++++++++--------
 .../src/main/front/wb/src/wb-tool-apointer.js      |  11 +-
 .../wb/src/{raw-tool-arrow.js => wb-tool-arrow.js} |  49 ++++----
 .../src/main/front/wb/src/wb-tool-base.js          |   6 +-
 .../src/main/front/wb/src/wb-tool-ellipse.js       |  32 +++++
 .../src/main/front/wb/src/wb-tool-line.js          |  27 +++++
 .../src/main/front/wb/src/wb-tool-paint.js         |  26 ++++
 .../{raw-tool-pointer.js => wb-tool-pointer.js}    |  14 ++-
 .../src/main/front/wb/src/wb-tool-rect.js          |  40 +++++++
 .../src/main/front/wb/src/wb-tool-shape-base.js    |  20 ++++
 .../src/main/front/wb/src/wb-tool-shape.js         |  68 +++++++++++
 .../src/main/front/wb/src/wb-tool-text.js          | 132 +++++++++++++++++++++
 .../src/main/front/wb/src/wb-tool-textbox.js       |  51 ++++++++
 .../src/main/front/wb/src/wb-tool-uline.js         |  10 ++
 .../src/main/front/wb/src/wb-tool-util.js          |   1 +
 .../src/main/front/wb/src/wb-tool-whiteout.js      |  31 +++++
 openmeetings-web/src/main/front/wb/src/wb-tools.js |  39 ++++--
 openmeetings-web/src/main/front/wb/src/wb-zoom.js  |  60 +++++-----
 openmeetings-web/src/main/front/wb/src/wb.js       |  92 +++++++-------
 openmeetings-web/src/main/webapp/css/raw-tree.css  |   2 +-
 31 files changed, 634 insertions(+), 536 deletions(-)

diff --git a/openmeetings-web/src/main/front/wb/package.json 
b/openmeetings-web/src/main/front/wb/package.json
index 13c30a5..7f5b724 100644
--- a/openmeetings-web/src/main/front/wb/package.json
+++ b/openmeetings-web/src/main/front/wb/package.json
@@ -16,6 +16,6 @@
     "tinyify": "^3.0.0"
   },
   "dependencies": {
-    "fabric": "^4.3.0"
+    "fabric": "^3.6.6"
   }
 }
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-ellipse.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-ellipse.js
deleted file mode 100644
index ba28275..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-ellipse.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Ellipse = function(wb, s, sBtn) {
-       const ellipse = Rect(wb, s, sBtn);
-       ellipse.createShape = function() {
-               ellipse.obj = new fabric.Ellipse({
-                       strokeWidth: ellipse.stroke.width
-                       , fill: ellipse.fill.enabled ? ellipse.fill.color : 
'rgba(0,0,0,0)'
-                       , stroke: ellipse.stroke.enabled ? ellipse.stroke.color 
: 'rgba(0,0,0,0)'
-                       , opacity: ellipse.opacity
-                       , left: ellipse.orig.x
-                       , top: ellipse.orig.y
-                       , rx: 0
-                       , ry: 0
-                       , originX: 'center'
-                       , originY: 'center'
-                       , omType: 'ellipse'
-               });
-               return ellipse.obj;
-       };
-       ellipse.updateShape = function(pointer) {
-               ellipse.obj.set({
-                       rx: Math.abs(ellipse.orig.x - pointer.x)
-                       , ry: Math.abs(ellipse.orig.y - pointer.y)
-               });
-       };
-       return ellipse;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-line.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-line.js
deleted file mode 100644
index 7bced0d..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-line.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Line = function(wb, s, sBtn) {
-       const line = Shape(wb, sBtn);
-       line.createShape = function() {
-               line.obj = new fabric.Line([line.orig.x, line.orig.y, 
line.orig.x, line.orig.y], {
-                       strokeWidth: line.stroke.width
-                       , fill: line.stroke.color
-                       , stroke: line.stroke.color
-                       , opacity: line.opacity
-                       , omType: 'line'
-               });
-               return line.obj;
-       };
-       line.internalActivate = function() {
-               ToolUtil.enableLineProps(s, line);
-       };
-       line.updateShape = function(pointer) {
-               line.obj.set({ x2: pointer.x, y2: pointer.y });
-       };
-       return line;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-paint.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-paint.js
deleted file mode 100644
index 080e73c..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-paint.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Paint = function(wb, s, sBtn) {
-       const paint = ShapeBase(wb);
-       paint.activate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.isDrawingMode = true;
-                       canvas.freeDrawingBrush.width = paint.stroke.width;
-                       canvas.freeDrawingBrush.color = paint.stroke.color;
-                       canvas.freeDrawingBrush.opacity = paint.opacity;
-               });
-               ToolUtil.enableLineProps(s, paint).o.prop('disabled', true);
-               VideoUtil.highlight(sBtn.removeClass('disabled'), 'bg-warning', 
5);
-       };
-       paint.deactivate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.isDrawingMode = false;
-               });
-       };
-       return paint;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-rect.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-rect.js
deleted file mode 100644
index 1745218..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-rect.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Rect = function(wb, s, sBtn) {
-       const rect = Shape(wb, sBtn);
-       rect.createShape = function() {
-               rect.obj = new fabric.Rect({
-                       strokeWidth: rect.stroke.width
-                       , fill: rect.fill.enabled ? rect.fill.color : 
'rgba(0,0,0,0)'
-                       , stroke: rect.stroke.enabled ? rect.stroke.color : 
'rgba(0,0,0,0)'
-                       , opacity: rect.opacity
-                       , left: rect.orig.x
-                       , top: rect.orig.y
-                       , width: 0
-                       , height: 0
-                       , omType: 'rect'
-               });
-               return rect.obj;
-       };
-       rect.internalActivate = function() {
-               ToolUtil.enableAllProps(s, rect);
-       };
-       rect.updateShape = function(pointer) {
-               if (rect.orig.x > pointer.x) {
-                       rect.obj.set({ left: pointer.x });
-               }
-               if (rect.orig.y > pointer.y) {
-                       rect.obj.set({ top: pointer.y });
-               }
-               rect.obj.set({
-                       width: Math.abs(rect.orig.x - pointer.x)
-                       , height: Math.abs(rect.orig.y - pointer.y)
-               });
-       };
-       return rect;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-shape-base.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-shape-base.js
deleted file mode 100644
index 8347521..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-shape-base.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var ShapeBase = function() {
-       const base = Base();
-       base.fill = {enabled: true, color: '#FFFF33'};
-       base.stroke = {enabled: true, color: '#FF6600', width: 5};
-       base.opacity = 1;
-       return base;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-shape.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-shape.js
deleted file mode 100644
index 7de0358..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-shape.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Shape = function(wb, sBtn) {
-       const shape = ShapeBase(wb);
-       shape.obj = null;
-       shape.isDown = false;
-       shape.orig = {x: 0, y: 0};
-
-       shape.add2Canvas = function(canvas) {
-               canvas.add(shape.obj);
-       }
-       shape.mouseDown = function(o) {
-               const canvas = this
-                       , pointer = canvas.getPointer(o.e);
-               shape.isDown = true;
-               shape.orig = {x: pointer.x, y: pointer.y};
-               shape.createShape(canvas);
-               shape.add2Canvas(canvas);
-       };
-       shape.mouseMove = function(o) {
-               const canvas = this;
-               if (!shape.isDown) {
-                       return;
-               }
-               const pointer = canvas.getPointer(o.e);
-               shape.updateShape(pointer);
-               canvas.requestRenderAll();
-       };
-       shape.updateCreated = function(o) {
-               return o;
-       };
-       shape.mouseUp = function() {
-               const canvas = this;
-               shape.isDown = false;
-               shape.obj.setCoords();
-               shape.obj.selectable = false;
-               canvas.requestRenderAll();
-               shape.objectCreated(shape.obj, canvas);
-       };
-       shape.internalActivate = function() {};
-       shape.activate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.on({
-                               'mouse:down': shape.mouseDown
-                               , 'mouse:move': shape.mouseMove
-                               , 'mouse:up': shape.mouseUp
-                       });
-               });
-               shape.internalActivate();
-               VideoUtil.highlight(sBtn.removeClass('disabled'), 'bg-warning', 
5);
-       };
-       shape.deactivate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.off({
-                               'mouse:down': shape.mouseDown
-                               , 'mouse:move': shape.mouseMove
-                               , 'mouse:up': shape.mouseUp
-                       });
-               });
-       };
-       return shape;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-text.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-text.js
deleted file mode 100644
index fb9da78..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-text.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Text = function(wb, s, sBtn) {
-       const text = ShapeBase();
-       text.obj = null;
-       text.omType = 'i-text';
-       text.fill.color = '#000000';
-       text.stroke.enabled = false;
-       text.stroke.width = 50; //fontSize
-       text.stroke.color = '#000000';
-       text.style = {bold: false, italic: false};
-
-       function __valid(o) {
-               return !!o && text.omType === o.omType;
-       }
-       function __getObj(canvas, o) {
-               if (__valid(o)) {
-                       return o;
-               } else {
-                       const _o = canvas.getActiveObject();
-                       return __valid(_o) ? _o : null;
-               }
-       }
-       text.createTextObj = function(canvas, pointer) {
-               return new fabric.IText('', {
-                       left: pointer.x
-                       , top: pointer.y
-                       , padding: 7
-                       , omType: text.omType
-                       , fill: text.fill.enabled ? text.fill.color : 
'rgba(0,0,0,0)'
-                       , stroke: text.stroke.enabled ? text.stroke.color : 
'rgba(0,0,0,0)'
-                       , fontSize: text.stroke.width
-                       , fontFamily: text.fontFamily
-                       , opacity: text.opacity
-               });
-       };
-       text._onMouseDown = function() {
-               text.obj.enterEditing();
-       };
-       text._onActivate = function() {
-               WbArea.removeDeleteHandler();
-       };
-       text._onDeactivate = function() {
-               WbArea.addDeleteHandler();
-       };
-       text.mouseDown = function(o) {
-               const canvas = this
-                       , pointer = canvas.getPointer(o.e)
-                       , ao = __getObj(canvas, o.target);
-               if (!!ao) {
-                       text.obj = ao;
-               } else {
-                       text.obj = text.createTextObj(canvas, pointer);
-                       if (text.style.bold) {
-                               text.obj.fontWeight = 'bold'
-                       }
-                       if (text.style.italic) {
-                               text.obj.fontStyle = 'italic'
-                       }
-                       canvas.add(text.obj).setActiveObject(text.obj);
-               }
-               text._onMouseDown();
-       };
-       text._editable = function(o) {
-               return text.omType === o.omType;
-       }
-       text.activate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.on('mouse:down', text.mouseDown);
-                       canvas.on('mouse:dblclick', text.doubleClick);
-                       canvas.selection = true;
-                       canvas.forEachObject(function(o) {
-                               if (text._editable(o)) {
-                                       o.selectable = true;
-                                       o.editable = true;
-                               }
-                       });
-               });
-               text.fontFamily = $('#wb-text-style-block').css('font-family');
-               ToolUtil.enableAllProps(s, text);
-               const b = s.find('.wb-prop-b').button("enable");
-               if (text.style.bold) {
-                       b.addClass('ui-state-active selected');
-               } else {
-                       b.removeClass('ui-state-active selected');
-               }
-               const i = s.find('.wb-prop-i').button("enable");
-               if (text.style.italic) {
-                       i.addClass('ui-state-active selected');
-               } else {
-                       i.removeClass('ui-state-active selected');
-               }
-               text._onActivate();
-               VideoUtil.highlight(sBtn.removeClass('disabled'), 'bg-warning', 
5);
-       };
-       text.deactivate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.off('mouse:down', text.mouseDown);
-                       canvas.off('mouse:dblclick', text.doubleClick);
-                       canvas.selection = false;
-                       canvas.forEachObject(function(o) {
-                               if (text.omType === o.omType) {
-                                       o.selectable = false;
-                                       o.editable = false;
-                               }
-                       });
-               });
-               text._onDeactivate();
-       };
-       return text;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-textbox.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-textbox.js
deleted file mode 100644
index 0fa8c78..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-textbox.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Textbox = function(wb, s, sBtn) {
-       const text = Text(wb, s, sBtn);
-       text.omType = 'textbox';
-
-       text.createTextObj = function(canvas, pointer) {
-               return new fabric.Textbox('', {
-                       left: pointer.x
-                       , top: pointer.y
-                       , padding: 7
-                       , fill: text.fill.enabled ? text.fill.color : 
'rgba(0,0,0,0)'
-                       , stroke: text.stroke.enabled ? text.stroke.color : 
'rgba(0,0,0,0)'
-                       //, strokeWidth: text.stroke.width
-                       , fontSize: text.stroke.width
-                       , omType: text.omType
-                       , fontFamily: text.fontFamily
-                       , opacity: text.opacity
-                       , breakWords: true
-                       , width: canvas.width / 4
-                       , lockScalingX: false
-                       , lockScalingY: true
-               });
-       };
-       text.doubleClick = function(e) {
-               const ao = e.target;
-               if (!!ao && text.omType === ao.omType) {
-                       text.obj = ao;
-                       text.obj.enterEditing();
-                       WbArea.removeDeleteHandler();
-               }
-       };
-       text._onMouseDown = function() {
-               WbArea.addDeleteHandler();
-       };
-       text._onActivate = function() {
-               WbArea.addDeleteHandler();
-       };
-       text._onDeactivate = function() {
-               WbArea.addDeleteHandler();
-       };
-       return text;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-uline.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-uline.js
deleted file mode 100644
index a90bc0b..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-uline.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var ULine = function(wb, s, sBtn) {
-       const uline = Line(wb, s, sBtn);
-       uline.stroke.width = 20;
-       uline.opacity = .5;
-       return uline;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-whiteout.js 
b/openmeetings-web/src/main/front/wb/src/raw-tool-whiteout.js
deleted file mode 100644
index c44dd78..0000000
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-whiteout.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Whiteout = function(wb, s, sBtn) {
-       const wout = ShapeBase(wb);
-       wout.fill.color = '#FFFFFF';
-       wout.stroke.color = '#FFFFFF';
-       wout.stroke.width = 25;
-       wout.activate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.isDrawingMode = true;
-                       canvas.freeDrawingBrush.width = wout.stroke.width;
-                       canvas.freeDrawingBrush.color = wout.stroke.color;
-                       canvas.freeDrawingBrush.opacity = wout.opacity;
-               });
-               ToolUtil.disableAllProps(s);
-               sBtn.addClass('disabled');
-       };
-       wout.deactivate = function() {
-               wb.eachCanvas(function(canvas) {
-                       canvas.isDrawingMode = false;
-               });
-       };
-       return wout;
-};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-area.js 
b/openmeetings-web/src/main/front/wb/src/wb-area.js
index d2d200c..6fb7e2e 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-area.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-area.js
@@ -9,59 +9,6 @@ const arrowImg = new Image(), delImg = new Image();
 arrowImg.src = 
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAICAYAAADqSp8ZAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAygAAAMoBawMUsgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFsSURBVCiRrdI/SEJRFMfx37lPGxqKoGwxKJoaImhpCf8NEUFL9WgLUrPnIyEIa6reVEPQn0GeWDS4NDQETQ2JT4waojUoHBqCoJKWINB3720yIhGl+q7ncj5nuIQ6jWiaq1xmU4IwBACQ5GCAU5D8IECRAkUQzt8V++wmlSrX20e1BoFIrFdwHidIIQhH5O68sgzD/vnOF4m0QyijJGgMQIHZtJdJJ4oNg6qqNr20dKwBaOWKvZFPpZ7qXV3JH4wNSMbjJHGZ7XIlYRiiFkiBsL4CphwLwbck5E7uwMw3ClXD2iRImY
 [...]
 delImg.src = 
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAAGgrv1cAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADNQTFRFAAAA4j094j094j094j094j094j094j094j094j094j094j094j094j094j094j094j09hIdAxgAAABB0Uk5TABAgMEBQYHCAj5+vv8/f7yMagooAAADXSURBVBgZBcEBYoQgDACw1DJETmz//9olwGn6AAAbBxoiSACTpCTtJd02smg+MPoef7UgnpPQeVM42Vg02kl+qAPeE2B19wYAgO83xi6ggRMoBfuvsUSxp+vPjag98VqwC8oI9ozC5rMnUVbw5ITID94Fo4D4umsAwN/+urvfOwDg6d8FiFUnALPnkwCs6zvg+UKcSmD3ZBWyL4hTye4J3s16AXG6J+D+uD/A7vtUAutF
 [...]
 ;
-
-// Fabric overrides (should be kept up-to-date on fabric.js updates)
-if ('function' !== typeof(window.originalDrawControl)) {
-       window.originalDrawControl = fabric.Object.prototype._drawControl;
-       window.originalGetRotatedCornerCursor = 
fabric.Canvas.prototype._getRotatedCornerCursor;
-       window.originalGetActionFromCorner = 
fabric.Canvas.prototype._getActionFromCorner;
-       window.originalGetCornerCursor = 
fabric.Canvas.prototype.getCornerCursor;
-       fabric.Object.prototype._drawControl = function(control, ctx, 
methodName, left, top, styleOverride) {
-               switch (control) {
-                       case 'mtr':
-                       {
-                               const x = left + (this.cornerSize - 
arrowImg.width) / 2
-                                       , y = top + (this.cornerSize - 
arrowImg.height) / 2;
-                               ctx.drawImage(arrowImg, x, y);
-                       }
-                               break;
-                       case 'tr':
-                       {
-                               if (role === Role.PRESENTER) {
-                                       const x = left + (this.cornerSize - 
delImg.width) / 2
-                                               , y = top + (this.cornerSize - 
delImg.height) / 2;
-                                       ctx.drawImage(delImg, x, y);
-                               } else {
-                                       window.originalDrawControl.call(this, 
control, ctx, methodName, left, top, styleOverride);
-                               }
-                       }
-                               break;
-                       default:
-                               window.originalDrawControl.call(this, control, 
ctx, methodName, left, top, styleOverride);
-                               break;
-               }
-       };
-       fabric.Canvas.prototype._getRotatedCornerCursor = function(corner, 
target, e) {
-               if (role === Role.PRESENTER && 'tr' === corner) {
-                       return 'pointer';
-               }
-               return window.originalGetRotatedCornerCursor.call(this, corner, 
target, e);
-       };
-       fabric.Canvas.prototype._getActionFromCorner = 
function(alreadySelected, corner, e) {
-               if (role === Role.PRESENTER && 'tr' === corner) {
-                       _performDelete();
-                       return 'none';
-               }
-               return window.originalGetActionFromCorner.call(this, 
alreadySelected, corner, e);
-       };
-       fabric.Canvas.prototype.getCornerCursor = function(corner, target, e) {
-               if (role === Role.PRESENTER && 'tr' === corner) {
-                       return 'pointer';
-               }
-               return window.originalGetCornerCursor.call(this, corner, 
target, e);
-       }
-}
-
 function __getWbTabId(id) {
        return 'wb-tab-' + id;
 }
@@ -120,6 +67,57 @@ module.exports = class DrawWbArea extends WbAreaBase {
                const self = this;
                let scroll, role = Role.NONE, _inited = false;
 
+               // Fabric overrides (should be kept up-to-date on fabric.js 
updates)
+               if ('function' !== typeof(window.originalDrawControl)) {
+                       window.originalDrawControl = 
fabric.Object.prototype._drawControl;
+                       window.originalGetRotatedCornerCursor = 
fabric.Canvas.prototype._getRotatedCornerCursor;
+                       window.originalGetActionFromCorner = 
fabric.Canvas.prototype._getActionFromCorner;
+                       window.originalGetCornerCursor = 
fabric.Canvas.prototype.getCornerCursor;
+               }
+               fabric.Object.prototype._drawControl = function(control, ctx, 
methodName, left, top, styleOverride) {
+                       switch (control) {
+                               case 'mtr':
+                               {
+                                       const x = left + (this.cornerSize - 
arrowImg.width) / 2
+                                               , y = top + (this.cornerSize - 
arrowImg.height) / 2;
+                                       ctx.drawImage(arrowImg, x, y);
+                               }
+                                       break;
+                               case 'tr':
+                               {
+                                       if (role === Role.PRESENTER) {
+                                               const x = left + 
(this.cornerSize - delImg.width) / 2
+                                                       , y = top + 
(this.cornerSize - delImg.height) / 2;
+                                               ctx.drawImage(delImg, x, y);
+                                       } else {
+                                               
window.originalDrawControl.call(this, control, ctx, methodName, left, top, 
styleOverride);
+                                       }
+                               }
+                                       break;
+                               default:
+                                       window.originalDrawControl.call(this, 
control, ctx, methodName, left, top, styleOverride);
+                                       break;
+                       }
+               };
+               fabric.Canvas.prototype._getRotatedCornerCursor = 
function(corner, target, e) {
+                       if (role === Role.PRESENTER && 'tr' === corner) {
+                               return 'pointer';
+                       }
+                       return window.originalGetRotatedCornerCursor.call(this, 
corner, target, e);
+               };
+               fabric.Canvas.prototype._getActionFromCorner = 
function(alreadySelected, corner, e) {
+                       if (role === Role.PRESENTER && 'tr' === corner) {
+                               _performDelete();
+                               return 'none';
+                       }
+                       return window.originalGetActionFromCorner.call(this, 
alreadySelected, corner, e);
+               };
+               fabric.Canvas.prototype.getCornerCursor = function(corner, 
target, e) {
+                       if (role === Role.PRESENTER && 'tr' === corner) {
+                               return 'pointer';
+                       }
+                       return window.originalGetCornerCursor.call(this, 
corner, target, e);
+               }
                function _performDelete() {
                        const wb = _getActive().data()
                                , canvas = wb.getCanvas();
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-apointer.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-apointer.js
index c94a442..3f4f7c2 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tool-apointer.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-apointer.js
@@ -22,8 +22,8 @@ module.exports = class APointer extends WbToolBase {
                                , y: ptr.y
                                , user: self.user
                        };
-                       obj.uid = self.objectCreated(obj, canvas);
-                       self.create(canvas, obj);
+                       obj.uid = self.objectCreated.call(self, obj, canvas);
+                       self.create.call(self, canvas, obj);
                }
 
                this.activate = () => {
@@ -43,8 +43,9 @@ module.exports = class APointer extends WbToolBase {
        }
 
        create(canvas, o) {
+               const zoom = this.wb.getZoom();
                fabric.Image.fromURL('./css/images/pointer.png', function(img) {
-                       const scale = 1. / this.wb.getZoom();
+                       const scale = 1. / zoom;
                        img.set({
                                left:15
                                , originX: 'right'
@@ -54,7 +55,7 @@ module.exports = class APointer extends WbToolBase {
                                radius: 20
                                , stroke: '#ff6600'
                                , strokeWidth: 2
-                               , fill: 'rgba(0,0,0,0)'
+                               , fill: ToolUtil.noColor
                                , originX: 'center'
                                , originY: 'center'
                        });
@@ -62,7 +63,7 @@ module.exports = class APointer extends WbToolBase {
                                radius: 6
                                , stroke: '#ff6600'
                                , strokeWidth: 2
-                               , fill: 'rgba(0,0,0,0)'
+                               , fill: ToolUtil.noColor
                                , originX: 'center'
                                , originY: 'center'
                        });
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-arrow.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-arrow.js
similarity index 52%
rename from openmeetings-web/src/main/front/wb/src/raw-tool-arrow.js
rename to openmeetings-web/src/main/front/wb/src/wb-tool-arrow.js
index a921acd..0d44346 100644
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-arrow.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-arrow.js
@@ -1,9 +1,19 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Arrow = function(wb, s, sBtn) {
-       const arrow = Line(wb, s, sBtn);
-       arrow.stroke.width = 20;
-       arrow.createShape = function() {
-               arrow.obj = new fabric.Polygon([
+const Line = require('./wb-tool-line');
+const ToolUtil = require('./wb-tool-util');
+
+module.exports = class Arrow extends Line {
+       constructor(wb, settings, sBtn) {
+               super(wb, settings, sBtn)
+               this.stroke.width = 20;
+
+               this.internalActivate = () => {
+                       ToolUtil.enableAllProps(settings, this);
+               };
+       }
+
+       createShape() {
+               this.obj = new fabric.Polygon([
                        {x: 0, y: 0},
                        {x: 0, y: 0},
                        {x: 0, y: 0},
@@ -12,23 +22,22 @@ var Arrow = function(wb, s, sBtn) {
                        {x: 0, y: 0},
                        {x: 0, y: 0}]
                        , {
-                               left: arrow.orig.x
-                               , top: arrow.orig.y
+                               left: this.orig.x
+                               , top: this.orig.y
                                , angle: 0
                                , strokeWidth: 2
-                               , fill: arrow.fill.enabled ? arrow.fill.color : 
'rgba(0,0,0,0)'
-                               , stroke: arrow.stroke.enabled ? 
arrow.stroke.color : 'rgba(0,0,0,0)'
-                               , opacity: arrow.opacity
+                               , fill: this.fill.enabled ? this.fill.color : 
ToolUtil.noColor
+                               , stroke: this.stroke.enabled ? 
this.stroke.color : ToolUtil.noColor
+                               , opacity: this.opacity
                                , omType: 'arrow'
                        });
+       }
 
-               return arrow.obj;
-       };
-       arrow.updateShape = function(pointer) {
-               const dx = pointer.x - arrow.orig.x
-                       , dy = pointer.y - arrow.orig.y
+       updateShape(pointer) {
+               const dx = pointer.x - this.orig.x
+                       , dy = pointer.y - this.orig.y
                        , d = Math.sqrt(dx * dx + dy * dy)
-                       , sw = arrow.stroke.width
+                       , sw = this.stroke.width
                        , hl = sw * 3
                        , h = 1.5 * sw
                        , points = [
@@ -39,7 +48,7 @@ var Arrow = function(wb, s, sBtn) {
                                {x: Math.max(0, d - hl), y: 0},
                                {x: Math.max(0, d - hl), y: sw / 2},
                                {x: 0, y: sw / 2}];
-               arrow.obj.set({
+               this.obj.set({
                        points: points
                        , angle: Math.atan2(dy, dx) * 180 / Math.PI
                        , width: d
@@ -51,9 +60,5 @@ var Arrow = function(wb, s, sBtn) {
                                y: h / 2
                        }
                });
-       };
-       arrow.internalActivate = function() {
-               ToolUtil.enableAllProps(s, arrow);
-       };
-       return arrow;
+       }
 };
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-base.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-base.js
index 5c4eb33..b0a8df7 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tool-base.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-base.js
@@ -1,9 +1,9 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
 module.exports = class WbToolBase {
        objectCreated(o, canvas) {
-               this.uid = uuidv4();
-               this.slide = canvas.slide;
-               canvas.trigger("wb:object:created", o);
+               o.uid = uuidv4();
+               o.slide = canvas.slide;
+               canvas.fire("wb:object:created", o);
                return o.uid;
        }
 };
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-ellipse.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-ellipse.js
new file mode 100644
index 0000000..ecdfb28
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-ellipse.js
@@ -0,0 +1,32 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const Rect = require('./wb-tool-rect');
+const ToolUtil = require('./wb-tool-util');
+
+module.exports = class Ellipse extends Rect {
+       constructor(wb, settings, sBtn) {
+               super(wb, settings, sBtn);
+       }
+
+       createShape() {
+               this.obj = new fabric.Ellipse({
+                       strokeWidth: this.stroke.width
+                       , fill: this.fill.enabled ? this.fill.color : 
ToolUtil.noColor
+                       , stroke: this.stroke.enabled ? this.stroke.color : 
ToolUtil.noColor
+                       , opacity: this.opacity
+                       , left: this.orig.x
+                       , top: this.orig.y
+                       , rx: 0
+                       , ry: 0
+                       , originX: 'center'
+                       , originY: 'center'
+                       , omType: 'ellipse'
+               });
+       }
+
+       updateShape(pointer) {
+               this.obj.set({
+                       rx: Math.abs(this.orig.x - pointer.x)
+                       , ry: Math.abs(this.orig.y - pointer.y)
+               });
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-line.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-line.js
new file mode 100644
index 0000000..1a24bf2
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-line.js
@@ -0,0 +1,27 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const WbShape = require('./wb-tool-shape');
+const ToolUtil = require('./wb-tool-util');
+
+module.exports = class Line extends WbShape {
+       constructor(wb, settings, sBtn) {
+               super(wb, sBtn);
+
+               this.internalActivate = () => {
+                       ToolUtil.enableLineProps(settings, this);
+               };
+       }
+
+       createShape() {
+               this.obj = new fabric.Line([this.orig.x, this.orig.y, 
this.orig.x, this.orig.y], {
+                       strokeWidth: this.stroke.width
+                       , fill: this.stroke.color
+                       , stroke: this.stroke.color
+                       , opacity: this.opacity
+                       , omType: 'line'
+               });
+       }
+
+       updateShape(pointer) {
+               this.obj.set({ x2: pointer.x, y2: pointer.y });
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-paint.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-paint.js
new file mode 100644
index 0000000..d87e1b8
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-paint.js
@@ -0,0 +1,26 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const WbShapeBase = require('./wb-tool-shape-base');
+const ToolUtil = require('./wb-tool-util');
+
+module.exports = class Paint extends WbShapeBase {
+       constructor(wb, s, sBtn) {
+               super();
+
+               const self = this;
+               this.activate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.isDrawingMode = true;
+                               canvas.freeDrawingBrush.width = 
self.stroke.width;
+                               canvas.freeDrawingBrush.color = 
self.stroke.color;
+                               canvas.freeDrawingBrush.opacity = self.opacity;
+                       });
+                       ToolUtil.enableLineProps(s, self).o.prop('disabled', 
true);
+                       VideoUtil.highlight(sBtn.removeClass('disabled'), 
'bg-warning', 5);
+               };
+               this.deactivate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.isDrawingMode = false;
+                       });
+               };
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/raw-tool-pointer.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-pointer.js
similarity index 73%
rename from openmeetings-web/src/main/front/wb/src/raw-tool-pointer.js
rename to openmeetings-web/src/main/front/wb/src/wb-tool-pointer.js
index 7e5b452..2221c47 100644
--- a/openmeetings-web/src/main/front/wb/src/raw-tool-pointer.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-pointer.js
@@ -1,7 +1,9 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Pointer = function(wb, s, sBtn) {
-       return {
-               activate: function() {
+const ToolUtil = require('./wb-tool-util');
+
+module.exports = class Pointer {
+       constructor(wb, s, sBtn) {
+               this.activate = () => {
                        wb.eachCanvas(function(canvas) {
                                canvas.selection = true;
                                canvas.forEachObject(function(o) {
@@ -10,8 +12,8 @@ var Pointer = function(wb, s, sBtn) {
                        });
                        ToolUtil.disableAllProps(s);
                        sBtn.addClass('disabled');
-               }
-               , deactivate: function() {
+               };
+               this.deactivate = () => {
                        wb.eachCanvas(function(canvas) {
                                canvas.selection = false;
                                canvas.forEachObject(function(o) {
@@ -19,5 +21,5 @@ var Pointer = function(wb, s, sBtn) {
                                });
                        });
                }
-       };
+       }
 };
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-rect.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-rect.js
new file mode 100644
index 0000000..f52930d
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-rect.js
@@ -0,0 +1,40 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const WbShape = require('./wb-tool-shape');
+const ToolUtil = require('./wb-tool-util');
+
+module.exports = class Rect extends WbShape {
+       constructor(wb, settings, sBtn) {
+               super(wb, sBtn);
+
+               this.internalActivate = () => {
+                       ToolUtil.enableAllProps(settings, this);
+               };
+       }
+
+       createShape() {
+               this.obj = new fabric.Rect({
+                       strokeWidth: this.stroke.width
+                       , fill: this.fill.enabled ? this.fill.color : 
ToolUtil.noColor
+                       , stroke: this.stroke.enabled ? this.stroke.color : 
ToolUtil.noColor
+                       , opacity: this.opacity
+                       , left: this.orig.x
+                       , top: this.orig.y
+                       , width: 0
+                       , height: 0
+                       , omType: 'rect'
+               });
+       }
+
+       updateShape(pointer) {
+               if (this.orig.x > pointer.x) {
+                       this.obj.set({ left: pointer.x });
+               }
+               if (this.orig.y > pointer.y) {
+                       this.obj.set({ top: pointer.y });
+               }
+               this.obj.set({
+                       width: Math.abs(this.orig.x - pointer.x)
+                       , height: Math.abs(this.orig.y - pointer.y)
+               });
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-shape-base.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-shape-base.js
new file mode 100644
index 0000000..995baab
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-shape-base.js
@@ -0,0 +1,20 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const WbToolBase = require('./wb-tool-base');
+
+module.exports = class ShapeBase extends WbToolBase {
+       constructor() {
+               super();
+               Object.assign(this, {
+                       fill: {
+                               enabled: true
+                               , color: '#FFFF33'
+                       }
+                       , stroke: {
+                               enabled: true
+                               , color: '#FF6600'
+                               , width: 5
+                       }
+                       , opacity: 1
+               });
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-shape.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-shape.js
new file mode 100644
index 0000000..1386cc8
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-shape.js
@@ -0,0 +1,68 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const WbShapeBase = require('./wb-tool-shape-base');
+
+module.exports = class WbShape extends WbShapeBase {
+       constructor(wb, sBtn) {
+               super();
+               Object.assign(this, {
+                       obj: null
+                       , isDown: false
+                       , orig: {x: 0, y: 0}
+               });
+
+               const self = this;
+               function _add2Canvas(canvas) {
+                       canvas.add(self.obj);
+               }
+               function _mouseDown(o) {
+                       const canvas = this
+                               , pointer = canvas.getPointer(o.e);
+                       self.isDown = true;
+                       self.orig = {x: pointer.x, y: pointer.y};
+                       self.createShape.call(self, canvas);
+                       _add2Canvas(canvas);
+               };
+               function _mouseMove(o) {
+                       const canvas = this;
+                       if (!self.isDown) {
+                               return;
+                       }
+                       const pointer = canvas.getPointer(o.e);
+                       self.updateShape.call(self, pointer);
+                       canvas.requestRenderAll();
+               };
+               function _mouseUp() {
+                       const canvas = this;
+                       self.isDown = false;
+                       self.obj.setCoords();
+                       self.obj.selectable = false;
+                       canvas.requestRenderAll();
+                       self.objectCreated(self.obj, canvas);
+               };
+
+               this.activate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.on({
+                                       'mouse:down': _mouseDown
+                                       , 'mouse:move': _mouseMove
+                                       , 'mouse:up': _mouseUp
+                               });
+                       });
+                       this.internalActivate();
+                       VideoUtil.highlight(sBtn.removeClass('disabled'), 
'bg-warning', 5);
+               };
+               this.deactivate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.off({
+                                       'mouse:down': _mouseDown
+                                       , 'mouse:move': _mouseMove
+                                       , 'mouse:up': _mouseUp
+                               });
+                       });
+               };
+       }
+
+       createShape() {}
+       updateShape() {}
+       internalActivate() {}
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-text.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-text.js
new file mode 100644
index 0000000..eee99c1
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-text.js
@@ -0,0 +1,132 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const ShapeBase = require('./wb-tool-shape-base');
+const ToolUtil = require('./wb-tool-util');
+require('fabric');
+
+module.exports = class Text extends ShapeBase {
+       constructor(wb, settings, sBtn) {
+               super();
+               Object.assign(this, {
+                       obj: null
+                       , omType: 'i-text'
+                       , style: {
+                               bold: false
+                               , italic: false
+                       }
+               });
+               this.fill.color = '#000000';
+               Object.assign(this.stroke, {
+                       enabled: false
+                       , width: 50 //fontSize
+                       , color: '#000000'
+               });
+
+               const self = this;
+               function _mouseDown(o) {
+                       const canvas = this
+                               , pointer = canvas.getPointer(o.e)
+                               , ao = self.__getObj.call(self, canvas, 
o.target);
+                       if (!!ao) {
+                               self.obj = ao;
+                       } else {
+                               self.obj = self.createTextObj.call(self, 
canvas, pointer);
+                               if (self.style.bold) {
+                                       self.obj.fontWeight = 'bold'
+                               }
+                               if (self.style.italic) {
+                                       self.obj.fontStyle = 'italic'
+                               }
+                               canvas.add(self.obj).setActiveObject(self.obj);
+                       }
+                       self._onMouseDown.call(self);
+               };
+               this.activate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.on('mouse:down', _mouseDown);
+                               canvas.on('mouse:dblclick', self._doubleClick);
+                               canvas.selection = true;
+                               canvas.forEachObject(function(o) {
+                                       if (self._editable(o)) {
+                                               o.selectable = true;
+                                               o.editable = true;
+                                       }
+                               });
+                       });
+                       this.fontFamily = 
$('#wb-text-style-block').css('font-family');
+                       ToolUtil.enableAllProps(settings, this);
+                       const b = settings.find('.wb-prop-b').button("enable");
+                       if (this.style.bold) {
+                               b.addClass('ui-state-active selected');
+                       } else {
+                               b.removeClass('ui-state-active selected');
+                       }
+                       const i = settings.find('.wb-prop-i').button("enable");
+                       if (this.style.italic) {
+                               i.addClass('ui-state-active selected');
+                       } else {
+                               i.removeClass('ui-state-active selected');
+                       }
+                       this._onActivate();
+                       VideoUtil.highlight(sBtn.removeClass('disabled'), 
'bg-warning', 5);
+               };
+               this.deactivate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.off('mouse:down', _mouseDown);
+                               canvas.off('mouse:dblclick', self._doubleClick);
+                               canvas.selection = false;
+                               canvas.forEachObject(function(o) {
+                                       if (self.omType === o.omType) {
+                                               o.selectable = false;
+                                               o.editable = false;
+                                       }
+                               });
+                       });
+                       this._onDeactivate();
+               };
+       }
+
+       _doubleClick() {}
+
+       __valid(o) {
+               return !!o && this.omType === o.omType;
+       }
+
+       __getObj(canvas, o) {
+               if (this.__valid(o)) {
+                       return o;
+               } else {
+                       const _o = canvas.getActiveObject();
+                       return this.__valid(_o) ? _o : null;
+               }
+       }
+
+       createTextObj(_, pointer) {
+               return new fabric.IText('', {
+                       left: pointer.x
+                       , top: pointer.y
+                       , padding: 7
+                       , omType: this.omType
+                       , fill: this.fill.enabled ? this.fill.color : 
ToolUtil.noColor
+                       , stroke: this.stroke.enabled ? this.stroke.color : 
ToolUtil.noColor
+                       , fontSize: this.stroke.width
+                       , fontFamily: this.fontFamily
+                       , opacity: this.opacity
+               });
+       }
+
+       _onMouseDown() {
+               this.obj.enterEditing();
+       }
+
+       _onActivate() {
+               WbArea.removeDeleteHandler();
+       }
+
+       _onDeactivate() {
+               WbArea.addDeleteHandler();
+       }
+
+       _editable(o) {
+               return this.omType === o.omType;
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-textbox.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-textbox.js
new file mode 100644
index 0000000..38cf7c0
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-textbox.js
@@ -0,0 +1,51 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const Text = require('./wb-tool-text');
+const ToolUtil = require('./wb-tool-util');
+require('fabric');
+
+module.exports = class Textbox extends Text {
+       constructor(wb, s, sBtn) {
+               super(wb, s, sBtn);
+               this.omType = 'textbox';
+       }
+
+       createTextObj(canvas, pointer) {
+               return new fabric.Textbox('', {
+                       left: pointer.x
+                       , top: pointer.y
+                       , padding: 7
+                       , fill: this.fill.enabled ? this.fill.color : 
ToolUtil.noColor
+                       , stroke: this.stroke.enabled ? this.stroke.color : 
ToolUtil.noColor
+                       //, strokeWidth: this.stroke.width
+                       , fontSize: this.stroke.width
+                       , omType: this.omType
+                       , fontFamily: this.fontFamily
+                       , opacity: this.opacity
+                       , breakWords: true
+                       , width: canvas.width / 4
+                       , lockScalingX: false
+                       , lockScalingY: true
+               });
+       }
+
+       _doubleClick(e) {
+               const ao = e.target;
+               if (!!ao && this.omType === ao.omType) {
+                       this.obj = ao;
+                       this.obj.enterEditing();
+                       WbArea.removeDeleteHandler();
+               }
+       }
+
+       _onMouseDown() {
+               WbArea.addDeleteHandler();
+       }
+
+       _onActivate() {
+               WbArea.addDeleteHandler();
+       }
+
+       _onDeactivate() {
+               WbArea.addDeleteHandler();
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-uline.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-uline.js
new file mode 100644
index 0000000..b77eda6
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-uline.js
@@ -0,0 +1,10 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const Line = require('./wb-tool-line');
+
+module.exports = class ULine extends Line {
+       constructor(wb, settings, sBtn) {
+               super(wb, settings, sBtn);
+               this.stroke.width = 20;
+               this.opacity = .5;
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-util.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-util.js
index 59099b3..b98ef23 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tool-util.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-util.js
@@ -59,4 +59,5 @@ module.exports = {
                        return result;
                }, {});
        }
+       , noColor: 'rgba(0,0,0,0)'
 };
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tool-whiteout.js 
b/openmeetings-web/src/main/front/wb/src/wb-tool-whiteout.js
new file mode 100644
index 0000000..e957b16
--- /dev/null
+++ b/openmeetings-web/src/main/front/wb/src/wb-tool-whiteout.js
@@ -0,0 +1,31 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const WbShapeBase = require('./wb-tool-shape-base');
+const ToolUtil = require('./wb-tool-util');
+
+module.exports = class Whiteout extends WbShapeBase {
+       constructor(wb, s, sBtn) {
+               super();
+               this.fill.color = '#FFFFFF';
+               Object.assign(this.stroke, {
+                       color: '#FFFFFF'
+                       , width: 25
+               });
+
+               const self = this;
+               this.activate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.isDrawingMode = true;
+                               canvas.freeDrawingBrush.width = 
self.stroke.width;
+                               canvas.freeDrawingBrush.color = 
self.stroke.color;
+                               canvas.freeDrawingBrush.opacity = self.opacity;
+                       });
+                       ToolUtil.disableAllProps(s);
+                       sBtn.addClass('disabled');
+               };
+               this.deactivate = () => {
+                       wb.eachCanvas(function(canvas) {
+                               canvas.isDrawingMode = false;
+                       });
+               };
+       }
+};
diff --git a/openmeetings-web/src/main/front/wb/src/wb-tools.js 
b/openmeetings-web/src/main/front/wb/src/wb-tools.js
index e9438a7..3aebd1a 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-tools.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-tools.js
@@ -2,6 +2,16 @@
 const Role = require('./wb-role');
 const WbUtils = require('./wb-utils');
 const APointer = require('./wb-tool-apointer');
+const Pointer = require('./wb-tool-pointer');
+const Text = require('./wb-tool-text');
+const Textbox = require('./wb-tool-textbox');
+const Whiteout = require('./wb-tool-whiteout');
+const Paint = require('./wb-tool-paint');
+const Line = require('./wb-tool-line');
+const ULine = require('./wb-tool-uline');
+const Rect = require('./wb-tool-rect');
+const Ellipse = require('./wb-tool-ellipse');
+const Arrow = require('./wb-tool-arrow');
 
 const ACTIVE = 'active';
 
@@ -92,19 +102,19 @@ module.exports = class WbTools {
                }
                function _initTexts(sBtn) {
                        const c = _initGroup('#wb-area-texts', 
_getBtn('apointer'));
-                       _initToolBtn('text', false, Text(wb, settings, sBtn));
-                       _initToolBtn('textbox', false, Textbox(wb, settings, 
sBtn));
+                       _initToolBtn('text', false, new Text(wb, settings, 
sBtn));
+                       _initToolBtn('textbox', false, new Textbox(wb, 
settings, sBtn));
                        _initGroupHandle(c, tools);
                }
                function _initDrawings(sBtn) {
                        const c = _initGroup('#wb-area-drawings', 
tools.find('.texts'));
-                       _initToolBtn('eraser', false, Whiteout(wb, settings, 
sBtn));
-                       _initToolBtn('paint', false, Paint(wb, settings, sBtn));
-                       _initToolBtn('line', false, Line(wb, settings, sBtn));
-                       _initToolBtn('uline', false, ULine(wb, settings, sBtn));
-                       _initToolBtn('rect', false, Rect(wb, settings, sBtn));
-                       _initToolBtn('ellipse', false, Ellipse(wb, settings, 
sBtn));
-                       _initToolBtn('arrow', false, Arrow(wb, settings, sBtn));
+                       _initToolBtn('eraser', false, new Whiteout(wb, 
settings, sBtn));
+                       _initToolBtn('paint', false, new Paint(wb, settings, 
sBtn));
+                       _initToolBtn('line', false, new Line(wb, settings, 
sBtn));
+                       _initToolBtn('uline', false, new ULine(wb, settings, 
sBtn));
+                       _initToolBtn('rect', false, new Rect(wb, settings, 
sBtn));
+                       _initToolBtn('ellipse', false, new Ellipse(wb, 
settings, sBtn));
+                       _initToolBtn('arrow', false, new Arrow(wb, settings, 
sBtn));
                        _initGroupHandle(c, tools);
                }
                function _initCliparts(sBtn) {
@@ -123,6 +133,9 @@ module.exports = class WbTools {
                function _initSettings() {
                        function setStyle(canvas, styleName, value) {
                                const o = canvas.getActiveObject();
+                               if (!o) {
+                                       return;
+                               }
                                if (o.setSelectionStyles && o.isEditing) {
                                        const style = {};
                                        style[styleName] = value;
@@ -243,7 +256,7 @@ module.exports = class WbTools {
                                StaticTMath.create(json, cnvs
                                        , function(obj) {
                                                _removeHandler(o);
-                                               cnvs.trigger('object:modified', 
{target: obj});
+                                               cnvs.fire('object:modified', 
{target: obj});
                                        }
                                        , function(msg) {
                                                const err = 
math.find('.status');
@@ -314,12 +327,12 @@ module.exports = class WbTools {
                                        if (role === Role.WHITEBOARD) {
                                                clearAll.addClass('disabled');
                                        }
-                                       _initToolBtn('pointer', _firstToolItem, 
Pointer(wb, settings, sBtn));
+                                       _initToolBtn('pointer', _firstToolItem, 
new Pointer(wb, settings, sBtn));
                                        _firstToolItem = false;
                                        _initTexts(sBtn);
                                        _initDrawings(sBtn);
-                                       _initToolBtn('math', _firstToolItem, 
TMath(wb, settings, sBtn));
-                                       _initCliparts(sBtn);
+                                       // FIXME TODO _initToolBtn('math', 
_firstToolItem, TMath(wb, settings, sBtn));
+                                       // FIXME TODO _initCliparts(sBtn);
                                        
tools.find('.om-icon.settings').click(function() {
                                                settings.show();
                                        });
diff --git a/openmeetings-web/src/main/front/wb/src/wb-zoom.js 
b/openmeetings-web/src/main/front/wb/src/wb-zoom.js
index 86188f0..840bb31 100644
--- a/openmeetings-web/src/main/front/wb/src/wb-zoom.js
+++ b/openmeetings-web/src/main/front/wb/src/wb-zoom.js
@@ -1,23 +1,23 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
 const Role = require('./wb-role');
 
-const area = $('.room-block .wb-block .wb-area .tabs')
-       , bar = area.find('.wb-tabbar')
-
 module.exports = class WbZoom {
        constructor(wbEl, wbObj) {
                this.zoom = 1.;
                this.zoomMode = 'PAGE_WIDTH';
 
+               const self = this
+                       , area = $('.room-block .wb-block .wb-area .tabs')
+                       , bar = area.find('.wb-tabbar');
                let zoomBar;
                function _sendSetSize() {
                        wbObj.doSetSize();
                        OmUtil.wbAction({action: 'setSize', data: {
                                wbId: wbObj.getId()
-                               , zoom: this.zoom
-                               , zoomMode: this.zoomMode
-                               , width: wbObj.getWidth()
-                               , height: wbObj.getHeight()
+                               , zoom: self.zoom
+                               , zoomMode: self.zoomMode
+                               , width: wbObj.width
+                               , height: wbObj.height
                        }});
                }
 
@@ -32,7 +32,7 @@ module.exports = class WbZoom {
                                zoomBar.find('.doc-group 
.curr-slide').removeClass("text-muted");
                                zoomBar.find('.doc-group 
.curr-slide').addClass("text-dark");
                                zoomBar.find('.doc-group 
.input-group-text').removeClass("text-muted");
-                               const ns = 1 * slide;
+                               const ns = 1 * wbObj.slide;
                                zoomBar.find('.doc-group .curr-slide').val(ns + 
1).attr('max', ccount);
                                zoomBar.find('.doc-group .up').prop('disabled', 
ns < 1);
                                zoomBar.find('.doc-group 
.down').prop('disabled', ns > ccount - 2);
@@ -55,21 +55,21 @@ module.exports = class WbZoom {
                        switch (role) {
                                case Role.PRESENTER:
                                        
zoomBar.find('.curr-slide').change(function() {
-                                               _setSlide($(this).val() - 1);
-                                               showCurrentSlide();
+                                               wbObj._doSetSlide($(this).val() 
- 1);
+                                               wbObj._showCurrentSlide();
                                        });
                                        zoomBar.find('.doc-group 
.up').click(function () {
-                                               _setSlide(1 * slide - 1);
-                                               showCurrentSlide();
+                                               wbObj._doSetSlide(1 * 
wbObj.slide - 1);
+                                               wbObj._showCurrentSlide();
                                        });
                                        zoomBar.find('.doc-group 
.down').click(function () {
-                                               _setSlide(1 * slide + 1);
-                                               showCurrentSlide();
+                                               wbObj._doSetSlide(1 * 
wbObj.slide + 1);
+                                               wbObj._showCurrentSlide();
                                        });
                                        
zoomBar.find('.settings-group').show().find('.settings').click(function () {
                                                const wbs = $('#wb-settings')
-                                                       , wbsw = 
wbs.find('.wbs-width').val(width)
-                                                       , wbsh = 
wbs.find('.wbs-height').val(height);
+                                                       , wbsw = 
wbs.find('.wbs-width').val(wbObj.width)
+                                                       , wbsh = 
wbs.find('.wbs-height').val(wbObj.height);
                                                function isNumeric(n) {
                                                        return 
!isNaN(parseInt(n)) && isFinite(n);
                                                }
@@ -77,8 +77,8 @@ module.exports = class WbZoom {
                                                
wbs.find('.btn-ok').off().click(function() {
                                                        const __w = wbsw.val(), 
__h = wbsh.val();
                                                        if (isNumeric(__w) && 
isNumeric(__h)) {
-                                                               width = 
parseInt(__w);
-                                                               height = 
parseInt(__h);
+                                                               wbObj.width = 
parseInt(__w);
+                                                               wbObj.height = 
parseInt(__h);
                                                                _sendSetSize();
                                                        }
                                                        wbs.modal('hide');
@@ -88,35 +88,35 @@ module.exports = class WbZoom {
                                        // fallthrough
                                case Role.NONE:
                                        
zoomBar.find('.zoom-out').click(function() {
-                                               this.zoom -= .2;
-                                               if (this.zoom < .1) {
-                                                       this.zoom = .1;
+                                               self.zoom -= .2;
+                                               if (self.zoom < .1) {
+                                                       self.zoom = .1;
                                                }
-                                               this.zoomMode = 'ZOOM';
+                                               self.zoomMode = 'ZOOM';
                                                _sendSetSize();
                                        });
                                        
zoomBar.find('.zoom-in').click(function() {
-                                               this.zoom += .2;
-                                               this.zoomMode = 'ZOOM';
+                                               self.zoom += .2;
+                                               self.zoomMode = 'ZOOM';
                                                _sendSetSize();
                                        });
                                        zoomBar.find('.zoom').change(function() 
{
                                                const zzz = $(this).val();
-                                               this.zoomMode = 'ZOOM';
+                                               self.zoomMode = 'ZOOM';
                                                if (isNaN(zzz)) {
                                                        switch (zzz) {
                                                                case 'FULL_FIT':
                                                                case 
'PAGE_WIDTH':
-                                                                       
this.zoomMode = zzz;
+                                                                       
self.zoomMode = zzz;
                                                                        break;
                                                                case 'custom':
-                                                                       
this.zoom = 1. * $(this).data('custom-val');
+                                                                       
self.zoom = 1. * $(this).data('custom-val');
                                                                        break;
                                                                default:
                                                                        //no-op
                                                        }
                                                } else {
-                                                       this.zoom = 1. * zzz;
+                                                       self.zoom = 1. * zzz;
                                                }
                                                _sendSetSize();
                                        });
@@ -127,11 +127,11 @@ module.exports = class WbZoom {
                this.setSize = () => {
                        switch (this.zoomMode) {
                                case 'FULL_FIT':
-                                       this.zoom = Math.min((area.width() - 
30) / wbObj.getWidth(), (area.height() - bar.height() - 30) / 
wbObj.getHeight());
+                                       this.zoom = Math.min((area.width() - 
30) / wbObj.width, (area.height() - bar.height() - 30) / wbObj.height);
                                        
zoomBar.find('.zoom').val(this.zoomMode);
                                        break;
                                case 'PAGE_WIDTH':
-                                       this.zoom = (area.width() - 30 - 40) / 
wbObj.getWidth(); // bumper + toolbar
+                                       this.zoom = (area.width() - 30 - 40) / 
wbObj.width; // bumper + toolbar
                                        
zoomBar.find('.zoom').val(this.zoomMode);
                                        break;
                                default:
diff --git a/openmeetings-web/src/main/front/wb/src/wb.js 
b/openmeetings-web/src/main/front/wb/src/wb.js
index 4f06ea4..c1be23f 100644
--- a/openmeetings-web/src/main/front/wb/src/wb.js
+++ b/openmeetings-web/src/main/front/wb/src/wb.js
@@ -14,22 +14,12 @@ module.exports = class Wb {
                this.title = wbo.name;
                this.width = wbo.width;
                this.height = wbo.height;
+               this.slide = 0;
+
                const canvases = [], self = this;
-               let wbEl, tools, zoomBar, slide = 0
+               let wbEl, tools, zoomBar
                        , role = null, scrollTimeout = null;
 
-               function _setSlide(_sld) {
-                       const sld = 1 * _sld;
-                       if (sld < 0 || sld > canvases.length - 1) {
-                               return;
-                       }
-                       slide = _sld;
-                       OmUtil.wbAction({action: 'setSlide', data: {
-                               wbId: self.id
-                               , slide: _sld
-                       }});
-                       zoomBar.update(role, canvases.length);
-               }
                function _findObject(o) {
                        let _o = null;
                        const cnvs = canvases[o.slide];
@@ -89,7 +79,7 @@ module.exports = class Wb {
                                                        b.data().deactivate();
                                                        b.data().activate();
                                                }
-                                               showCurrentSlide();
+                                               self._showCurrentSlide();
                                        }
                                }
                                        break;
@@ -230,37 +220,20 @@ module.exports = class Wb {
                                const sc = wbEl.find('.scroll-container')
                                        , canvases = 
sc.find('.canvas-container');
                                if (Math.round(sc.height() + sc[0].scrollTop) 
=== sc[0].scrollHeight) {
-                                       if (slide !== canvases.length - 1) {
-                                               _setSlide(canvases.length - 1);
+                                       if (self.slide !== canvases.length - 1) 
{
+                                               
self._doSetSlide(canvases.length - 1);
                                        }
                                        return false;
                                }
                                canvases.each(function(idx) {
                                        const h = $(this).height(), pos = 
$(this).position();
-                                       if (slide !== idx && pos.top > BUMPER - 
h && pos.top < BUMPER) {
-                                               _setSlide(idx);
+                                       if (self.slide !== idx && pos.top > 
BUMPER - h && pos.top < BUMPER) {
+                                               self._doSetSlide(idx);
                                                return false;
                                        }
                                });
                        }, 100);
                }
-               function showCurrentSlide() {
-                       wbEl.find('.scroll-container 
.canvas-container').each(function(idx) {
-                               if (role === Role.PRESENTER) {
-                                       $(this).show();
-                                       const cclist = 
wbEl.find('.scroll-container .canvas-container');
-                                       if (cclist.length > slide) {
-                                               cclist[slide].scrollIntoView();
-                                       }
-                               } else {
-                                       if (idx === slide) {
-                                               $(this).show();
-                                       } else {
-                                               $(this).hide();
-                                       }
-                               }
-                       });
-               }
                /*TODO interactive text change
                var textEditedHandler = function (e) {
                        var obj = e.target;
@@ -310,7 +283,7 @@ module.exports = class Wb {
                        canvases.push(canvas);
                        const cc = $('#' + cid).closest('.canvas-container');
                        if (role === Role.NONE) {
-                               if (sl === slide) {
+                               if (sl === self.slide) {
                                        cc.show();
                                } else {
                                        cc.hide();
@@ -329,7 +302,7 @@ module.exports = class Wb {
                        self.eachCanvas(function(canvas) {
                                __setSize(canvas);
                        });
-                       _setSlide(slide);
+                       self._doSetSlide(self.slide);
                }
                function _videoStatus(json) {
                        const g = _findObject(json);
@@ -347,7 +320,7 @@ module.exports = class Wb {
                                } else {
                                        sc.on('scroll', scrollHandler);
                                }
-                               showCurrentSlide();
+                               self._showCurrentSlide();
                                this.eachCanvas(function(canvas) {
                                        setHandlers(canvas);
                                        canvas.forEachObject(function(__o) {
@@ -374,9 +347,38 @@ module.exports = class Wb {
                                _setSize();
                        }
                };
+               this._showCurrentSlide = () => {
+                       wbEl.find('.scroll-container 
.canvas-container').each(function(idx) {
+                               if (role === Role.PRESENTER) {
+                                       $(this).show();
+                                       const cclist = 
wbEl.find('.scroll-container .canvas-container');
+                                       if (cclist.length > self.slide) {
+                                               
cclist[self.slide].scrollIntoView();
+                                       }
+                               } else {
+                                       if (idx === self.slide) {
+                                               $(this).show();
+                                       } else {
+                                               $(this).hide();
+                                       }
+                               }
+                       });
+               };
+               this._doSetSlide = (_sld) => {
+                       const sld = 1 * _sld;
+                       if (sld < 0 || sld > canvases.length - 1) {
+                               return;
+                       }
+                       self.slide = _sld;
+                       OmUtil.wbAction({action: 'setSlide', data: {
+                               wbId: self.id
+                               , slide: _sld
+                       }});
+                       zoomBar.update(role, canvases.length);
+               };
                this.setSlide = (_sl) => {
-                       slide = _sl;
-                       showCurrentSlide();
+                       self.slide = _sl;
+                       self._showCurrentSlide();
                };
                this.createObj = (obj) => {
                        const arr = [], del = [], _arr = Array.isArray(obj) ? 
obj : [obj];
@@ -477,7 +479,7 @@ module.exports = class Wb {
                        }
                };
                this.getCanvas = () => {
-                       return canvases[slide];
+                       return canvases[self.slide];
                };
                this.eachCanvas = (func) => {
                        for (let i = 0; i < canvases.length; ++i) {
@@ -509,12 +511,4 @@ module.exports = class Wb {
        getId() {
                return this.id;
        }
-
-       getWidth() {
-               return this.width;
-       }
-
-       getHeight() {
-               return this.height;
-       }
 };
diff --git a/openmeetings-web/src/main/webapp/css/raw-tree.css 
b/openmeetings-web/src/main/webapp/css/raw-tree.css
index b09e216..dd90f4d 100644
--- a/openmeetings-web/src/main/webapp/css/raw-tree.css
+++ b/openmeetings-web/src/main/webapp/css/raw-tree.css
@@ -182,7 +182,7 @@
 .file.item .name {
        padding-top: 10px;
 }
-.file.item .errors {
+.tree-node .file.item .errors {
        position: absolute;
        top: 0px;
        right: 1px;

Reply via email to