demowebsocket.lpr 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. program demowebsocket;
  2. {$mode objfpc}
  3. {$modeswitch externalclass}
  4. uses
  5. browserconsole, browserapp, JS, Classes, SysUtils, Web, strutils;
  6. type
  7. TServerConfig = Class External name 'Object' (TJSObject)
  8. url : string;
  9. end;
  10. Var
  11. ServerConfig : TServerConfig; external name 'serverConfig';
  12. Type
  13. { TMyApplication }
  14. TMsgKind = (mkIncoming,mkOutgoing,mkSystem);
  15. TMyApplication = class(TBrowserApplication)
  16. btnSend: TJSHTMLButtonElement;
  17. EdtSender : TJSHTMLInputElement;
  18. EdtMessage : TJSHTMLInputElement;
  19. EdtRecipient : TJSHTMLInputElement;
  20. divMessages : TJSHTMLDivElement;
  21. btnConnect : TJSHTMLButtonElement;
  22. WS : TJSWebSocket;
  23. procedure doRun; override;
  24. private
  25. function AppendIcon(aParent: TJSHTMLElement; aName: String): TJSHTMLElement;
  26. function CreateMessageEl(aKind : TMsgKind): TJSHTMLElement;
  27. procedure DisplayClose;
  28. procedure DisplayOpen;
  29. function DoClosed(Event: TEventListenerEvent): boolean;
  30. procedure doConnect;
  31. function DoIncomingMessage(Event: TEventListenerEvent): boolean;
  32. procedure DisplayMessage(Sender, Msg: String; Incoming: Boolean=True);
  33. function DoOpen(Event: TEventListenerEvent): boolean;
  34. function DoReconnect(aEvent: TJSMouseEvent): boolean;
  35. function DoSendClick(aEvent: TJSMouseEvent): boolean;
  36. function DoToggleConnectClick(aEvent: TJSMouseEvent): boolean;
  37. procedure SendMessage(const aRecipient, aMessage: String);
  38. end;
  39. procedure TMyApplication.doRun;
  40. begin
  41. edtRecipient:=TJSHTMLInputElement(GetHTMLElement('edtRecipient'));
  42. edtSender:=TJSHTMLInputElement(GetHTMLElement('edtSender'));
  43. edtMessage:=TJSHTMLInputElement(GetHTMLElement('edtMessage'));
  44. btnConnect:=TJSHTMLButtonElement(GetHTMLElement('btnConnect'));
  45. btnConnect.onclick:=@DoToggleConnectClick;
  46. btnSend:=TJSHTMLButtonElement(GetHTMLElement('btnSend'));
  47. btnSend.onclick:=@DoSendClick;
  48. divMessages:=TJSHTMLDivElement(GetHTMLElement('messages'));
  49. DoConnect;
  50. Terminate;
  51. end;
  52. procedure TMyApplication.doConnect;
  53. Var
  54. URL,aHost : string;
  55. begin
  56. URL:='';
  57. if Assigned(ServerConfig) and isString(serverConfig.URL) then
  58. URL:=serverConfig.URL;
  59. if URL='' then
  60. begin
  61. aHost:=window.location.host;
  62. aHost:=ExtractWord(1,aHost,[':']);
  63. URL:='ws://'+aHost+':8080/';
  64. end;
  65. try
  66. WS:=TJSWebsocket.New(url);
  67. WS.onmessage:=@DoIncomingMessage;
  68. WS.onclose:=@DoClosed;
  69. WS.onopen:=@DoOpen;
  70. except
  71. on TJSError do
  72. Window.Alert('Could not connect to websocket server at '+URL);
  73. end;
  74. end;
  75. function TMyApplication.DoOpen(Event: TEventListenerEvent): boolean;
  76. begin
  77. btnSend.disabled:=False;
  78. DisplayOpen;
  79. btnConnect.InnerText:='Disconnect';
  80. end;
  81. function TMyApplication.DoClosed(Event: TEventListenerEvent): boolean;
  82. begin
  83. btnSend.disabled:=true;
  84. btnConnect.InnerText:='Connect';
  85. DisplayClose;
  86. end;
  87. function TMyApplication.DoIncomingMessage(Event: TEventListenerEvent): boolean;
  88. Var
  89. Msg: TJSMessageEvent absolute Event;
  90. JS : TJSObject;
  91. begin
  92. if isString(Msg.Data) then
  93. begin
  94. JS:=TJSJSON.parseObject(String(Msg.Data));
  95. DisplayMessage(String(JS['from']),String(JS['msg']),True)
  96. end
  97. else
  98. DisplayMessage('','<<unknown data arrived>>',True);
  99. end;
  100. function TMyApplication.CreateMessageEl(aKind : TMsgKind): TJSHTMLElement;
  101. Var
  102. ImgDiv,ImgEl,msgEl : TJSHTMLElement;
  103. begin
  104. MsgEl:=TJSHTMLElement(Document.createElement('DIV'));
  105. if aKind=mkSystem then
  106. begin
  107. MsgEl.className:='text-center my-2';
  108. end
  109. else
  110. begin
  111. msgEl.className:='d-flex align-items-center'+Ifthen(aKind=mkIncoming,'',' text-right justify-content-end');
  112. ImgDiv:=TJSHTMLElement(Document.createElement('DIV'));
  113. ImgDiv.ClassName:='text-left pr-1';
  114. ImgEl:=TJSHTMLElement(Document.createElement('IMG'));
  115. ImgEl['Src']:=IfThen(aKind=mkIncoming,'guest','you')+'.png';
  116. ImgDiv.AppendChild(ImgEl);
  117. if aKind=mkIncoming then
  118. msgEl.AppendChild(ImgDiv);
  119. end;
  120. Result:=TJSHTMLElement(Document.createElement(IfThen(aKind=mkSystem,'span','div')));
  121. if aKind=mkSystem then
  122. Result.className:='between'
  123. else
  124. Result.className:='pr-2'+IfThen(aKind=mkIncoming,' pl-1','');
  125. msgEl.AppendChild(Result);
  126. if aKind=mkOutgoing then
  127. msgEl.AppendChild(ImgDiv);
  128. divMessages.appendChild(msgEl);
  129. msgEl.scrollIntoView;
  130. end;
  131. function TMyApplication.AppendIcon(aParent: TJSHTMLElement; aName: String
  132. ): TJSHTMLElement;
  133. begin
  134. Result:=TJSHTMLElement(Document.createElement('i'));
  135. Result.className:='fas fa-'+aName+' mr-3';
  136. aParent.AppendChild(Result);
  137. end;
  138. procedure TMyApplication.DisplayClose;
  139. Var
  140. iEl,pEl : TJSHTMLElement;
  141. begin
  142. Pel:=CreateMessageEl(mkSystem);
  143. iEl:=AppendIcon(Pel,'plug');
  144. iEl.onclick:=@DoReconnect;
  145. pEl.AppendChild(Document.createTextNode('Connection closed, click icon to reconnect'));
  146. end;
  147. procedure TMyApplication.DisplayOpen;
  148. Var
  149. pEl : TJSHTMLElement;
  150. begin
  151. Pel:=CreateMessageEl(mkSystem);
  152. AppendIcon(Pel,'link');
  153. pEl.AppendChild(Document.createTextNode('Connection open, you can start messaging'));
  154. end;
  155. procedure TMyApplication.DisplayMessage(Sender,Msg: String; Incoming: Boolean = True);
  156. Const
  157. kinds : Array[Boolean] of TMsgKind = (mkOutgoing,mkIncoming);
  158. Var
  159. pEl,pEl2 : TJSHTMLElement;
  160. begin
  161. pEl:=CreateMessageEl(Kinds[Incoming]);
  162. if (Sender<>'') then
  163. begin
  164. pEl2:=TJSHTMLElement(Document.createElement('span'));
  165. pEl2.className:='name';
  166. pEl2.innerText:=Sender;
  167. pEl.appendChild(pEl2);
  168. end;
  169. pEl2:=TJSHTMLElement(Document.createElement('p'));
  170. PEL2.className:='msg';
  171. PEL2.AppendChild(Document.createTextNode(Msg));
  172. pEl.AppendChild(pEL2);
  173. end;
  174. function TMyApplication.DoReconnect(aEvent: TJSMouseEvent): boolean;
  175. begin
  176. DoConnect;
  177. end;
  178. procedure TMyApplication.SendMessage(const aRecipient,aMessage: String);
  179. Var
  180. JS : TJSObject;
  181. begin
  182. JS:=New(['from',EdtSender.Value,'msg',aMessage,'to',aRecipient]);
  183. WS.send(TJSJSON.Stringify(JS));
  184. DisplayMessage('you',aMessage,False);
  185. end;
  186. function TMyApplication.DoSendClick(aEvent: TJSMouseEvent): boolean;
  187. begin
  188. SendMessage(EDTRecipient.Value, EdtMessage.Value);
  189. end;
  190. function TMyApplication.DoToggleConnectClick(aEvent: TJSMouseEvent): boolean;
  191. begin
  192. if btnConnect.InnerText='Connect' then
  193. doConnect
  194. else
  195. begin
  196. WS.close;
  197. WS:=nil;
  198. end;
  199. end;
  200. var
  201. Application : TMyApplication;
  202. begin
  203. Application:=TMyApplication.Create(nil);
  204. Application.Initialize;
  205. Application.Run;
  206. end.