Initial commit of Islams Shared Notes module. Module is off by default

This commit is contained in:
@deniszgonjanin 2011-01-29 01:35:07 -05:00
parent 941882409e
commit fc7296d88c
16 changed files with 1062 additions and 0 deletions

View File

@ -0,0 +1,113 @@
<!--
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
-->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
implements="org.bigbluebutton.common.IBigBlueButtonModule"
creationComplete="onCreationComplete()" >
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.containers.MDIWindow;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.main.events.CloseWindowEvent;
import org.bigbluebutton.main.events.OpenWindowEvent;
import org.bigbluebutton.main.events.ToolbarButtonEvent;
import org.bigbluebutton.modules.sharednotes.SharedNotesWindow;
import org.bigbluebutton.modules.sharednotes.ToolbarButton;
import org.bigbluebutton.modules.sharednotes.infrastructure.Client;
import org.bigbluebutton.modules.sharednotes.infrastructure.HTTPServerConnection;
private var _moduleId:String = "NotesModule";
private var _moduleName:String = "Notes Module";
private var _attributes:Object;
private var globalDispatcher:Dispatcher = new Dispatcher();
private function onCreationComplete():void {
LogUtil.debug("NotesModule Initialized");
globalDispatcher = new Dispatcher();
}
public function get moduleId():String {
return _moduleId;
}
public function get moduleName():String {
return _moduleName;
}
public function get uri():String {
if (_attributes.mode == "PLAYBACK") {
return _attributes.uri + "/" + _attributes.playbackRoom;
}
return _attributes.uri + "/" + _attributes.room;
}
public function get username():String {
return _attributes.username;
}
public function get connection():NetConnection {
return _attributes.connection;
}
public function get mode():String {
if (_attributes.mode == null) {
_attributes.mode = "LIVE"
LogUtil.debug('Setting NotesModule mode: ' + _attributes.mode);
}
LogUtil.debug('NotesModule mode: ' + _attributes.mode);
return _attributes.mode;
}
public function get userid():Number {
return _attributes.userid as Number;
}
public function get role():String {
return _attributes.userrole as String;
}
public function start(attributes:Object):void {
LogUtil.debug("notes attr: " + attributes.username);
_attributes = attributes;
SharedNotesWindow.document = _attributes.room;
// The following line has been removed, as the uri should be in
// the URI property in the config.xml file of the client
// HTTPServerConnection.syncURL = (_attributes.uri as String).replace("RTMP", "http") + "/notes/notes.jsp";
addToolbarButton();
}
public function addToolbarButton():void{
var button:ToolbarButton = new ToolbarButton();
var event:ToolbarButtonEvent = new ToolbarButtonEvent(ToolbarButtonEvent.ADD);
event.button = button;
globalDispatcher.dispatchEvent(event);
}
public function stop():void {}
]]>
</mx:Script>
</mx:Module>

View File

