Commit 9d73ff41 authored by Dele Olajide's avatar Dele Olajide

ofmeet plugin version 0.1.1

Added co-browsing to collaboration api
Moved collaboration api to chrome extension for co-browsing
Updated chrome extension to 0.0.2
parent 80a744ea
......@@ -49,6 +49,14 @@
Openfire Meetings Plugin Changelog
</h1>
<p><b>0.1.1</b> -- Feb 8th, 2015</p>
<ul>
<li>Added co-browsing to collaboration api</li>
<li>Moved collaboration api to chrome extension for co-browsing</li>
<li>Updated chrome extension to 0.0.2</li>
</ul>
<p><b>0.1.0</b> -- Feb 4th, 2015</p>
<ul>
......
......@@ -5,8 +5,8 @@
<name>Openfire Meetings</name>
<description>Provides high quality, scalable video conferences using Jitsi Meet and Jitsi Videobridge</description>
<author>Ignite Realtime</author>
<version>0.1.0</version>
<date>02/04/2015</date>
<version>0.1.1</version>
<date>02/08/2015</date>
<minServerVersion>3.9.9</minServerVersion>
<adminconsole>
......
var hash = null;
var url = urlParam("url");
var frameWindow = null;
function start()
{
frameWindow = document.getElementById('iframe1');
if (window.location.hash) hash = window.location.hash.substring(1);
console.log("app.js start", hash, url);
frameWindow.onload = function()
{
console.log("app.js onload", frameWindow);
}
if (url) frameWindow.src = url;
window.parent.connection.ofmuc.appReady();
}
function stop()
{
console.log("app.js stop");
}
function urlParam(name)
{
var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (!results) { return undefined; }
return decodeURIComponent(results[1]) || undefined;
}
window.onhashchange = function()
{
hash = window.location.hash.substring(1);
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
var FOREGROUND_COLORS = ["#111", "#eee"];
var CURSOR_HEIGHT = 50;
var CURSOR_ANGLE = (35 / 180) * Math.PI;
var CURSOR_WIDTH = Math.ceil(Math.sin(CURSOR_ANGLE) * CURSOR_HEIGHT);
var CLICK_TRANSITION_TIME = 3000;
var peers = {};
var session = {
send: function(msg)
{
window.parent.console.log('remote session.send', msg);
window.parent.connection.ofmuc.appMessage(msg);
}
};
var handleAppMessage = function(json, from)
{
try {
var obj = JSON.parse(json);
//window.parent.console.log("remote handleAppMessage", obj, json, from);
var p = peers[from];
if (! p) {
p = Cursor.getClient(from);
p.updatePeer({id: from, name: from, status: "live"});
peers[from] = p;
}
if (obj.type == "cursor-update") p.updatePosition(obj);
if (obj.type == "cursor-click") handleCursorClick(obj, p);
} catch (e) { window.parent.console.error(e)}
};
var handleCursorClick = function (pos, peer)
{
// When the click is calculated isn't always the same as how the
// last cursor update was calculated, so we force the cursor to
// the last location during a click:
peer.updatePosition(pos);
var topPos = pos.top + pos.offsetY;
var leftPos = pos.left + pos.offsetX;
if (pos.element) {
var target = $(elementFinder.findElement(pos.element));
var offset = target.offset();
topPos = offset.top + pos.offsetY;
leftPos = offset.left + pos.offsetX;
}
displayClick({top: topPos, left: leftPos}, 'red');
};
var elementFinder = {};
elementFinder.ignoreElement = function ignoreElement(el)
{
if (el instanceof $) {
el = el[0];
}
while (el) {
if ($(el).hasClass("togetherjs")) {
return true;
}
el = el.parentNode;
}
return false;
};
elementFinder.elementLocation = function elementLocation(el) {
if (el instanceof $) {
// a jQuery element
el = el[0];
}
if (el[0] && el.attr && el[0].nodeType == 1) {
// Or a jQuery element not made by us
el = el[0];
}
if (el.id) {
return "#" + el.id;
}
if (el.tagName == "BODY") {
return "body";
}
if (el.tagName == "HEAD") {
return "head";
}
if (el === document) {
return "document";
}
var parent = el.parentNode;
if ((! parent) || parent == el) {
console.warn("elementLocation(", el, ") has null parent");
throw new Error("No locatable parent found");
}
var parentLocation = elementLocation(parent);
var children = parent.childNodes;
var _len = children.length;
var index = 0;
for (var i=0; i<_len; i++) {
if (children[i] == el) {
break;
}
if (children[i].nodeType == document.ELEMENT_NODE) {
if (children[i].className.indexOf("togetherjs") != -1) {
// Don't count our UI
continue;
}
// Don't count text or comments
index++;
}
}
return parentLocation + ":nth-child(" + (index+1) + ")";
};
elementFinder.CannotFind = {
constructor: function CannotFind(location, reason, context) {
this.prefix = "";
this.location = location;
this.reason = reason;
this.context = context;
},
toString: function () {
var loc;
try {
loc = elementFinder.elementLocation(this.context);
} catch (e) {
loc = this.context;
}
return (
"[CannotFind " + this.prefix +
"(" + this.location + "): " +
this.reason + " in " +
loc + "]");
}
};
elementFinder.findElement = function findElement(loc, container) {
// FIXME: should this all just be done with document.querySelector()?
// But no! We can't ignore togetherjs elements with querySelector.
// But maybe! We *could* make togetherjs elements less obtrusive?
container = container || document;
var el, rest;
if (loc === "body") {
return document.body;
} else if (loc === "head") {
return document.head;
} else if (loc === "document") {
return document;
} else if (loc.indexOf("body") === 0) {
el = document.body;
try {
return findElement(loc.substr(("body").length), el);
} catch (e) {
if (e instanceof elementFinder.CannotFind) {
e.prefix = "body" + e.prefix;
}
throw e;
}
} else if (loc.indexOf("head") === 0) {
el = document.head;
try {
return findElement(loc.substr(("head").length), el);
} catch (e) {
if (e instanceof elementFinder.CannotFind) {
e.prefix = "head" + e.prefix;
}
throw e;
}
} else if (loc.indexOf("#") === 0) {
var id;
loc = loc.substr(1);
if (loc.indexOf(":") === -1) {
id = loc;
rest = "";
} else {
id = loc.substr(0, loc.indexOf(":"));
rest = loc.substr(loc.indexOf(":"));
}
el = document.getElementById(id);
if (! el) {
throw elementFinder.CannotFind("#" + id, "No element by that id", container);
}
if (rest) {
try {
return findElement(rest, el);
} catch (e) {
if (e instanceof elementFinder.CannotFind) {
e.prefix = "#" + id + e.prefix;
}
throw e;
}
} else {
return el;
}
} else if (loc.indexOf(":nth-child(") === 0) {
loc = loc.substr((":nth-child(").length);
if (loc.indexOf(")") == -1) {
throw "Invalid location, missing ): " + loc;
}
var num = loc.substr(0, loc.indexOf(")"));
num = parseInt(num, 10);
var count = num;
loc = loc.substr(loc.indexOf(")") + 1);
var children = container.childNodes;
el = null;
for (var i=0; i<children.length; i++) {
var child = children[i];
if (child.nodeType == document.ELEMENT_NODE) {
if (child.className.indexOf("togetherjs") != -1) {
continue;
}
count--;
if (count === 0) {
// this is the element
el = child;
break;
}
}
}
if (! el) {
throw elementFinder.CannotFind(":nth-child(" + num + ")", "container only has " + (num - count) + " elements", container);
}
if (loc) {
try {
return elementFinder.findElement(loc, el);
} catch (e) {
if (e instanceof elementFinder.CannotFind) {
e.prefix = ":nth-child(" + num + ")" + e.prefix;
}
throw e;
}
} else {
return el;
}
} else {
throw elementFinder.CannotFind(loc, "Malformed location", container);
}
};
elementFinder.elementByPixel = function (height) {
/* Returns {location: "...", offset: pixels}
To get the pixel position back, you'd do:
$(location).offset().top + offset
*/
function search(start, height) {
var last = null;
var children = start.children();
children.each(function () {
var el = $(this);
if (el.hasClass("togetherjs") || el.css("position") == "fixed" || ! el.is(":visible")) {
return;
}
if (el.offset().top > height) {
return false;
}
last = el;
});
if ((! children.length) || (! last)) {
// There are no children, or only inapplicable children
return {
location: elementFinder.elementLocation(start[0]),
offset: height - start.offset().top,
absoluteTop: height,
documentHeight: $(document).height()
};
}
return search(last, height);
}
return search($(document.body), height);
};
elementFinder.pixelForPosition = function (position) {
/* Inverse of elementFinder.elementByPixel */
if (position.location == "body") {
return position.offset;
}
var el;
try {
el = elementFinder.findElement(position.location);
} catch (e) {
if (e instanceof elementFinder.CannotFind && position.absoluteTop) {
// We don't trust absoluteTop to be quite right locally, so we adjust
// for the total document height differences:
var percent = position.absoluteTop / position.documentHeight;
return $(document).height() * percent;
}
throw e;
}
var top = $(el).offset().top;
// FIXME: maybe here we should test for sanity, like if an element is
// hidden. We can use position.absoluteTop to get a sense of where the
// element roughly should be. If the sanity check failed we'd use
// absoluteTop
return top + position.offset;
};
// Number of milliseconds after page load in which a scroll-update
// related hello-back message will be processed:
var SCROLL_UPDATE_CUTOFF = 2000;
// FIXME: should check for a peer leaving and remove the cursor object
var Cursor = util.Class({
constructor: function (clientId) {
this.clientId = clientId;
this.element = $('<div id="togetherjs-template-cursor" class="togetherjs-cursor togetherjs"> <!-- Note: images/cursor.svg is a copy of this (for editing): --> <!-- crossbrowser svg dropshadow http://demosthenes.info/blog/600/Creating-a-True-CrossBrowser-Drop-Shadow- --> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="15px" height="22.838px" viewBox="96.344 146.692 15 22.838" enable-background="new 96.344 146.692 15 22.838" xml:space="preserve"> <path fill="#231F20" d="M98.984,146.692c2.167,1.322,1.624,6.067,3.773,7.298c-0.072-0.488,2.512-0.931,3.097,0 c0.503,0.337,1.104-0.846,2.653,0.443c0.555,0.593,3.258,2.179,1.001,8.851c-0.446,1.316,2.854,0.135,1.169,2.619 c-3.748,5.521-9.455,2.787-9.062,1.746c1.06-2.809-6.889-4.885-4.97-9.896c0.834-2.559,2.898,0.653,2.923,0.29 c-0.434-1.07-2.608-5.541-2.923-6.985C96.587,150.793,95.342,147.033,98.984,146.692z"/> </svg> <!-- <img class="togetherjs-cursor-img" src="http://localhost:8080/togetherjs/images/cursor.svg"> --> <span class="togetherjs-cursor-container"> <span class="togetherjs-cursor-name">Dummy</span> <span style="display:none" class="togetherjs-cursor-typing" id="togetherjs-cursor-typebox"> <span class="togetherjs-typing-ellipse-one">&#9679;</span><span class="togetherjs-typing-ellipse-two">&#9679;</span><span class="togetherjs-typing-ellipse-three">&#9679;</span> </span> <!-- Displayed when the cursor is below the screen: --> <span class="togetherjs-cursor-down"> </span> <!-- Displayed when the cursor is above the screen: --> <span class="togetherjs-cursor-up"> </span> </span> </div>');
this.elementClass = "togetherjs-scrolled-normal";
this.element.addClass(this.elementClass);
//this.updatePeer(peers.getPeer(clientId));
this.lastTop = this.lastLeft = null;
$(document.body).append(this.element);
//this.element.animateCursorEntry();
this.keydownTimeout = null;
this.clearKeydown = this.clearKeydown.bind(this);
this.atOtherUrl = false;
this.color = Math.floor(Math.random() * 0xffffff).toString(16);
while (this.color.length < 6) {
this.color = "0" + this.color;
}
this.color = "#" + this.color;
},
// How long after receiving a setKeydown call that we should show the
// user typing. This should be more than MIN_KEYDOWN_TIME:
KEYDOWN_WAIT_TIME: 2000,
updatePeer: function (peer) {
// FIXME: can I use peer.setElement()?
this.element.css({color: this.color});
var img = this.element.find("img.togetherjs-cursor-img");
img.attr("src", makeCursor(this.color));
var name = this.element.find(".togetherjs-cursor-name");
var nameContainer = this.element.find(".togetherjs-cursor-container");
name.text(peer.name);
nameContainer.css({
backgroundColor: this.color,
color: tinycolor.mostReadable(this.color, FOREGROUND_COLORS)
});
var path = this.element.find("svg path");
path.attr("fill", this.color);
// FIXME: should I just remove the element?
if (peer.status != "live") {
this.element.hide();
this.element.find("svg").animate({
opacity: 0
}, 350);
this.element.find(".togetherjs-cursor-container").animate({
width: 34,
height: 20,
padding: 12,
margin: 0
}, 200).animate({
width: 0,
height: 0,
padding: 0,
opacity: 0
}, 200);
} else {
this.element.show();
this.element.animate({
opacity:0.3
}).animate({
opacity:1
});
}
},
setClass: function (name) {
if (name != this.elementClass) {
this.element.removeClass(this.elementClass).addClass(name);
this.elementClass = name;
}
},
updatePosition: function (pos) {
var top, left;
if (this.atOtherUrl) {
this.element.show();
this.atOtherUrl = false;
}
if (pos.element) {
var target = $(elementFinder.findElement(pos.element));
var offset = target.offset();
top = offset.top + pos.offsetY;
left = offset.left + pos.offsetX;
} else {
// No anchor, just an absolute position
top = pos.top;
left = pos.left;
}
// These are saved for use by .refresh():
this.lastTop = top;
this.lastLeft = left;
this.setPosition(top, left);
},
hideOtherUrl: function () {
if (this.atOtherUrl) {
return;
}
this.atOtherUrl = true;
// FIXME: should show away status better:
this.element.hide();
},
// place Cursor rotate function down here FIXME: this doesnt do anything anymore. This is in the CSS as an animation
rotateCursorDown: function(){
var e = $(this.element).find('svg');
e.animate({borderSpacing: -150, opacity: 1}, {
step: function(now, fx) {
if (fx.prop == "borderSpacing") {
e.css('-webkit-transform', 'rotate('+now+'deg)')
.css('-moz-transform', 'rotate('+now+'deg)')
.css('-ms-transform', 'rotate('+now+'deg)')
.css('-o-transform', 'rotate('+now+'deg)')
.css('transform', 'rotate('+now+'deg)');
} else {
e.css(fx.prop, now);
}
},
duration: 500
}, 'linear').promise().then(function () {
e.css('-webkit-transform', '')
.css('-moz-transform', '')
.css('-ms-transform', '')
.css('-o-transform', '')
.css('transform', '')
.css("opacity", "");
});
},
setPosition: function (top, left) {
var wTop = $(window).scrollTop();
var height = $(window).height();
if (top < wTop) {
// FIXME: this is a totally arbitrary number, but is meant to be big enough
// to keep the cursor name from being off the top of the screen.
top = 25;
this.setClass("togetherjs-scrolled-above");
} else if (top > wTop + height - CURSOR_HEIGHT) {
top = height - CURSOR_HEIGHT - 5;
this.setClass("togetherjs-scrolled-below");
} else {
this.setClass("togetherjs-scrolled-normal");
}
this.element.css({
top: top,
left: left
});
},
refresh: function () {
if (this.lastTop !== null) {
this.setPosition(this.lastTop, this.lastLeft);
}
},
setKeydown: function () {
if (this.keydownTimeout) {
clearTimeout(this.keydownTimeout);
} else {
this.element.find(".togetherjs-cursor-typing").show().animateKeyboard();
}
this.keydownTimeout = setTimeout(this.clearKeydown, this.KEYDOWN_WAIT_TIME);
},
clearKeydown: function () {
this.keydownTimeout = null;
this.element.find(".togetherjs-cursor-typing").hide().stopKeyboardAnimation();
},
_destroy: function () {
this.element.remove();
this.element = null;
}
});
Cursor._cursors = {};
Cursor.getClient = function (clientId) {
var c = Cursor._cursors[clientId];
if (! c) {
c = Cursor._cursors[clientId] = Cursor(clientId);
}
return c;
};
Cursor.forEach = function (callback, context) {
context = context || null;
for (var a in Cursor._cursors) {
if (Cursor._cursors.hasOwnProperty(a)) {
callback.call(context, Cursor._cursors[a], a);
}
}
};
Cursor.destroy = function (clientId) {
Cursor._cursors[clientId]._destroy();
delete Cursor._cursors[clientId];
};
/*
peers.on("new-peer identity-updated status-updated", function (peer) {
var c = Cursor.getClient(peer.id);
c.updatePeer(peer);
});
*/
var lastTime = 0;
var MIN_TIME = 100;
var lastPosX = -1;
var lastPosY = -1;
var lastMessage = null;
function mousemove(event)
{
var now = Date.now();
if (now - lastTime < MIN_TIME) {
return;
}
lastTime = now;
var pageX = event.pageX;
var pageY = event.pageY;
if (Math.abs(lastPosX - pageX) < 3 && Math.abs(lastPosY - pageY) < 3) {
// Not a substantial enough change
return;
}
lastPosX = pageX;
lastPosY = pageY;
var target = event.target;
if (elementFinder.ignoreElement(target)) {
target = null;
}
if ((! target) || target == document.documentElement || target == document.body) {
lastMessage = {
type: "cursor-update",
top: pageY,
left: pageX
};
session.send(lastMessage);
return;
}
target = $(target);
var offset = target.offset();
if (! offset) {
// FIXME: this really is walkabout.js's problem to fire events on the
// document instead of a specific element
console.warn("Could not get offset of element:", target[0]);
return;
}
var offsetX = pageX - offset.left;
var offsetY = pageY - offset.top;
lastMessage = {
type: "cursor-update",
element: elementFinder.elementLocation(target),
offsetX: Math.floor(offsetX),
offsetY: Math.floor(offsetY)
};
session.send(lastMessage);
}
function makeCursor(color)
{
var canvas = $("<canvas></canvas>");
canvas.attr("height", CURSOR_HEIGHT);
canvas.attr("width", CURSOR_WIDTH);
var context = canvas[0].getContext('2d');
context.fillStyle = color;
context.moveTo(0, 0);
context.beginPath();
context.lineTo(0, CURSOR_HEIGHT/1.2);
context.lineTo(Math.sin(CURSOR_ANGLE/2) * CURSOR_HEIGHT / 1.5,
Math.cos(CURSOR_ANGLE/2) * CURSOR_HEIGHT / 1.5);
context.lineTo(Math.sin(CURSOR_ANGLE) * CURSOR_HEIGHT / 1.2,
Math.cos(CURSOR_ANGLE) * CURSOR_HEIGHT / 1.2);
context.lineTo(0, 0);
context.shadowColor = 'rgba(0,0,0,0.3)';
context.shadowBlur = 2;
context.shadowOffsetX = 1;
context.shadowOffsetY = 2;
context.strokeStyle = "#ffffff";
context.stroke();
context.fill();
return canvas[0].toDataURL("image/png");
}
var scrollTimeout = null;
var scrollTimeoutSet = 0;
var SCROLL_DELAY_TIMEOUT = 75;
var SCROLL_DELAY_LIMIT = 300;
function scroll() {
var now = Date.now();
if (scrollTimeout) {
if (now - scrollTimeoutSet < SCROLL_DELAY_LIMIT) {
clearTimeout(scrollTimeout);
} else {
// Just let it progress anyway
return;
}
}
scrollTimeout = setTimeout(_scrollRefresh, SCROLL_DELAY_TIMEOUT);
if (! scrollTimeoutSet) {
scrollTimeoutSet = now;
}
}
var lastScrollMessage = null;
function _scrollRefresh() {
scrollTimeout = null;
scrollTimeoutSet = 0;
Cursor.forEach(function (c) {
c.refresh();
});
lastScrollMessage = {
type: "scroll-update",
position: elementFinder.elementByPixel($(window).scrollTop())
};
session.send(lastScrollMessage);
}
function documentClick(event) {
if (event.togetherjsInternal) {
// This is an artificial internal event
return;
}
// FIXME: this might just be my imagination, but somehow I just
// really don't want to do anything at this stage of the event
// handling (since I'm catching every click), and I'll just do
// something real soon:
setTimeout(function ()
{
var element = event.target;
if (element == document.documentElement) {
// For some reason clicking on <body> gives the <html> element here
element = document.body;
}
if (elementFinder.ignoreElement(element)) {
return;
}
//Prevent click events on video objects to avoid conflicts with
//togetherjs's own video events
if (element.nodeName.toLowerCase() === 'video'){
return;
}
var location = elementFinder.elementLocation(element);
var offset = $(element).offset();
var offsetX = event.pageX - offset.left;
var offsetY = event.pageY - offset.top;
session.send({
type: "cursor-click",
element: location,
offsetX: offsetX,
offsetY: offsetY,
top: offset.top,
left: offset.left
});
displayClick({top: event.pageY, left: event.pageX}, 'red');
});
}
function displayClick(pos, color) {
console.log("displayClick", pos, color);
// FIXME: should we hide the local click if no one else is going to see it?
// That means tracking who might be able to see our screen.
var element = $('<div class="togetherjs-click togetherjs"></div>');
$(document.body).append(element);
element.css({
top: pos.top,
left: pos.left,
borderColor: color
});
setTimeout(function () {
element.addClass("togetherjs-clicking");
}, 100);
setTimeout(function () {
element.remove();
}, CLICK_TRANSITION_TIME);
}
var lastKeydown = 0;
var MIN_KEYDOWN_TIME = 500;
function documentKeydown(event) {
setTimeout(function () {
var now = Date.now();
if (now - lastKeydown < MIN_KEYDOWN_TIME) {
return;
}
lastKeydown = now;
// FIXME: is event.target interesting here? That is, *what* the
// user is typing into, not just that the user is typing? Also
// I'm assuming we don't care if the user it typing into a
// togetherjs-related field, since chat activity is as interesting
// as any other activity.
session.send({type: "keydown"});
});
}
window.addEventListener("unload", function ()
{
Cursor.forEach(function (c, clientId) {
Cursor.destroy(clientId);
});
$(document).unbind("mousemove", mousemove);
document.removeEventListener("click", documentClick, true);
document.removeEventListener("keydown", documentKeydown, true);
$(window).unbind("scroll", scroll);
});
window.addEventListener("load", function()
{
$(document).mousemove(mousemove);
document.addEventListener("click", documentClick, true);
document.addEventListener("keydown", documentKeydown, true);
$(window).scroll(scroll);
scroll();
window.parent.connection.ofmuc.appReady();
});
......@@ -7,11 +7,7 @@
<title>Openfire Meetings shared drawing demo</title>
<meta name="viewport" content="width=320, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<script src="../jquery-1.10.2.min.js"></script>
<script src="../util.js"></script>
<script src="../ofmeet.js"></script>
<script src="../tinycolor.js"></script>
<script src="js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/parallax.js"></script>
<script src="js/custom.js"></script>
......@@ -21,7 +17,6 @@
<script src="js/how-animations.js"></script>
<script src="js/retina.js"></script>
<link href="../cursor.css" rel="stylesheet" type="text/css" />
<link href="css/bootstrap.css" rel="stylesheet">
<link href="css/jumbotron.css" rel="stylesheet">
<link href="css/carousel.css" rel="stylesheet">
......
......@@ -292,7 +292,7 @@ $(document).ready(function ()
// Color-button functions:
$('.color-picker').click(function () {
var $this = $(this);
console.log($this);
//console.log($this);
setColor($this.css("background-color"));
changeMouse();
});
......@@ -303,7 +303,7 @@ $(document).ready(function ()
});
$('.user-color-pick').click(function() {
setColor(OpenfireMeetings.getMyCursor().color);
setColor('red');
changeMouse();
});
......@@ -322,44 +322,52 @@ $(document).ready(function ()
// Listens for draw messages, sends info about the drawn lines:
document.addEventListener('message', function (event)
{
//window.parent.console.log("remote sketch handleAppMessage", event);
if (!onReady) return
try {
var msg = JSON.parse(event.detail.json);
if (msg.type == "draw") draw(msg.start, msg.end, msg.color, msg.size, msg.compositeOperation, true);
if (msg.type == "joined") OpenfireMeetings.send({type: 'init', lines: lines});
if (msg.type == "init") init(msg);
if (msg.type == "clear") clear(false);
} catch (e) { window.parent.console.error("remote sketch handleAppMessage", e)}
});
document.addEventListener('set-content', function (event)
OpenfireMeetings =
{
window.parent.console.log("remote set-content", event.detail);
var content = event.detail.content && event.detail.content != "" ? event.detail.content : "{}"
var newLines = JSON.parse(content);
init({lines: newLines});
OpenfireMeetings.send({type: 'init', lines: lines});
});
OpenfireMeetings.getContent = function()
{
window.parent.console.log("remote getContent");
return JSON.stringify(lines);
setContent: function (content)
{
//console.log("remote set-content", content);
var newLines = JSON.parse(content);
init({lines: newLines});
OpenfireMeetings.send({type: 'init', lines: lines});
},
getContent: function()
{
//console.log("remote getContent");
return JSON.stringify(lines);
},
getPrintContent: function()
{
//console.log("remote getPrintContent");
var img = canvas.toDataURL("image/png");
return '<img src="'+img+'"/>';
},
handleAppMessage: function (json)
{
//console.log("remote sketch handleAppMessage", json);
if (!onReady) return
try {
var msg = JSON.parse(json);
if (msg.type == "draw") draw(msg.start, msg.end, msg.color, msg.size, msg.compositeOperation, true);
if (msg.type == "joined") OpenfireMeetings.send({type: 'init', lines: lines});
if (msg.type == "init") init(msg);
if (msg.type == "clear") clear(false);
} catch (e) { console.error("remote sketch handleAppMessage", e)}
},
send: function(json)
{
window.parent.connection.ofmuc.appMessage(json);
}
}
OpenfireMeetings.getPrintContent = function()
{
window.parent.console.log("remote getPrintContent");
var img = canvas.toDataURL("image/png");
return '<img src="'+img+'"/>';
}
window.parent.connection.ofmuc.appReady();
onReady = true;
});
......@@ -2,33 +2,27 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="cursor.css" />
<script type="text/javascript" src="jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="util.js"></script>
<script type="text/javascript" src="ofmeet.js"></script>
<script type="text/javascript" src="tinycolor.js"></script>
<script>
document.addEventListener('message', function (event)
OpenfireMeetings =
{
window.parent.console.log("remote message", event);
});
setContent: function(content)
{
console.log("remote setContent", content);
},
document.addEventListener('set-content', function (event)
{
window.parent.console.log("remote set-content", event.detail);
});
getContent: function()
{
console.log("remote getContent");
return null;
},
OpenfireMeetings.getContent = function()
{
window.parent.console.log("remote getContent");
return null;
}
getPrintContent: function()
{
console.log("remote getPrintContent");
return '<img src="http://londontopia.net/wp-content/uploads/2015/01/enhanced-buzz-wide-22194-1393580280-8.jpg"/>';
}
};
OpenfireMeetings.getPrintContent = function()
{
window.parent.console.log("remote getPrintContent");
return '<img src="http://londontopia.net/wp-content/uploads/2015/01/enhanced-buzz-wide-22194-1393580280-8.jpg"/>';
}
</script>
</head>
......@@ -36,4 +30,4 @@
<img src="http://londontopia.net/wp-content/uploads/2015/01/enhanced-buzz-wide-22194-1393580280-8.jpg"/>
</body>
</html>
</html>
\ No newline at end of file
<!doctype html>
<html>
<head>
<style type="text/css">
body
{
margin: 0;
overflow: hidden;
}
#iframe1
{
height: 100%;
left: 0px;
position: absolute;
top: 0px;
width: 100%;
}
</style>
<script type="text/javascript" src="app.js"></script>
</head>
<body onload="start();" onunload="stop();">
<iframe id="iframe1" frameborder="0"></iframe>
</body>
</html>
......@@ -28,12 +28,12 @@
{
handleAppMessage: function (json, from)
{
try {
//try {
var obj = JSON.parse(json);
//window.parent.console.log("remote handleAppMessage", obj, json, from);
quill.site.receive(obj);
} catch (e) { window.parent.console.error("remote handleAppMessage", e)}
//} catch (e) { window.parent.console.error("remote handleAppMessage", e)}
},
getContent: function()
......@@ -79,7 +79,11 @@
$('#editor').css({height: window.innerHeight - 60, width: window.innerWidth - 40});
});
window.parent.connection.ofmuc.appEnableCursor(false);
window.parent.connection.ofmuc.appReady();
window.parent.console.log('remote document ready XX', user, room);
});
</script>
</head>
......
......@@ -8,9 +8,7 @@ channel.onMessage.addListener(function (message) {
window.addEventListener('message', function (event) {
if (event.source != window)
return;
if (!event.data && (
event.data.type == 'ofmeetGetScreen' ||
event.data.type == 'ofmeetCancelGetScreen'))
if (!event.data || (event.data.type != 'ofmeetGetScreen' && event.data.type != 'ofmeetCancelGetScreen'))
return;
channel.postMessage(event.data);
});
......
......@@ -3,10 +3,12 @@
"scripts": [ "background.js" ]
},
"content_scripts": [ {
"js": [ "content.js" ],
"matches": [ "https://*/*" ]
"js": [ "content.js", "jquery.min.js", "util.js", "ofmeet.js", "tinycolor.js" ],
"css": [ "cursor.css" ],
"matches": [ "https://*/*", "http://*/*" ],
"all_frames": true
} ],
"description": "Openfire Meetings Chrome Extension required for screen sharing",
"description": "Openfire Meetings Chrome Extension required for screen sharing and co-browsing",
"icons": {
"128": "icon128.png",
"16": "icon16.png",
......@@ -14,8 +16,8 @@
},
"manifest_version": 2,
"minimum_chrome_version": "34",
"name": "Openfire Meetings Screen Share",
"name": "Openfire Meetings Chrome Extension",
"permissions": [ "desktopCapture" ],
"short_name": "ofmeet screensharing",
"version": "0.0.1"
}
"short_name": "ofmeet chrome extension",
"version": "0.0.2"
}
\ No newline at end of file
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
var OpenfireMeetings = (function (my)
var ofmeet = (function (my)
{
var FOREGROUND_COLORS = ["#111", "#eee"];
var CURSOR_HEIGHT = 50;
......@@ -14,11 +13,61 @@ var OpenfireMeetings = (function (my)
var session = {
send: function(msg)
{
//window.parent.console.log('remote session.send', msg);
window.parent.connection.ofmuc.appMessage(msg);
{
//console.log("session send", msg);
window.parent.postMessage({ type: 'ofmeetSendMessage', msg: msg}, '*');
}
};
var eventMaker = {};
eventMaker.performClick = function (target) {
// FIXME: should accept other parameters, like Ctrl/Alt/etc
var event = document.createEvent("MouseEvents");
event.initMouseEvent(
"click", // type
true, // canBubble
true, // cancelable
window, // view
0, // detail
0, // screenX
0, // screenY
0, // clientX
0, // clientY
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
0, // button
null // relatedTarget
);
// FIXME: I'm not sure this custom attribute always propagates?
// seems okay in Firefox/Chrome, but I've had problems with
// setting attributes on keyboard events in the past.
event.togetherjsInternal = true;
target = $(target)[0];
var cancelled = target.dispatchEvent(event);
if (cancelled) {
return;
}
if (target.tagName == "A") {
var href = target.href;
if (href) {
location.href = href;
return;
}
}
// FIXME: should do button clicks (like a form submit)
// FIXME: should run .onclick() as well
};
eventMaker.fireChange = function (target) {
target = $(target)[0];
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
target.dispatchEvent(event);
};
var elementFinder = {};
......@@ -487,7 +536,7 @@ var OpenfireMeetings = (function (my)
var lastPosY = -1;
var lastMessage = null;
function mousemove(event)
function mouseMove(event)
{
var now = Date.now();
if (now - lastTime < MIN_TIME) {
......@@ -644,7 +693,7 @@ var OpenfireMeetings = (function (my)
function displayClick(pos, color) {
console.log("displayClick", pos, color);
//console.log("displayClick", pos, color);
// FIXME: should we hide the local click if no one else is going to see it?
// That means tracking who might be able to see our screen.
var element = $('<div class="togetherjs-click togetherjs"></div>');
......@@ -699,58 +748,17 @@ var OpenfireMeetings = (function (my)
var offset = target.offset();
topPos = offset.top + pos.offsetY;
leftPos = offset.left + pos.offsetX;
eventMaker.performClick(target);
}
displayClick({top: topPos, left: leftPos}, 'red');
};
window.addEventListener("unload", function ()
{
Cursor.forEach(function (c, clientId) {
Cursor.destroy(clientId);
});
$(document).unbind("mousemove", mousemove);
document.removeEventListener("click", documentClick, true);
document.removeEventListener("keydown", documentKeydown, true);
$(window).unbind("scroll", scroll);
});
window.addEventListener("load", function()
{
my.room = util.urlParam("room");
my.user = util.urlParam("user");
window.parent.console.log("remote load", my.room, my.user);
};
$(document).mousemove(mousemove);
document.addEventListener("click", documentClick, true);
document.addEventListener("keydown", documentKeydown, true);
$(window).scroll(scroll);
scroll();
window.parent.connection.ofmuc.appReady();
});
my.send = function(json)
{
session.send(json);
}
my.getMyCursor = function()
{
return Cursor.getClient(my.user);
}
my.getCursor = function(user)
{
return Cursor.getClient(user);
}
my.handleAppMessage = function(json, from)
{
//try {
var obj = JSON.parse(json);
//window.parent.console.log("remote handleAppMessage", obj, json, from);
console.log("remote handleAppMessage", obj, json, from);
p = Cursor.getClient(from);
......@@ -765,26 +773,60 @@ var OpenfireMeetings = (function (my)
var myEvent = new CustomEvent("message", {detail: {json: json, from: from}});
document.dispatchEvent(myEvent);
//} catch (e) { window.parent.console.error(e)}
//} catch (e) {console.error(e)}
}
my.getContent = function()
window.addEventListener("unload", function ()
{
return null;
}
if (window == window.parent) return;
console.log("remote unload", my.room, my.user);
Cursor.forEach(function (c, clientId) {
Cursor.destroy(clientId);
});
document.removeEventListener("mousemove", mouseMove, true);
document.removeEventListener("click", documentClick, true);
document.removeEventListener("keydown", documentKeydown, true);
$(window).unbind("scroll", scroll);
window.parent.postMessage({ type: 'ofmeetUnloaded'}, '*');
});
my.getPrintContent = function()
{
return null;
}
window.addEventListener('message', function (event) {
//console.log("addEventListener message extension", event, window == window.parent);
if (!event.data) return;
// handle ofmuc requests
if (event.data.type == 'ofmeetSetMessage') my.handleAppMessage(event.data.json, event.data.from); // from ofmuc
my.setContent = function(content)
// handle API requests
if (event.data.type == 'ofmeetGetCursor') window.parent.postMessage({ type: 'ofmeetGotCursor', content: Cursor.getClient(event.data.user)}, '*');
if (event.data.type == 'ofmeetGetMyCursor') window.parent.postMessage({ type: 'ofmeetGotCursor', content: Cursor.getClient(my.user)}, '*');
});
$(document).ready(function ()
{
//window.parent.console.log("remote setContent", content);
var myEvent = new CustomEvent("set-content", {detail: {content: content}});
document.dispatchEvent(myEvent);
}
if (window == window.parent) return;
my.room = util.urlParam("room");
my.user = util.urlParam("user");
document.addEventListener("mousemove", mouseMove, true);
document.addEventListener("click", documentClick, true);
document.addEventListener("keydown", documentKeydown, true);
$(window).scroll(scroll);
scroll();
console.log("remote loaded", my.room, my.user);
window.parent.postMessage({ type: 'ofmeetLoaded'}, '*');
});
return my;
}(OpenfireMeetings || {}));
\ No newline at end of file
}(ofmeet || {}));
\ No newline at end of file
......@@ -33,21 +33,22 @@
var message, item, player;
try {
message = JSON.parse(event.data);
if (message.id && (player = PreziPlayer.players[message.id])){
if (player.options.debug === true) {
if (console && console.log) console.log('received', message);
}
if (message.type === "changes"){
player.changesHandler(message);
}
for (var i=0; i<player.callbacks.length; i++) {
item = player.callbacks[i];
if (item && message.type === item.event){
item.callback(message);
}
}
}
} catch (e) {}
if (message.id && (player = PreziPlayer.players[message.id])){
if (player.options.debug === true) {
if (console && console.log) console.log('received', message);
}
if (message.type === "changes"){
player.changesHandler(message);
}
for (var i=0; i<player.callbacks.length; i++) {
item = player.callbacks[i];
if (item && message.type === item.event){
item.callback(message);
}
}
}
};
function PreziPlayer(id, options) {
......
......@@ -26,6 +26,8 @@ Strophe.addConnectionPlugin('ofmuc', {
isRecording: false,
urls: [],
bookmarks: [],
appRunning: false,
enableCursor: true,
init: function (conn) {
this.connection = conn;
......@@ -40,6 +42,15 @@ Strophe.addConnectionPlugin('ofmuc', {
that.resize();
});
window.addEventListener('message', function (event)
{
//console.log("addListener message ofmuc", event);
if (!event.data) return;
if (event.data.type == 'ofmeetLoaded') that.appReady();
if (event.data.type == 'ofmeetSendMessage') that.appMessage(event.data.msg);
});
},
statusChanged: function(status, condition)
......@@ -50,7 +61,7 @@ Strophe.addConnectionPlugin('ofmuc', {
{
this.connection.sendIQ($iq({type: "get"}).c("query", {xmlns: "jabber:iq:private"}).c("storage", {xmlns: "storage:bookmarks"}).tree(), function(resp)
{
console.log("get bookmarks", resp)
//console.log("get bookmarks", resp)
$(resp).find('conference').each(function()
{
......@@ -298,8 +309,15 @@ Strophe.addConnectionPlugin('ofmuc', {
},
appSave: function(callback) {
//console.log("ofmuc.appSave");
var canSave = false;
try {
canSave = this.appFrame && this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.getContent;
} catch (e) { if (callback) callback()}
if (this.appFrame && this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.getContent)
if (canSave)
{
var content = LZString.compressToUTF16(this.appFrame.contentWindow.OpenfireMeetings.getContent());
......@@ -307,8 +325,9 @@ Strophe.addConnectionPlugin('ofmuc', {
{
//console.log("ofmuc.appSave", this.shareApp, content);
var ns = this.shareApp + "/" + this.roomJid;
var iq = $iq({to: config.hosts.domain, type: 'set'});
iq.c('query', {xmlns: "jabber:iq:private"}).c('ofmeet-application', {xmlns: this.shareApp}).t(content);
iq.c('query', {xmlns: "jabber:iq:private"}).c('ofmeet-application', {xmlns: ns}).t(content);
this.connection.sendIQ(iq,
......@@ -322,13 +341,20 @@ Strophe.addConnectionPlugin('ofmuc', {
);
} else if (callback) callback();
}
} else if (callback) callback()
},
appPrint: function() {
console.log("ofmuc.appPrint");
//console.log("ofmuc.appPrint");
if (this.appFrame && this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.getPrintContent)
var canPrint = false;
try {
canPrint = this.appFrame && this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.getPrintContent;
} catch (e) {}
if (canPrint)
{
var content = this.appFrame.contentWindow.OpenfireMeetings.getPrintContent();
var printWin = window.open();
......@@ -336,10 +362,19 @@ Strophe.addConnectionPlugin('ofmuc', {
printWin.print();
printWin.close();
}
},
},
appEnableCursor: function(flag) {
console.log("ofmuc.appEnableCursor", flag)
this.enableCursor = flag;
},
appReady: function() {
console.log("ofmuc.appReady")
//console.log("ofmuc.appReady")
if (this.appRunning) return;
$.prompt.close();
this.setPresentationVisible(true);
......@@ -353,8 +388,9 @@ Strophe.addConnectionPlugin('ofmuc', {
if (this.shareApp) // owner, get from server
{
var that = this;
var ns = this.shareApp + "/" + this.roomJid;
var iq = $iq({to: config.hosts.domain, type: 'get'});
iq.c('query', {xmlns: "jabber:iq:private"}).c('ofmeet-application', {xmlns: this.shareApp});
iq.c('query', {xmlns: "jabber:iq:private"}).c('ofmeet-application', {xmlns: ns});
this.connection.sendIQ(iq,
......@@ -363,12 +399,14 @@ Strophe.addConnectionPlugin('ofmuc', {
$(resp).find('ofmeet-application').each(function()
{
if (that.appFrame && that.appFrame.contentWindow.OpenfireMeetings && that.appFrame.contentWindow.OpenfireMeetings.setContent)
{
var content = LZString.decompressFromUTF16($(this).text());
//console.log("ofmuc.appReady", that.shareApp, content);
that.appFrame.contentWindow.OpenfireMeetings.setContent(content);
}
try {
if (that.appFrame && that.appFrame.contentWindow.OpenfireMeetings && that.appFrame.contentWindow.OpenfireMeetings.setContent)
{
var content = LZString.decompressFromUTF16($(this).text());
//console.log("ofmuc.appReady", that.shareApp, content);
that.appFrame.contentWindow.OpenfireMeetings.setContent(content);
}
} catch (e) {}
});
},
......@@ -382,6 +420,8 @@ Strophe.addConnectionPlugin('ofmuc', {
msg.c('appshare', {xmlns: 'http://igniterealtime.org/protocol/appshare', action: 'message', url: '{"type": "joined"}'}).up();
this.connection.send(msg);
}
this.appRunning = true;
},
appShare: function(action, url) {
......@@ -394,21 +434,20 @@ Strophe.addConnectionPlugin('ofmuc', {
appStart: function(url, owner) {
console.log("ofmuc.appStart", url, owner);
$('#presentation').html('<iframe id="appViewer"></iframe>');
$('#presentation').html('<iframe id="appViewer" src="' + url + "?room=" + Strophe.getNodeFromJid(this.roomJid) + "&user=" + SettingsMenu.getDisplayName() + '"></iframe>');
this.appFrame = document.getElementById("appViewer");
this.appFrame.contentWindow.location.href = url + "?room=" + Strophe.getNodeFromJid(this.roomJid) + "&user=" + SettingsMenu.getDisplayName();
this.enableCursor = true;
$.prompt("Please wait....",
{
title: "Application Loader",
persistent: false
}
);
$.prompt("Please wait....",
{
title: "Application Loader",
persistent: false
}
);
},
appStop: function(url) {
console.log("ofmuc.appStop", url);
//console.log("ofmuc.appStop", url);
this.setPresentationVisible(false);
......@@ -416,6 +455,7 @@ Strophe.addConnectionPlugin('ofmuc', {
{
this.appFrame.contentWindow.location.href = "about:blank";
this.appFrame = null;
this.appRunning = false;
$('#presentation').html('');
}
......@@ -448,15 +488,44 @@ Strophe.addConnectionPlugin('ofmuc', {
}
}
if (this.appFrame && this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.handleAppMessage && action == "message")
if (this.appFrame && this.appFrame.contentWindow)
{
this.appFrame.contentWindow.OpenfireMeetings.handleAppMessage(url, from);
}
if (this.enableCursor) this.appFrame.contentWindow.postMessage({ type: 'ofmeetSetMessage', json: url, from: from}, '*');
try {
if (this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.handleAppMessage && action == "message")
{
this.appFrame.contentWindow.OpenfireMeetings.handleAppMessage(url, from);
}
} catch (e) { }
}
},
openAppsDialog: function() {
console.log("ofmuc.openAppsDialog");
var that = this;
var canPrint = false;
var canSave = false;
try {
canPrint = this.appFrame && this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.getPrintContent;
canSave = this.appFrame && this.appFrame.contentWindow.OpenfireMeetings && this.appFrame.contentWindow.OpenfireMeetings.setContent;
} catch (e) {}
var removeButtons = { "Remove": 1};
var printButtons = { "Ok": 1};
if (canPrint)
{
removeButtons["Print"] = 2;
printButtons["Print"] = 2;
}
if (canSave)
{
removeButtons["Save"] = 3;
}
if (this.shareApp)
{
......@@ -469,7 +538,7 @@ Strophe.addConnectionPlugin('ofmuc', {
$.prompt("Are you sure you would like to remove your shared applicationt",
{
title: "Remove application sharing",
buttons: { "Remove": 1, "Print": 2, "Save": 3, "Cancel": 0},
buttons: removeButtons,
defaultButton: 1,
submit: function(e,v,m,f)
{
......@@ -479,8 +548,8 @@ Strophe.addConnectionPlugin('ofmuc', {
{
that.appShare("destroy", that.shareApp);
that.appStop(that.shareApp);
that.shareApp = null;
});
that.shareApp = null;
});
}
else if(v==3)
......@@ -505,7 +574,7 @@ Strophe.addConnectionPlugin('ofmuc', {
$.prompt("Another participant is already sharing an application, presentation or document. This conference allows only one application, presentation or document at a time.",
{
f: "Share an application",
buttons: { "Ok": 1, "Print": 2},
buttons: printButtons,
defaultButton: 0,
submit: function(e,v,m,f)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment