diff --git a/Scrummer/Code/Controls/ChatControl.cs b/Scrummer/Code/Controls/ChatControl.cs
new file mode 100644
index 0000000..7b13adc
--- /dev/null
+++ b/Scrummer/Code/Controls/ChatControl.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.HtmlControls;
+using System.Web.UI.WebControls;
+
+namespace Scrummer.Code.Controls
+{
+ public class ChatControl : Control
+ {
+ private int _idBoard = 0;
+
+ public int IDBoard
+ {
+ get { return _idBoard; }
+ set { _idBoard = value; }
+ }
+
+ public ChatControl()
+ {
+ Init += ChatControl_Init;
+ }
+
+ void ChatControl_Init(object sender, EventArgs e)
+ {
+ var divChat = new Panel { ID = "divChat", CssClass = "divChat" };
+ Controls.Add(divChat);
+
+ var hidIDMessage = new HiddenField { ID = "hidIDMessage", Value = "0" };
+ Controls.Add(hidIDMessage);
+
+ var txtText = new TextBox { ID = "txtText", CssClass = "chatTextBox" };
+ Controls.Add(txtText);
+
+ var btnSend = new Button { ID = "btnSend", Text = "Send", CssClass = "chatButton" };
+ Controls.Add(btnSend);
+ btnSend.Attributes.Add("onclick", String.Format("SendChat('{0}',{1}); return false;", txtText.ClientID, _idBoard));
+
+
+ LiteralControl litScript = new LiteralControl();
+ litScript.Text = String.Format("",
+ divChat.ClientID, _idBoard, hidIDMessage.ClientID);
+ Controls.Add(litScript);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scrummer/Code/Controls/ChatHandler.cs b/Scrummer/Code/Controls/ChatHandler.cs
new file mode 100644
index 0000000..dd07d0e
--- /dev/null
+++ b/Scrummer/Code/Controls/ChatHandler.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Web;
+using Scrummer.Code.JSON;
+
+namespace Scrummer.Code
+{
+ public class Message
+ {
+ public int IDMessage { get; set; }
+ public string UserName { get; set; }
+ public string Text { get; set; }
+ };
+
+ public class MessageBoard
+ {
+ private List _messages = new List();
+ private int lastIDMessage = 0;
+
+ public List Messages_GetList(int idMessage)
+ {
+ List listMessages = new List();
+ for (int i = 0, n = _messages.Count; i < n; i++)
+ {
+ Message msg = _messages[i];
+ if (msg.IDMessage > idMessage)
+ {
+ listMessages.Insert(0, msg);
+ }
+ else
+ {
+ break;
+ }
+ }
+ return listMessages;
+ }
+
+ public void Message_Add(string userName, string text)
+ {
+ lastIDMessage++;
+ Message msg = new Message();
+ msg.IDMessage = lastIDMessage;
+ msg.UserName = userName;
+ msg.Text = text;
+ _messages.Insert(0, msg);
+ }
+ }
+
+ public class ChatHandler : IHttpHandler
+ {
+ private static object _monitor = new object();
+ private static Dictionary _chatBoards = new Dictionary();
+
+ public bool IsReusable
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public void ProcessRequest(HttpContext context)
+ {
+ if (context.Request.RequestType == "GET")
+ {
+ ProcessRevicer(context);
+ }
+ if (context.Request.RequestType == "POST")
+ {
+ ProcessSender(context);
+ }
+ }
+
+ private void ProcessRevicer(HttpContext context)
+ {
+ int idBoard = Convert.ToInt32(context.Request.Params["idBoard"]);
+ int idMessage = Convert.ToInt32(context.Request.Params["idMessage"]);
+ MessageBoard messageBoard;
+ bool mustWait = true;
+ do
+ {
+ if (_chatBoards.ContainsKey(idBoard))
+ {
+ messageBoard = _chatBoards[idBoard];
+ List listMessages = messageBoard.Messages_GetList(idMessage);
+ if (listMessages.Count > 0)
+ {
+ mustWait = false;
+ ResponseObject(context, listMessages);
+ }
+ }
+ if (mustWait)
+ {
+ lock (_monitor) { Monitor.Wait(_monitor, 10000); }
+ }
+ } while (mustWait);
+ }
+
+ private void ProcessSender(HttpContext context)
+ {
+ string strIDBoard = GetRequestParm(context, "idBoard");
+ int idBoard = Convert.ToInt32(string.IsNullOrEmpty(strIDBoard) ? "0" : strIDBoard);
+ string userName = Convert.ToString(GetRequestParm(context, "userName"));
+ string text = Convert.ToString(GetRequestParm(context, "text"));
+
+ lock (_chatBoards)
+ {
+ MessageBoard messageBoard;
+ if (_chatBoards.ContainsKey(idBoard))
+ {
+ messageBoard = _chatBoards[idBoard];
+ }
+ else
+ {
+ messageBoard = new MessageBoard();
+ _chatBoards[idBoard] = messageBoard;
+ }
+ messageBoard.Message_Add(userName, text);
+ lock (_monitor) { Monitor.PulseAll(_monitor); }
+ }
+ }
+
+ private string GetRequestParm(HttpContext context, string parm)
+ {
+ foreach (string key in context.Request.Params.AllKeys)
+ {
+ if (string.IsNullOrEmpty(key) == false && key.EndsWith(parm))
+ {
+ return context.Request.Params[key];
+ }
+ }
+ return string.Empty;
+ }
+
+ private void ResponseObject(HttpContext context, object obj)
+ {
+ var jsonWritter = new JSONWriter(true);
+ context.Response.ContentType = "text/json";
+ context.Response.Write(jsonWritter.Write(obj));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scrummer/Scripts/10. Chat.js b/Scrummer/Scripts/10. Chat.js
new file mode 100644
index 0000000..038a627
--- /dev/null
+++ b/Scrummer/Scripts/10. Chat.js
@@ -0,0 +1,75 @@
+function RunChat(divContainer, idBoard, hidIDMessage) {
+ divContainer = GetElement(divContainer);
+ hidIDMessage = GetElement(hidIDMessage);
+
+ var CreateMessageDOM = function (message) {
+ var divMessageRow = document.createElement("DIV");
+ divMessageRow.className = "messageRow";
+
+ var divMessage = document.createElement("DIV");
+ divMessage.className = "message";
+ divMessageRow.appendChild(divMessage);
+
+ var divUser = document.createElement("DIV");
+ divUser.className = "user";
+ divUser.innerHTML = escapeHTML(message.UserName);
+ divMessage.appendChild(divUser);
+
+ var text = message.Text;
+
+ var divText = document.createElement("DIV");
+ divText.className = "text";
+ divText.innerHTML = escapeHTML(text);
+ divMessage.appendChild(divText);
+
+ return divMessageRow;
+ };
+
+ var RequestChatData = function () {
+ var requestUrl = "ChatHandler?idBoard=" + idBoard + "&idMessage=" + hidIDMessage.value;
+ var ReciveChatData = function (responseText) {
+
+ recvMsgs = JSON.parse(responseText);
+ if (recvMsgs) {
+ var idMessage = parseInt(hidIDMessage.value);
+ for (var i = 0, n = recvMsgs.length; i < n; i++) {
+ var msg = recvMsgs[i];
+ if (idMessage < msg.IDMessage) {
+ hidIDMessage.value = msg.IDMessage;
+ idMessage = msg.IDMessage;
+ var elemMessage = CreateMessageDOM(msg);
+ divContainer.appendChild(elemMessage);
+ }
+ }
+ divContainer.scrollTop = divContainer.scrollHeight;
+ }
+
+ // Reset pool
+ window.setTimeout(function () {
+ RequestChatData();
+ }, 20);
+ };
+ var ErrorChatData = function () {
+
+ // Retry
+ window.setTimeout(function () {
+ RequestChatData();
+ }, 5000);
+ };
+
+ // Pool data
+ SendRequest(requestUrl, ReciveChatData, ErrorChatData);
+ };
+ RequestChatData();
+}
+
+function SendChat(txtText, idBoard) {
+ txtText = GetElement(txtText);
+ var data = {
+ "text": txtText.value,
+ "idBoard": idBoard,
+ "userName": "VAR"
+ };
+ txtText.value = "";
+ SendData("ChatHandler", data, null, null);
+}
\ No newline at end of file
diff --git a/Scrummer/Scrummer.csproj b/Scrummer/Scrummer.csproj
index eab6730..26b1a1d 100644
--- a/Scrummer/Scrummer.csproj
+++ b/Scrummer/Scrummer.csproj
@@ -70,11 +70,15 @@
+
+
+
+
diff --git a/Scrummer/Styles/10. Chat.css b/Scrummer/Styles/10. Chat.css
new file mode 100644
index 0000000..004b1c0
--- /dev/null
+++ b/Scrummer/Styles/10. Chat.css
@@ -0,0 +1,4 @@
+.divChat {
+ overflow: auto;
+ height: 300px;
+}