@ -0,0 +1,220 @@
<!--
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
-->
<containers:MDIWindow xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:containers="flexlib.mdi.containers.*"
layout="absolute" width="508" height="494" implements="org.bigbluebutton.common.IBbbModuleWindow"
title="Notes" creationComplete="init()" xmlns:components="org.bigbluebutton.modules.sharednotes.components.*">
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flash.utils.getTimer;
import flexlib.mdi.events.MDIWindowEvent;
import mx.events.ResizeEvent;
import mx.events.SliderEvent;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.sharednotes.infrastructure.Client;
import org.bigbluebutton.modules.sharednotes.infrastructure.ServerConnection;
private var _xPosition:int, _yPosition:int;
private var _defaultWidth:int = 400;
private var _defaultHeight:int = 300;
private var client:org.bigbluebutton.modules.sharednotes.infrastructure.Client;
private static var _room:String;
public static const SHARED_NOTES_CLOSED:String = "SHARED_NOTES_CLOSED";
private var resizeTimer:Timer = new Timer(500);
public function init():void {
textArea.addEventListener(Event.CHANGE, onTextChange);
addEventListener(ServerConnection.SYNCED_EVENT, onSynced);
addEventListener(ServerConnection.SYNCING_EVENT, onSyncing);
addEventListener(MDIWindowEvent.RESIZE, onResize);
addEventListener(MDIWindowEvent.MAXIMIZE, function(e:Event):void { resizeTimer.start(); });
addEventListener(MDIWindowEvent.RESTORE, function(e:Event):void { resizeTimer.start(); });
addEventListener(MDIWindowEvent.CLOSE, function(e:Event):void { if (client) client.shutdown(); });
resizeTimer.addEventListener(TimerEvent.TIMER, onResizeTimer);
client = new Client(textArea, this);
}
private function onResizeTimer(e:Event):void {
onResize();
resizeTimer.stop();
}
private function addBlur():void {
var bf:BlurFilter = new BlurFilter(0,0,0);
var myFilters:Array = new Array();
myFilters.push(bf);
textArea.filters = myFilters;
}
private function onResize(e:Event = null):void {
const RIGHT_PADDING:int = textArea.x * 2, TOP_PADDING:int = 35, BOTTOM_PADDING:int = 60;
textArea.width = canvas.width - RIGHT_PADDING;
textArea.height = canvas.height - BOTTOM_PADDING;
txtPlayback.width = canvas.width - RIGHT_PADDING;
txtPlayback.height = canvas.height - BOTTOM_PADDING - 20;
btnPlayback.y = canvas.height - TOP_PADDING;
btnBackPlayback.y = canvas.height - TOP_PADDING;
icoSynced.y = canvas.height - TOP_PADDING;
icoSyncing.y = canvas.height - TOP_PADDING;
icoSynced.x = canvas.width - RIGHT_PADDING - 10;
icoSyncing.x = canvas.width - RIGHT_PADDING - 10;
playbackSlider.width = canvas.width - playbackSlider.x - RIGHT_PADDING;
playbackSlider.y = canvas.height - BOTTOM_PADDING - 10;
lblPlaybackVersion.y = playbackSlider.y + 3;
}
private function onSynced(e:Event):void {
if (client.version > 0 && !btnBackPlayback.visible) { btnPlayback.visible = true; }
icoSynced.visible = true;
icoSyncing.visible = false;
lblConnecting.visible = false;
}
private function onSyncing(e:Event):void {
icoSyncing.visible = true;
icoSynced.visible = false;
}
private function onTextChange(e:Event):void {
onSyncing(e);
}
protected function btnPlay_clickHandler(event:MouseEvent):void
{
if (client.isTypingTestRunning())
{
client.stopTyping();
btnPlay.label = "Start";
}
else
{
client.startTyping();
btnPlay.label = "Stop";
}
}
private var previousSliderValue:int = 0;
protected function playbackSlider_changeHandler(event:SliderEvent):void
{
txtPlayback.text = client.getSnapshotAtVersion(previousSliderValue, event.value, txtPlayback.text);
lblPlaybackVersion.text = "Version " + event.value + ":";
}
protected function btnPlayback_clickHandler(event:MouseEvent):void
{
playbackSlider.maximum = client.version;
playbackSlider.value = 0;
textArea.visible = false;
btnBackPlayback.visible = true;
btnPlayback.visible = false;
previousSliderValue = 0;
txtPlayback.text = client.getSnapshotAtVersion(0, 0, "");
}
protected function btnBackPlayback_clickHandler(event:MouseEvent):void
{
textArea.visible = true;
btnBackPlayback.visible = false;
btnPlayback.visible = true;
}
public function get xPosition():int {
return _xPosition;
}
public function get yPosition():int {
return _yPosition;
}
public function set xPosition(x:int):void {
_xPosition = x;
}
public function set yPosition(y:int):void {
_yPosition = y;
}
public function get defaultWidth():int{
return _defaultWidth;
}
public function get defaultHeight():int{
return _defaultHeight;
}
public function set defaultHeight(height:int):void{
this._defaultHeight = height;
}
public function set defaultWidth(width:int):void{
this._defaultWidth = width;
}
public function resetWidthAndHeight():void{
this.width = _defaultWidth;
this.height = _defaultHeight;
}
public function getPrefferedPosition():String{
return MainCanvas.MIDDLE;
}
public static function set document(document:String):void {
Client.documentName = document;
}
override public function close(event:MouseEvent=null):void {
new Dispatcher().dispatchEvent(new Event(SHARED_NOTES_CLOSED, true));
super.close(event);
}
]]>
</mx:Script>
<mx:Fade id="dissolveOut" duration="1000" alphaFrom="1.0" alphaTo="0.0"/>
<mx:Fade id="dissolveIn" duration="1000" alphaFrom="0.0" alphaTo="1.0"/>
<mx:Canvas id="canvas" x="0" y="0" width="100%" height="100%">
<mx:Button x="10" y="425" label="Playback" id="btnPlayback" click="btnPlayback_clickHandler(event)" icon="@Embed(source='images/play-icon.png')" visible="false"/>
<mx:HSlider x="79" y="395" width="413" id="playbackSlider" liveDragging="true" snapInterval="1" change="playbackSlider_changeHandler(event)" maximum="0"/>
<mx:TextArea x="10" y="10" width="482" height="387" id="txtPlayback"/>
<mx:Image x="476" y="431" source="@Embed(source='images/tick.png')" id="icoSynced" visible="false" toolTip="Document up to date."/>
<mx:Image x="476" y="431" source="@Embed(source='images/action_refresh.gif')" id="icoSyncing" visible="false" toolTip="Updating document..."/>
<mx:Button x="10" y="425" icon="@Embed(source='images/arrow_left.png')" id="btnBackPlayback" click="btnBackPlayback_clickHandler(event)" visible="false"/>
<mx:Label id="lblPlaybackVersion" x="10" y="399" text="Version 0:"/>
<components:PatchableTextArea x="10" y="10" id="textArea" hideEffect="{dissolveOut}" showEffect="{dissolveIn}" creationComplete="addBlur()" width="482" height="407"/>
<mx:Button x="114" y="425" label="Start" id="btnPlay" click="btnPlay_clickHandler(event)" visible="false"/>
<mx:Label x="211.5" y="186" text="Connecting..." id="lblConnecting"/>
</mx:Canvas>
</containers:MDIWindow>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton - http://www.bigbluebutton.org
Copyright (c) 2008-2009 by respective authors (see below). All rights reserved.
BigBlueButton is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, If not, see <http://www.gnu.org/licenses/>.
$Id: $
-->
<mx:Button xmlns:mx="http://www.adobe.com/2006/mxml" icon="{notesIcon}"
click="openPublishWindow()" creationComplete="init()" toolTip="{ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip')}" xmlns:mate="http://mate.asfusion.com/">
<mate:Listener type="{SharedNotesWindow.SHARED_NOTES_CLOSED}" method="closeEventHandler" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.main.events.OpenWindowEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
[Embed(source="images/note_edit.png")]
public var notes:Class;
[Bindable] public var notesIcon:Class = notes;
private var dispatcher:Dispatcher;
private function init():void{
dispatcher = new Dispatcher();
this.addEventListener(SharedNotesWindow.SHARED_NOTES_CLOSED, closeEventHandler);
}
private function openPublishWindow():void{
var window:IBbbModuleWindow = new SharedNotesWindow();
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
event.window = window;
dispatcher.dispatchEvent(event);
this.enabled = false;
}
public function show():void{
this.enabled = true;
}
private function closeEventHandler(e:Event):void {
show();
}
]]>
</mx:Script>
</mx:Button>

View File

@ -0,0 +1,89 @@
/*
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
*/
package org.bigbluebutton.modules.sharednotes.components
{
import mx.controls.TextArea;
import org.bigbluebutton.modules.sharednotes.util.DiffPatch;
public class PatchableTextArea extends TextArea
{
private var _tackOnText : String = "";
private var _tackOnTextChanged : Boolean = false;
private var _patch : String = "";
private var _patchChanged : Boolean = false;
public function set tackOnText(value:String):void
{
_tackOnText = value;
_tackOnTextChanged = true;
invalidateProperties();
}
public function get tackOnText():String
{
return _tackOnText;
}
public function set patch(value:String):void
{
_patch = value;
_patchChanged = true;
invalidateProperties();
}
public function get patch():String
{
return _patch;
}
override protected function commitProperties():void
{
super.commitProperties();
if (_patchChanged) {
patchClientText();
patch = "";
_patchChanged = false;
}
if(_tackOnTextChanged) {
this.textField.text += tackOnText;
tackOnText = "";
_tackOnTextChanged = false;
}
}
public function get textFieldText():String {
return this.textField.text;
}
private function patchClientText():void {
var results:Array = DiffPatch.patchClientText(patch, textField.text, selectionBeginIndex, selectionEndIndex);
textField.text = results[1];
var cursorSelection:Array = results[0];
textField.setSelection(cursorSelection[0], cursorSelection[1]);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

View File

@ -0,0 +1,207 @@
/*
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
*/
package org.bigbluebutton.modules.sharednotes.infrastructure
{
import com.adobe.crypto.SHA1;
import com.adobe.serialization.json.JSON;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.TimerEvent;
import flash.utils.Timer;
import org.bigbluebutton.modules.sharednotes.components.PatchableTextArea;
import org.bigbluebutton.modules.sharednotes.util.DiffPatch;
public class Client
{
private var _id:int;
private var textArea:PatchableTextArea; // the text component to display the document
private var documentShadow:String = ""; // the shadow of the current document
private var initialDocument:String; // for storing the initial document
private var logPrefix:String; // used for logging
private var patchHistory:Array = new Array(); // a history of the patches
private var server:ServerConnection;
private var testCharacterTimer:Timer;
private var documentCheckTimer:Timer; // timer to check for changes in the document
private var timeoutTimer:Timer = new Timer(5000); // setting the timeout for server requests to 5 seconds
public static var documentName:String = "";
public function Client(textComponent:PatchableTextArea, dispatcher:IEventDispatcher) {
textArea = textComponent;
server = new HTTPServerConnection(this, dispatcher);
}
public function initClient(id:int, serverConnection:ServerConnection, document:String = ""):void {
_id = id;
documentShadow = new String(document);
textArea.text = new String(document);
initialDocument = new String(document);
server = serverConnection;
logPrefix = "[Client " + id + "] ";
initDocumentCheckTimer();
timeoutTimer.addEventListener(TimerEvent.TIMER, function(e:Event):void {
timeoutTimer.stop();
sendMessage();
});
// used for testing
testCharacterTimer = new Timer(10);
testCharacterTimer.addEventListener(TimerEvent.TIMER, playSentence);
}
private function initDocumentCheckTimer():void {
documentCheckTimer = new Timer(500);
documentCheckTimer.addEventListener(TimerEvent.TIMER, documentCheckEventHandler);
documentCheckTimer.start();
}
private function documentCheckEventHandler(e:TimerEvent):void {
if (!server.pendingResponse) {
sendMessage();
}
}
public function sendMessage():void {
var messageToSend:Message = new Message(id, documentName, ServerConnection.connectionType);
if (documentShadow != textArea.textFieldText) {
trace("****** SENDING MESSAGE *******");
textArea.editable = false;
var clientText:String = new String(textArea.textFieldText); // a snapshot of the client text
messageToSend.patchData = DiffPatch.diff(documentShadow, clientText);
patchHistory.push(messageToSend.patchData);
documentShadow = clientText;
messageToSend.checksum = SHA1.hash(documentShadow);
textArea.editable = true;
trace(logPrefix + "sending " + messageToSend);
}
server.send("m, " + JSON.encode(messageToSend));
timeoutTimer.start();
}
public function receiveMessage(serverMessage:Message): void {
timeoutTimer.stop(); // we received a response - cancel the time out
trace(logPrefix + "received message.\nMessage: " + serverMessage);
if (serverMessage.patchData != "") {
var result:String = DiffPatch.patch(serverMessage.patchData, documentShadow);
if (SHA1.hash(result) == serverMessage.checksum) {
documentShadow = result;
textArea.patch = serverMessage.patchData;
patchHistory.push(serverMessage.patchData);
}
else {
throw new Error("Checksum mismatch");
}
}
server.pendingResponse = false;
}
public function getSnapshotAtVersion(initialVersion:int, finalVersion:int, documentSnapshot:String = ""):String {
if (initialVersion == 0) documentSnapshot = initialDocument;
if (initialVersion < finalVersion) {
for (var i:int = initialVersion; i < finalVersion; i++) {
documentSnapshot = DiffPatch.patch(patchHistory[i], documentSnapshot);
}
}
else {
for (i = finalVersion; i < initialVersion;i++) {
documentSnapshot = DiffPatch.unpatch(patchHistory[i], documentSnapshot);
}
}
return documentSnapshot;
}
public function startTyping():void {
testCharacterTimer.start();
}
public function stopTyping():void {
testCharacterTimer.stop();
}
private var testSentence:String = "The quick brown fox jumps over the lazy dog.";
private var testCounter:int = 0;
private function playSentence(event:TimerEvent):void {
if (textArea.editable) {
if (testCounter == testSentence.length) {
testCounter = 0;
textArea.tackOnText += "\n";
}
else {
textArea.tackOnText += testSentence.charAt(testCounter++);
}
textArea.editable = true;
}
}
public function isTypingTestRunning():Boolean {
return testCharacterTimer.running;
}
private var pendingServerMessage:Message;
private function startReceive(e:TimerEvent):void {
receiveMessage(pendingServerMessage);
}
private function timeoutHandler(event:TimerEvent):void {
sendMessage();
}
public function get version():int { return patchHistory.length; }
public function get id():int { return _id; }
public function shutdown():void {
server.shutdown();
if (testCharacterTimer) testCharacterTimer.stop();
if (documentCheckTimer) documentCheckTimer.stop();
if (timeoutTimer) timeoutTimer.stop();
}
}
}

View File

@ -0,0 +1,64 @@
/*
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
*/
package org.bigbluebutton.modules.sharednotes.infrastructure
{
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
public class HTTPServerConnection extends ServerConnection
{
public static var syncURL:String = "";
private var loader:URLLoader = new URLLoader(); // used for connecting to the server
public function HTTPServerConnection(client:Client, dispatcher:IEventDispatcher)
{
super(client, dispatcher);
ServerConnection.connectionType = "http";
loader.addEventListener(Event.COMPLETE, completeHandler);
sendConnectRequest();
}
private function completeHandler(event:Event):void {
var loader:URLLoader = URLLoader(event.target);
loader.close();
receive(loader.data);
}
public override function send(message:String):void {
var params : URLVariables = new URLVariables();
params.message = message;
var request:URLRequest = new URLRequest(syncURL);
request.data = params;
request.method = URLRequestMethod.POST;
try {
loader.load(request);
pendingResponse = true;
} catch (error:Error) {
trace("Unable to load requested document.");
}
}
}
}

View File

@ -0,0 +1,53 @@
/*
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
*/
package org.bigbluebutton.modules.sharednotes.infrastructure
{
public class Message
{
public var senderId:int;
public var patchData:String;
public var checksum:String;
public var documentName:String;
public var conType:String;
public function Message(senderId:int, documentName:String, conType:String, patchData:String = "" , checksum:String = ""){
this.senderId = senderId;
this.documentName = documentName;
this.patchData = patchData;
this.checksum = checksum;
this.conType = conType;
}
public static function deserialize(o:Object):Message {
var patchData:String = "", checksum:String = "";
if (o.checksum) checksum = o.checksum;
if (o.patchData) patchData = o.patchData;
return new Message(o.senderId, o.documentName, o.conType, patchData, checksum);
}
public function toString():String {
return "Message: " + ", senderId " + senderId + ", patchData: " + patchData + ", checksum " + checksum
}
}
}

View File

@ -0,0 +1,33 @@
/*
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
*/
package org.bigbluebutton.modules.sharednotes.infrastructure
{
public class Patch
{
public var version:int, data:String;
public function Patch(version:int, data:String)
{
this.version = version;
this.data = data;
}
}
}

View File

@ -0,0 +1,97 @@
/*
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
*/
package org.bigbluebutton.modules.sharednotes.infrastructure
{
import com.adobe.serialization.json.JSON;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class ServerConnection
{
public static const SYNCING_EVENT : String = "SN_SYNCING_EVENT";
public static const SYNCED_EVENT : String = "SN_SYNCED_EVENT";
private var client:Client;
protected var dispatcher:IEventDispatcher;
private var _pendingResponse:Boolean = false; // flag indicating if a response has been received from the server
private var connectionTimeout:Timer = new Timer(5000); //TODO: change the timeout
public static var connectionType:String = ""; // determines the type of server connection (whether socket or http)
public function ServerConnection(client:Client, dispatcher:IEventDispatcher) {
this.client = client;
this.dispatcher = dispatcher;
connectionTimeout.addEventListener(TimerEvent.TIMER, function(e:Event):void {
connectionTimeout.stop();
sendConnectRequest();
});
}
protected function sendConnectRequest():void {
var request:Object = new Object();
request.documentName = Client.documentName;
request.connectionType = ServerConnection.connectionType;
send("c, " + JSON.encode(request));
connectionTimeout.start();
}
public function send(message:String):void { } // to be overridden
protected function receive(data:String):void {
if (data.indexOf("c,") == 0) {
trace("Received connection data: " + data);
var clientData:Object = JSON.decode(data.substring(2));
client.initClient(clientData.id, this, clientData.initialDocument);
connectionTimeout.stop();
pendingResponse = false;
}
else if (data.indexOf("m,") == 0) {
var message:Message = Message.deserialize(JSON.decode(data.substring(2)));
client.receiveMessage(message);
}
else {
trace("unrecognized data: " + data);
}
}
public function get pendingResponse():Boolean {
return _pendingResponse;
}
public function set pendingResponse(value : Boolean):void {
_pendingResponse = value;
if (_pendingResponse) {
dispatcher.dispatchEvent(new Event(SYNCING_EVENT));
} else {
dispatcher.dispatchEvent(new Event(SYNCED_EVENT));
}
}
public function shutdown():void {
connectionTimeout.stop();
}
}
}

View File

@ -0,0 +1,80 @@
/*
This file is part of BBB-Notes.
Copyright (c) Islam El-Ashi. All rights reserved.
BBB-Notes is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
BBB-Notes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with BBB-Notes. If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>, <http://www.ielashi.com>
*/
package org.bigbluebutton.modules.sharednotes.infrastructure
{
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.TimerEvent;
import flash.net.XMLSocket;
import flash.utils.Timer;
public class XMLServerConnection extends ServerConnection
{
public static var serverURL:String = "192.168.0.238";
public static const XML_CONNECTION_FAILED:String = "XML_CONNECTION_FAILED";
private static const MAXIMUM_CONNECTION_ATTEMPTS:int = 5;
private var _socket: XMLSocket;
private var timeoutTimer: Timer = new Timer(5000);
private var connectionAttempts:int = MAXIMUM_CONNECTION_ATTEMPTS; // attempt to connect five times before dispatching a failure
private function get socket():XMLSocket {
return _socket;
}
public function XMLServerConnection(client:Client, dispatcher:IEventDispatcher)
{
super(client, dispatcher);
ServerConnection.connectionType = "xmlsocket";
timeoutTimer.addEventListener(TimerEvent.TIMER, function(e:Event):void { timeoutTimer.stop(); connect(); });
connect();
}
public function connect():void {
_socket = new XMLSocket();
_socket.addEventListener(Event.CONNECT, function(e:Event):void {
connectionAttempts = MAXIMUM_CONNECTION_ATTEMPTS;
sendConnectRequest();
});
_socket.addEventListener(DataEvent.DATA, function(e:DataEvent):void { receive(e.data); });
socket.connect(serverURL, 8095);
}
public override function send(message:String):void {
try {
pendingResponse = true;
socket.send(message);
} catch(e:Error) {
//socket.close();
if (connectionAttempts > 0) {
trace(e.message);
connectionAttempts--;
timeoutTimer.start();
} else {
dispatcher.dispatchEvent(new Event(XML_CONNECTION_FAILED));
}
}
}
}
}

View File

@ -0,0 +1,39 @@
package org.bigbluebutton.modules.sharednotes.util
{
import flash.external.ExternalInterface;
public class DiffPatch
{
public static function diff(originalText:String, modifiedText:String):String {
var result:String = ExternalInterface.call("diff", escape(originalText), escape(modifiedText));
if (result == null) throw new Error("Javascript files not found.");
return result;
}
public static function patch(diff:String, text:String):String {
var result:String = ExternalInterface.call("patch", diff, escape(text));
if (result == null) throw new Error("Javascript files not found.");
return result;
}
public static function unpatch(diff:String, text:String):String {
var result:String = unescape(ExternalInterface.call("unpatch", diff, escape(text)));
if (result == null) throw new Error("Javascript files not found.");
return result;
}
public static function patchClientText(patches:String, clientText:String, selectionStart:int, selectionEnd:int):Array {
var result:Array = ExternalInterface.call("patchClientText", patches, escape(clientText), selectionStart, selectionEnd);
if (result == null) throw new Error("Javascript files not found.");
return result;
}
}
}