├── .gitignore ├── JavaScriptClient ├── Index.html ├── JavaScriptClient.csproj ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── jquery-3.4.1.intellisense.js │ ├── jquery-3.4.1.js │ ├── jquery-3.4.1.min.js │ ├── jquery-3.4.1.min.map │ ├── jquery-3.4.1.slim.js │ ├── jquery-3.4.1.slim.min.js │ ├── jquery-3.4.1.slim.min.map │ ├── jquery.signalR-2.4.1.js │ └── jquery.signalR-2.4.1.min.js ├── Web.Debug.config ├── Web.Release.config ├── Web.config └── packages.config ├── README.md ├── SignalRSamples.sln ├── WinFormsClient ├── App.config ├── FrmClient.Designer.cs ├── FrmClient.cs ├── FrmClient.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── WinFormsClient.csproj └── packages.config ├── WinFormsServer ├── App.config ├── ClientItem.cs ├── FrmServer.Designer.cs ├── FrmServer.cs ├── FrmServer.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── SimpleHub.cs ├── Startup.cs ├── WinFormsServer.csproj └── packages.config └── images ├── js_client2.png ├── winforms_client.png └── winforms_server.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | [Oo]utput/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | 144 | # TODO: Un-comment the next line if you do not want to checkin 145 | # your web deploy settings because they may include unencrypted 146 | # passwords 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # NuGet Packages 151 | *.nupkg 152 | # The packages folder can be ignored because of Package Restore 153 | **/packages/* 154 | # except build/, which is used as an MSBuild target. 155 | !**/packages/build/ 156 | # Uncomment if necessary however generally it will be regenerated when needed 157 | #!**/packages/repositories.config 158 | # NuGet v3's project.json files produces more ignoreable files 159 | *.nuget.props 160 | *.nuget.targets 161 | 162 | # Microsoft Azure Build Output 163 | csx/ 164 | *.build.csdef 165 | 166 | # Microsoft Azure Emulator 167 | ecf/ 168 | rcf/ 169 | 170 | # Microsoft Azure ApplicationInsights config file 171 | ApplicationInsights.config 172 | 173 | # Windows Store app package directory 174 | AppPackages/ 175 | BundleArtifacts/ 176 | 177 | # Visual Studio cache files 178 | # files ending in .cache can be ignored 179 | *.[Cc]ache 180 | # but keep track of directories ending in .cache 181 | !*.[Cc]ache/ 182 | 183 | # Others 184 | ClientBin/ 185 | [Ss]tyle[Cc]op.* 186 | ~$* 187 | *~ 188 | *.dbmdl 189 | *.dbproj.schemaview 190 | *.pfx 191 | *.publishsettings 192 | node_modules/ 193 | orleans.codegen.cs 194 | 195 | # RIA/Silverlight projects 196 | Generated_Code/ 197 | 198 | # Backup & report files from converting an old project file 199 | # to a newer Visual Studio version. Backup files are not needed, 200 | # because we have git ;-) 201 | _UpgradeReport_Files/ 202 | Backup*/ 203 | UpgradeLog*.XML 204 | UpgradeLog*.htm 205 | 206 | # SQL Server files 207 | *.mdf 208 | *.ldf 209 | 210 | # Business Intelligence projects 211 | *.rdl.data 212 | *.bim.layout 213 | *.bim_*.settings 214 | 215 | # Microsoft Fakes 216 | FakesAssemblies/ 217 | 218 | # GhostDoc plugin setting file 219 | *.GhostDoc.xml 220 | 221 | # Node.js Tools for Visual Studio 222 | .ntvs_analysis.dat 223 | 224 | # Visual Studio 6 build log 225 | *.plg 226 | 227 | # Visual Studio 6 workspace options file 228 | *.opt 229 | 230 | # Visual Studio LightSwitch build output 231 | **/*.HTMLClient/GeneratedArtifacts 232 | **/*.DesktopClient/GeneratedArtifacts 233 | **/*.DesktopClient/ModelManifest.xml 234 | **/*.Server/GeneratedArtifacts 235 | **/*.Server/ModelManifest.xml 236 | _Pvt_Extensions 237 | 238 | # LightSwitch generated files 239 | GeneratedArtifacts/ 240 | ModelManifest.xml 241 | 242 | # Paket dependency manager 243 | .paket/paket.exe 244 | 245 | # FAKE - F# Make 246 | .fake/ -------------------------------------------------------------------------------- /JavaScriptClient/Index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | SignalR JavaScript Client Sample 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | Connection 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 |
Url
User name
    32 |
35 |
36 |
37 |
38 |
39 | Messaging 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
Message
 
50 |
51 |
52 |
53 |
54 | Group Membership 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 64 | 65 |
Group name
    63 |
66 |
67 |
68 |
69 |
70 | Log 71 | 72 |
73 |
74 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /JavaScriptClient/JavaScriptClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | 2.0 10 | {2112EA45-525D-453C-961B-2BA91F9EFC3F} 11 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 12 | Library 13 | Properties 14 | JavaScriptClient 15 | JavaScriptClient 16 | v4.5.2 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | true 29 | full 30 | false 31 | bin\ 32 | DEBUG;TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | pdbonly 39 | true 40 | bin\ 41 | TRACE 42 | prompt 43 | 4 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Web.config 89 | 90 | 91 | Web.config 92 | 93 | 94 | 95 | 10.0 96 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | True 106 | True 107 | 59054 108 | / 109 | http://localhost:59054/ 110 | False 111 | False 112 | 113 | 114 | False 115 | 116 | 117 | 118 | 119 | 120 | 121 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 122 | 123 | 124 | 125 | 132 | -------------------------------------------------------------------------------- /JavaScriptClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("JavaScriptClient")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("JavaScriptClient")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2112ea45-525d-453c-961b-2ba91f9efc3f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /JavaScriptClient/Scripts/jquery-3.4.1.slim.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.4.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(g,e){"use strict";var t=[],v=g.document,r=Object.getPrototypeOf,s=t.slice,y=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,m=n.hasOwnProperty,a=m.toString,l=a.call(Object),b={},x=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},w=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function C(e,t,n){var r,i,o=(n=n||v).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function T(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector",E=function(e,t){return new E.fn.init(e,t)},d=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function p(e){var t=!!e&&"length"in e&&e.length,n=T(e);return!x(e)&&!w(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+R+")"+R+"*"),U=new RegExp(R+"|>"),V=new RegExp(W),X=new RegExp("^"+B+"$"),Q={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),TAG:new RegExp("^("+B+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,G=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+R+"?|("+R+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){C()},ae=xe(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{O.apply(t=P.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){q.apply(e,P.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&((e?e.ownerDocument||e:m)!==T&&C(e),e=e||T,E)){if(11!==d&&(u=Z.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return O.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&p.getElementsByClassName&&e.getElementsByClassName)return O.apply(n,e.getElementsByClassName(i)),n}if(p.qsa&&!S[t+" "]&&(!v||!v.test(t))&&(1!==d||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===d&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=N),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+be(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return O.apply(n,f.querySelectorAll(c)),n}catch(e){S(t,!0)}finally{s===N&&e.removeAttribute("id")}}}return g(t.replace(F,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[N]=!0,e}function ce(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)x.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in p=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},C=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==T&&9===r.nodeType&&r.documentElement&&(a=(T=r).documentElement,E=!i(T),m!==T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),p.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),p.getElementsByTagName=ce(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),p.getElementsByClassName=J.test(T.getElementsByClassName),p.getById=ce(function(e){return a.appendChild(e).id=N,!T.getElementsByName||!T.getElementsByName(N).length}),p.getById?(x.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=p.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):p.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},x.find.CLASS=p.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(p.qsa=J.test(T.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+R+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+N+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+N+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+R+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(p.matchesSelector=J.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){p.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=J.test(a.compareDocumentPosition),y=t||J.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument===m&&y(m,e)?-1:t===T||t.ownerDocument===m&&y(m,t)?1:u?H(u,e)-H(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===T?-1:t===T?1:i?-1:o?1:u?H(u,e)-H(u,t):0;if(i===o)return de(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?de(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),T},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==T&&C(e),p.matchesSelector&&E&&!S[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){S(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&V.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=d[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&d(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function L(e,n,r){return x(n)?E.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?E.grep(e,function(e){return e===n!==r}):"string"!=typeof n?E.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(E.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof E?t[0]:t,E.merge(this,E.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:v,!0)),D.test(r[1])&&E.isPlainObject(t))for(r in t)x(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=v.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):x(e)?void 0!==n.ready?n.ready(e):e(E):E.makeArray(e,this)}).prototype=E.fn,j=E(v);var O=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};function H(e,t){while((e=e[t])&&1!==e.nodeType);return e}E.fn.extend({has:function(e){var t=E(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i,he={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ge(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?E.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;nx",b.noCloneChecked=!!ye.cloneNode(!0).lastChild.defaultValue;var we=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function Ne(){return!1}function Ae(e,t){return e===function(){try{return v.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ne;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return E().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=E.guid++)),e.each(function(){E.event.add(this,t,i,r,n)})}function Se(e,i,o){o?(G.set(e,i,!1),E.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=G.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(E.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),G.set(this,i,r),t=o(this,i),this[i](),r!==(n=G.get(this,i))||t?G.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(G.set(this,i,{value:E.event.trigger(E.extend(r[0],E.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===G.get(e,i)&&E.event.add(e,i,Ee)}E.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,d,p,h,g,v=G.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&E.find.matchesSelector(ie,i),n.guid||(n.guid=E.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof E&&E.event.triggered!==e.type?E.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(I)||[""]).length;while(l--)p=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),p&&(f=E.event.special[p]||{},p=(i?f.delegateType:f.bindType)||p,f=E.event.special[p]||{},c=E.extend({type:p,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&E.expr.match.needsContext.test(i),namespace:h.join(".")},o),(d=u[p])||((d=u[p]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(p,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),E.event.global[p]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,d,p,h,g,v=G.hasData(e)&&G.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(I)||[""]).length;while(l--)if(p=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),p){f=E.event.special[p]||{},d=u[p=(r?f.delegateType:f.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;while(o--)c=d[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));a&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||E.removeEvent(e,p,v.handle),delete u[p])}else for(p in u)E.event.remove(e,p+t[l],n,r,!0);E.isEmptyObject(u)&&G.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=E.event.fix(e),u=new Array(arguments.length),l=(G.get(this,"events")||{})[s.type]||[],c=E.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,Le=/\s*$/g;function Oe(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&E(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Ie(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(G.hasData(e)&&(o=G.access(e),a=G.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(b.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||E.isXMLDoc(e)))for(a=ge(c),r=0,i=(o=ge(e)).length;r
",2===pt.childNodes.length),E.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(b.createHTMLDocument?((r=(t=v.implementation.createHTMLDocument("")).createElement("base")).href=v.location.href,t.head.appendChild(r)):t=v),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&E(o).remove(),E.merge([],i.childNodes)));var r,i,o},E.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=E.css(e,"position"),c=E(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=E.css(e,"top"),u=E.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),x(t)&&(t=t.call(e,n,E.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},E.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){E.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===E.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===E.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=E(e).offset()).top+=E.css(e,"borderTopWidth",!0),i.left+=E.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-E.css(r,"marginTop",!0),left:t.left-i.left-E.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===E.css(e,"position"))e=e.offsetParent;return e||ie})}}),E.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;E.fn[t]=function(e){return z(this,function(e,t,n){var r;if(w(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),E.each(["top","left"],function(e,n){E.cssHooks[n]=ze(b.pixelPosition,function(e,t){if(t)return t=Fe(e,n),Me.test(t)?E(e).position()[n]+"px":t})}),E.each({Height:"height",Width:"width"},function(a,s){E.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){E.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return z(this,function(e,t,n){var r;return w(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?E.css(e,t,i):E.style(e,t,n,i)},s,n?e:void 0,n)}})}),E.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){E.fn[n]=function(e,t){return 0=0;u--)f=t[u],n.type(f)==="string"&&r.transports[f]||(i.log("Invalid transport: "+f+", removing it from the transports list."),t.splice(u,1));t.length===0&&(i.log("No transports remain within the specified transport array."),t=null)}else if(r.transports[t]||t==="auto"){if(t==="auto"&&r._.ieVersion<=8)return["longPolling"]}else i.log("Invalid transport: "+t.toString()+"."),t=null;return t}function b(n){return n==="http:"?80:n==="https:"?443:void 0}function a(n,t){return t.match(/:\d+$/)?t:t+":"+b(n)}function k(t,i){var u=this,r=[];u.tryBuffer=function(i){return t.state===n.signalR.connectionState.connecting?(r.push(i),!0):!1};u.drain=function(){if(t.state===n.signalR.connectionState.connected)while(r.length>0)i(r.shift())};u.clear=function(){r=[]}}var f={nojQuery:"jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file.",noTransportOnInit:"No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.",errorOnNegotiate:"Error during negotiation request.",stoppedWhileLoading:"The connection was stopped during page load.",stoppedWhileNegotiating:"The connection was stopped during the negotiate request.",errorParsingNegotiateResponse:"Error parsing negotiate response.",errorRedirectionExceedsLimit:"Negotiate redirection limit exceeded.",errorDuringStartRequest:"Error during start request. Stopping the connection.",errorFromServer:"Error message received from the server: '{0}'.",stoppedDuringStartRequest:"The connection was stopped during the start request.",errorParsingStartResponse:"Error parsing start response: '{0}'. Stopping the connection.",invalidStartResponse:"Invalid start response: '{0}'. Stopping the connection.",protocolIncompatible:"You are using a version of the client that isn't compatible with the server. Client version {0}, server version {1}.",aspnetCoreSignalrServer:"Detected a connection attempt to an ASP.NET Core SignalR Server. This client only supports connecting to an ASP.NET SignalR Server. See https://aka.ms/signalr-core-differences for details.",sendFailed:"Send failed.",parseFailed:"Failed at parsing response: {0}",longPollFailed:"Long polling request failed.",eventSourceFailedToConnect:"EventSource failed to connect.",eventSourceError:"Error raised by EventSource",webSocketClosed:"WebSocket closed.",pingServerFailedInvalidResponse:"Invalid ping response when pinging server: '{0}'.",pingServerFailed:"Failed to ping server.",pingServerFailedStatusCode:"Failed to ping server. Server responded with status code {0}, stopping the connection.",pingServerFailedParse:"Failed to parse ping server response, stopping the connection.",noConnectionTransport:"Connection is in an invalid state, there is no transport active.",webSocketsInvalidState:"The Web Socket transport is in an invalid state, transitioning into reconnecting.",reconnectTimeout:"Couldn't reconnect within the configured timeout of {0} ms, disconnecting.",reconnectWindowTimeout:"The client has been inactive since {0} and it has exceeded the inactivity timeout of {1} ms. Stopping the connection.",jsonpNotSupportedWithAccessToken:"The JSONP protocol does not support connections that require a Bearer token to connect, such as the Azure SignalR Service."};if(typeof n!="function")throw new Error(f.nojQuery);var r,h,o=t.document.readyState==="complete",e=n(t),c="__Negotiate Aborted__",u={onStart:"onStart",onStarting:"onStarting",onReceived:"onReceived",onError:"onError",onConnectionSlow:"onConnectionSlow",onReconnecting:"onReconnecting",onReconnect:"onReconnect",onStateChanged:"onStateChanged",onDisconnect:"onDisconnect"},v=function(n,i){if(i!==!1){var r;typeof t.console!="undefined"&&(r="["+(new Date).toTimeString()+"] SignalR: "+n,t.console.debug?t.console.debug(r):t.console.log&&t.console.log(r))}},s=function(t,i,r){return i===t.state?(t.state=r,n(t).triggerHandler(u.onStateChanged,[{oldState:i,newState:r}]),!0):!1},y=function(n){return n.state===r.connectionState.disconnected},l=function(n){return n._.keepAliveData.activated&&n.transport.supportsKeepAlive(n)},p=function(i){var f,e;i._.configuredStopReconnectingTimeout||(e=function(t){var i=r._.format(r.resources.reconnectTimeout,t.disconnectTimeout);t.log(i);n(t).triggerHandler(u.onError,[r._.error(i,"TimeoutException")]);t.stop(!1,!1)},i.reconnecting(function(){var n=this;n.state===r.connectionState.reconnecting&&(f=t.setTimeout(function(){e(n)},n.disconnectTimeout))}),i.stateChanged(function(n){n.oldState===r.connectionState.reconnecting&&t.clearTimeout(f)}),i._.configuredStopReconnectingTimeout=!0)};if(r=function(n,t,i){return new r.fn.init(n,t,i)},r._={defaultContentType:"application/x-www-form-urlencoded; charset=UTF-8",ieVersion:function(){var i,n;return t.navigator.appName==="Microsoft Internet Explorer"&&(n=/MSIE ([0-9]+\.[0-9]+)/.exec(t.navigator.userAgent),n&&(i=t.parseFloat(n[1]))),i}(),error:function(n,t,i){var r=new Error(n);return r.source=t,typeof i!="undefined"&&(r.context=i),r},transportError:function(n,t,r,u){var f=this.error(n,r,u);return f.transport=t?t.name:i,f},format:function(){for(var t=arguments[0],n=0;n<\/script>.");}},typeof e.on=="function")e.on("load",function(){o=!0});else e.load(function(){o=!0});r.fn=r.prototype={init:function(t,i,r){var f=n(this);this.url=t;this.qs=i;this.lastError=null;this._={keepAliveData:{},connectingMessageBuffer:new k(this,function(n){f.triggerHandler(u.onReceived,[n])}),lastMessageAt:(new Date).getTime(),lastActiveAt:(new Date).getTime(),beatInterval:5e3,beatHandle:null,totalTransportConnectTimeout:0,redirectQs:null};typeof r=="boolean"&&(this.logging=r)},_parseResponse:function(n){var t=this;return n?typeof n=="string"?t.json.parse(n):n:n},_originalJson:t.JSON,json:t.JSON,isCrossDomain:function(i,r){var u;return(i=n.trim(i),r=r||t.location,i.indexOf("http")!==0)?!1:(u=t.document.createElement("a"),u.href=i,u.protocol+a(u.protocol,u.host)!==r.protocol+a(r.protocol,r.host))},ajaxDataType:"text",contentType:"application/json; charset=UTF-8",logging:!1,state:r.connectionState.disconnected,clientProtocol:"2.1",supportedProtocols:["1.5","2.0","2.1"],negotiateRedirectSupportedProtocols:["2.0","2.1"],reconnectDelay:2e3,transportConnectTimeout:0,disconnectTimeout:3e4,reconnectWindow:3e4,keepAliveWarnAt:2/3,start:function(i,h){var a=this,v={pingInterval:3e5,waitForPageLoad:!0,transport:"auto",jsonp:!1},g,y=a._deferral||n.Deferred(),b=t.document.createElement("a"),nt=function(i,u){i.url===u&&i.baseUrl||(i.url=u,b.href=i.url,b.protocol&&b.protocol!==":"?(i.protocol=b.protocol,i.host=b.host):(i.protocol=t.document.location.protocol,i.host=b.host||t.document.location.host),i.baseUrl=i.protocol+"//"+i.host,i.wsProtocol=i.protocol==="https:"?"wss://":"ws://",i.url.indexOf("//")===0&&(i.url=t.location.protocol+i.url,i.log("Protocol relative URL detected, normalizing it to '"+i.url+"'.")),i.isCrossDomain(i.url)&&(i.log("Auto detected cross domain url."),v.transport==="auto"&&(v.transport=["webSockets","serverSentEvents","longPolling"]),typeof i.withCredentials=="undefined"&&(i.withCredentials=!0),n.support.cors||(i.ajaxDataType="jsonp",i.log("Using jsonp because this browser doesn't support CORS.")),i.contentType=r._.defaultContentType))},d,k;if(a.lastError=null,a._deferral=y,!a.json)throw new Error("SignalR: No JSON parser found. Please ensure json2.js is referenced before the SignalR.js file if you need to support clients without native JSON parsing support, e.g. IE<8.");if(n.type(i)==="function"?h=i:n.type(i)==="object"&&(n.extend(v,i),n.type(v.callback)==="function"&&(h=v.callback)),v.transport=w(v.transport,a),!v.transport)throw new Error("SignalR: Invalid transport(s) specified, aborting start.");return(a._.config=v,!o&&v.waitForPageLoad===!0)?(a._.deferredStartHandler=function(){a.start(i,h)},e.bind("load",a._.deferredStartHandler),y.promise()):a.state===r.connectionState.connecting?y.promise():s(a,r.connectionState.disconnected,r.connectionState.connecting)===!1?(y.resolve(a),y.promise()):(p(a),v.transport==="auto"&&v.jsonp===!0&&(v.transport="longPolling"),a.withCredentials=v.withCredentials,a._originalUrl=a.url,a.ajaxDataType=v.jsonp?"jsonp":"text",nt(a,a.url),n(a).bind(u.onStart,function(){n.type(h)==="function"&&h.call(a);y.resolve(a)}),a._.initHandler=r.transports._logic.initHandler(a),g=function(i,o){var c=r._.error(f.noTransportOnInit);if(o=o||0,o>=i.length){o===0?a.log("No transports supported by the server were selected."):o===1?a.log("No fallback transports were selected."):a.log("Fallback transports exhausted.");n(a).triggerHandler(u.onError,[c]);y.reject(c);a.stop();return}if(a.state!==r.connectionState.disconnected){var p=i[o],h=r.transports[p],v=function(){g(i,o+1)};a.transport=h;try{a._.initHandler.start(h,function(){var f=r._.firefoxMajorVersion(t.navigator.userAgent)>=11,i=!0;a.log("The start request succeeded. Transitioning to the connected state.");l(a)&&r.transports._logic.monitorKeepAlive(a);r.transports._logic.startHeartbeat(a);r._.configurePingInterval(a);s(a,r.connectionState.connecting,r.connectionState.connected)||a.log("WARNING! The connection was not in the connecting state.");a._.connectingMessageBuffer.drain();n(a).triggerHandler(u.onStart);e.bind("unload",function(){a.log("Window unloading, stopping the connection.");a.stop(i)});f&&e.bind("beforeunload",function(){t.setTimeout(function(){a.stop(i)},0)})},v)}catch(w){a.log(h.name+" transport threw '"+w.message+"' when attempting to start.");v()}}},d=a.url+"/negotiate",k=function(t,i){var e=r._.error(f.errorOnNegotiate,t,i._.negotiateRequest);n(i).triggerHandler(u.onError,e);y.reject(e);i.stop()},n(a).triggerHandler(u.onStarting),d=r.transports._logic.prepareQueryString(a,d),a.log("Negotiating with '"+d+"'."),a._.negotiateRequest=function(){var t,h=0,w=100,i,e,o=[],s=[],l=function(n,t){var u=r.transports._logic.prepareQueryString(n,n.url+"/negotiate"),i;return n.log("Negotiating with '"+u+"'."),i={url:u,error:function(t,i){i!==c?k(t,n):y.reject(r._.error(f.stoppedWhileNegotiating,null,n._.negotiateRequest))},success:t},n.accessToken&&(i.headers={Authorization:"Bearer "+n.accessToken}),r.transports._logic.ajax(n,i)},p=function(c){try{t=a._parseResponse(c)}catch(d){k(r._.error(f.errorParsingNegotiateResponse,d),a);return}if(t.availableTransports){e=r._.error(f.aspnetCoreSignalrServer);n(a).triggerHandler(u.onError,[e]);y.reject(e);return}if(!t.ProtocolVersion||a.supportedProtocols.indexOf(t.ProtocolVersion)===-1){e=r._.error(r._.format(f.protocolIncompatible,a.clientProtocol,t.ProtocolVersion));n(a).triggerHandler(u.onError,[e]);y.reject(e);return}if(a.negotiateRedirectSupportedProtocols.indexOf(t.ProtocolVersion)!==-1){if(t.Error){e=r._.error(r._.format(f.errorFromServer,t.Error));n(a).triggerHandler(u.onError,[e]);y.reject(e);return}if(t.RedirectUrl){if(h===w){k(r._.error(f.errorRedirectionExceedsLimit),a);return}v.transport==="auto"&&(v.transport=["webSockets","serverSentEvents","longPolling"]);a.log("Received redirect to: "+t.RedirectUrl);a.accessToken=t.AccessToken;var b=t.RedirectUrl.split("?",2);if(nt(a,b[0]),a._.redirectQs=b.length===2?b[1]:null,a.ajaxDataType==="jsonp"&&a.accessToken){k(r._.error(f.jsonpNotSupportedWithAccessToken),a);return}h++;l(a,p);return}}i=a._.keepAliveData;a.appRelativeUrl=t.Url;a.id=t.ConnectionId;a.token=t.ConnectionToken;a.webSocketServerUrl=t.WebSocketServerUrl;a._.pollTimeout=t.ConnectionTimeout*1e3+1e4;a.disconnectTimeout=t.DisconnectTimeout*1e3;a._.totalTransportConnectTimeout=a.transportConnectTimeout+t.TransportConnectTimeout*1e3;t.KeepAliveTimeout?(i.activated=!0,i.timeout=t.KeepAliveTimeout*1e3,i.timeoutWarning=i.timeout*a.keepAliveWarnAt,a._.beatInterval=(i.timeout-i.timeoutWarning)/3):i.activated=!1;a.reconnectWindow=a.disconnectTimeout+(i.timeout||0);n.each(r.transports,function(n){if(n.indexOf("_")===0||n==="webSockets"&&!t.TryWebSockets)return!0;s.push(n)});n.isArray(v.transport)?n.each(v.transport,function(t,i){n.inArray(i,s)>=0&&o.push(i)}):v.transport==="auto"?o=s:n.inArray(v.transport,s)>=0&&o.push(v.transport);g(o)};return l(a,p)}(),y.promise())},starting:function(t){var i=this;return n(i).bind(u.onStarting,function(){t.call(i)}),i},send:function(n){var t=this;if(t.state===r.connectionState.disconnected)throw new Error("SignalR: Connection must be started before data can be sent. Call .start() before .send()");if(t.state===r.connectionState.connecting)throw new Error("SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started.");return t.transport.send(t,n),t},received:function(t){var i=this;return n(i).bind(u.onReceived,function(n,r){t.call(i,r)}),i},stateChanged:function(t){var i=this;return n(i).bind(u.onStateChanged,function(n,r){t.call(i,r)}),i},error:function(t){var i=this;return n(i).bind(u.onError,function(n,r,u){i.lastError=r;t.call(i,r,u)}),i},disconnected:function(t){var i=this;return n(i).bind(u.onDisconnect,function(){t.call(i)}),i},connectionSlow:function(t){var i=this;return n(i).bind(u.onConnectionSlow,function(){t.call(i)}),i},reconnecting:function(t){var i=this;return n(i).bind(u.onReconnecting,function(){t.call(i)}),i},reconnected:function(t){var i=this;return n(i).bind(u.onReconnect,function(){t.call(i)}),i},stop:function(i,h){var a=this,v=a._deferral;if(a._.deferredStartHandler&&e.unbind("load",a._.deferredStartHandler),delete a._.config,delete a._.deferredStartHandler,!o&&(!a._.config||a._.config.waitForPageLoad===!0)){a.log("Stopping connection prior to negotiate.");v&&v.reject(r._.error(f.stoppedWhileLoading));return}if(a.state!==r.connectionState.disconnected)return a.log("Stopping connection."),t.clearTimeout(a._.beatHandle),t.clearInterval(a._.pingIntervalId),a.transport&&(a.transport.stop(a),h!==!1&&a.transport.abort(a,i),l(a)&&r.transports._logic.stopMonitoringKeepAlive(a),a.transport=null),a._.negotiateRequest&&(a._.negotiateRequest.abort(c),delete a._.negotiateRequest),a._.initHandler&&a._.initHandler.stop(),delete a._deferral,delete a.messageId,delete a.groupsToken,delete a.id,delete a._.pingIntervalId,delete a._.lastMessageAt,delete a._.lastActiveAt,a._.connectingMessageBuffer.clear(),n(a).unbind(u.onStart),delete a.accessToken,delete a.protocol,delete a.host,delete a.baseUrl,delete a.wsProtocol,delete a.contentType,a.url=a._originalUrl,a._.redirectQs=null,s(a,a.state,r.connectionState.disconnected),n(a).triggerHandler(u.onDisconnect),a},log:function(n){v(n,this.logging)}};r.fn.init.prototype=r.fn;r.noConflict=function(){return n.connection===r&&(n.connection=h),r};n.connection&&(h=n.connection);n.connection=n.signalR=r})(window.jQuery,window),function(n,t,i){function s(n){n._.keepAliveData.monitoring&&l(n);u.markActive(n)&&(n._.beatHandle=t.setTimeout(function(){s(n)},n._.beatInterval))}function l(t){var i=t._.keepAliveData,u;t.state===r.connectionState.connected&&(u=(new Date).getTime()-t._.lastMessageAt,u>=i.timeout?(t.log("Keep alive timed out. Notifying transport that connection has been lost."),t.transport.lostConnection(t)):u>=i.timeoutWarning?i.userNotified||(t.log("Keep alive has been missed, connection may be dead/slow."),n(t).triggerHandler(f.onConnectionSlow),i.userNotified=!0):i.userNotified=!1)}function e(n,t){var i=n.url+t;return n.transport&&(i+="?transport="+n.transport.name),u.prepareQueryString(n,i)}function h(n){this.connection=n;this.startRequested=!1;this.startCompleted=!1;this.connectionStopped=!1}var r=n.signalR,f=n.signalR.events,c=n.signalR.changeState,o="__Start Aborted__",u;r.transports={};h.prototype={start:function(n,r,u){var f=this,e=f.connection,o=!1;if(f.startRequested||f.connectionStopped){e.log("WARNING! "+n.name+" transport cannot be started. Initialization ongoing or completed.");return}e.log(n.name+" transport starting.");n.start(e,function(){o||f.initReceived(n,r)},function(t){return o||(o=!0,f.transportFailed(n,t,u)),!f.startCompleted||f.connectionStopped});f.transportTimeoutHandle=t.setTimeout(function(){o||(o=!0,e.log(n.name+" transport timed out when trying to connect."),f.transportFailed(n,i,u))},e._.totalTransportConnectTimeout)},stop:function(){this.connectionStopped=!0;t.clearTimeout(this.transportTimeoutHandle);r.transports._logic.tryAbortStartRequest(this.connection)},initReceived:function(n,i){var u=this,f=u.connection;if(u.startRequested){f.log("WARNING! The client received multiple init messages.");return}u.connectionStopped||(u.startRequested=!0,t.clearTimeout(u.transportTimeoutHandle),f.log(n.name+" transport connected. Initiating start request."),r.transports._logic.ajaxStart(f,function(){u.startCompleted=!0;i()}))},transportFailed:function(i,u,e){var o=this.connection,h=o._deferral,s;this.connectionStopped||(t.clearTimeout(this.transportTimeoutHandle),this.startRequested?this.startCompleted||(s=r._.error(r.resources.errorDuringStartRequest,u),o.log(i.name+" transport failed during the start request. Stopping the connection."),n(o).triggerHandler(f.onError,[s]),h&&h.reject(s),o.stop()):(i.stop(o),o.log(i.name+" transport failed to connect. Attempting to fall back."),e()))}};u=r.transports._logic={ajax:function(t,i){return n.ajax(n.extend(!0,{},n.signalR.ajaxDefaults,{type:"GET",data:{},xhrFields:{withCredentials:t.withCredentials},contentType:t.contentType,dataType:t.ajaxDataType},i))},pingServer:function(t){var e,f,i=n.Deferred();return t.transport?(e=t.url+"/ping",e=u.addQs(e,t.qs),f=u.ajax(t,{url:e,headers:t.accessToken?{Authorization:"Bearer "+t.accessToken}:{},success:function(n){var u;try{u=t._parseResponse(n)}catch(e){i.reject(r._.transportError(r.resources.pingServerFailedParse,t.transport,e,f));t.stop();return}u.Response==="pong"?i.resolve():i.reject(r._.transportError(r._.format(r.resources.pingServerFailedInvalidResponse,n),t.transport,null,f))},error:function(n){n.status===401||n.status===403?(i.reject(r._.transportError(r._.format(r.resources.pingServerFailedStatusCode,n.status),t.transport,n,f)),t.stop()):i.reject(r._.transportError(r.resources.pingServerFailed,t.transport,n,f))}})):i.reject(r._.transportError(r.resources.noConnectionTransport,t.transport)),i.promise()},prepareQueryString:function(n,i){var r;return r=u.addQs(i,"clientProtocol="+n.clientProtocol),r=typeof n._.redirectQs=="string"?u.addQs(r,n._.redirectQs):u.addQs(r,n.qs),n.token&&(r+="&connectionToken="+t.encodeURIComponent(n.token)),n.data&&(r+="&connectionData="+t.encodeURIComponent(n.data)),r},addQs:function(t,i){var r=t.indexOf("?")!==-1?"&":"?",u;if(!i)return t;if(typeof i=="object")return t+r+n.param(i);if(typeof i=="string")return u=i.charAt(0),(u==="?"||u==="&")&&(r=""),t+r+i;throw new Error("Query string property must be either a string or object.");},getUrl:function(n,i,r,f,e){var h=i==="webSockets"?"":n.baseUrl,o=h+n.appRelativeUrl,s="transport="+i;return!e&&n.groupsToken&&(s+="&groupsToken="+t.encodeURIComponent(n.groupsToken)),r?(o+=f?"/poll":"/reconnect",!e&&n.messageId&&(s+="&messageId="+t.encodeURIComponent(n.messageId))):o+="/connect",o+="?"+s,o=u.prepareQueryString(n,o),n.transport&&n.accessToken&&(n.transport.name==="serverSentEvents"||n.transport.name==="webSockets")&&(o+="&access_token="+t.encodeURIComponent(n.accessToken)),e||(o+="&tid="+Math.floor(Math.random()*11)),o},maximizePersistentResponse:function(n){return{MessageId:n.C,Messages:n.M,Initialized:typeof n.S!="undefined"?!0:!1,ShouldReconnect:typeof n.T!="undefined"?!0:!1,LongPollDelay:n.L,GroupsToken:n.G,Error:n.E}},updateGroups:function(n,t){t&&(n.groupsToken=t)},stringifySend:function(n,t){return typeof t=="string"||typeof t=="undefined"||t===null?t:n.json.stringify(t)},ajaxSend:function(t,i){var h=u.stringifySend(t,i),c=e(t,"/send"),o,s=function(t,u){n(u).triggerHandler(f.onError,[r._.transportError(r.resources.sendFailed,u.transport,t,o),i])};return o=u.ajax(t,{url:c,type:t.ajaxDataType==="jsonp"?"GET":"POST",contentType:r._.defaultContentType,headers:t.accessToken?{Authorization:"Bearer "+t.accessToken}:{},data:{data:h},success:function(n){var i;if(n){try{i=t._parseResponse(n)}catch(r){s(r,t);t.stop();return}u.triggerReceived(t,i)}},error:function(n,i){i!=="abort"&&i!=="parsererror"&&s(n,t)}})},ajaxAbort:function(n,t){if(typeof n.transport!="undefined"){t=typeof t=="undefined"?!0:t;var i=e(n,"/abort");u.ajax(n,{url:i,async:t,timeout:1e3,type:"POST",headers:n.accessToken?{Authorization:"Bearer "+n.accessToken}:{},dataType:"text"});n.log("Fired ajax abort async = "+t+".")}},ajaxStart:function(t,i){var h=function(n){var i=t._deferral;i&&i.reject(n)},s=function(i){t.log("The start request failed. Stopping the connection.");n(t).triggerHandler(f.onError,[i]);h(i);t.stop()};t._.startRequest=u.ajax(t,{url:e(t,"/start"),headers:t.accessToken?{Authorization:"Bearer "+t.accessToken}:{},success:function(n,u,f){var e;try{e=t._parseResponse(n)}catch(o){s(r._.error(r._.format(r.resources.errorParsingStartResponse,n),o,f));return}e.Response==="started"?i():s(r._.error(r._.format(r.resources.invalidStartResponse,n),null,f))},error:function(n,i,u){i!==o?s(r._.error(r.resources.errorDuringStartRequest,u,n)):(t.log("The start request aborted because connection.stop() was called."),h(r._.error(r.resources.stoppedDuringStartRequest,null,n)))}})},tryAbortStartRequest:function(n){n._.startRequest&&(n._.startRequest.abort(o),delete n._.startRequest)},tryInitialize:function(n,t,i){t.Initialized&&i?i():t.Initialized&&n.log("WARNING! The client received an init message after reconnecting.")},triggerReceived:function(t,i){t._.connectingMessageBuffer.tryBuffer(i)||n(t).triggerHandler(f.onReceived,[i])},processMessages:function(t,i,f){var e;if(i&&typeof i.I!="undefined"){u.triggerReceived(t,i);return}if(u.markLastMessage(t),i){if(e=u.maximizePersistentResponse(i),e.Error){t.log("Received an error message from the server: "+i.E);n(t).triggerHandler(r.events.onError,[r._.error(i.E,"ServerError")]);t.stop(!1,!1);return}u.updateGroups(t,e.GroupsToken);e.MessageId&&(t.messageId=e.MessageId);e.Messages&&(n.each(e.Messages,function(n,i){u.triggerReceived(t,i)}),u.tryInitialize(t,e,f))}},monitorKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring?t.log("Tried to monitor keep alive but it's already being monitored."):(i.monitoring=!0,u.markLastMessage(t),t._.keepAliveData.reconnectKeepAliveUpdate=function(){u.markLastMessage(t)},n(t).bind(f.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t.log("Now monitoring keep alive with a warning timeout of "+i.timeoutWarning+", keep alive timeout of "+i.timeout+" and disconnecting timeout of "+t.disconnectTimeout))},stopMonitoringKeepAlive:function(t){var i=t._.keepAliveData;i.monitoring&&(i.monitoring=!1,n(t).unbind(f.onReconnect,t._.keepAliveData.reconnectKeepAliveUpdate),t._.keepAliveData={},t.log("Stopping the monitoring of the keep alive."))},startHeartbeat:function(n){n._.lastActiveAt=(new Date).getTime();s(n)},markLastMessage:function(n){n._.lastMessageAt=(new Date).getTime()},markActive:function(n){return u.verifyLastActive(n)?(n._.lastActiveAt=(new Date).getTime(),!0):!1},isConnectedOrReconnecting:function(n){return n.state===r.connectionState.connected||n.state===r.connectionState.reconnecting},ensureReconnectingState:function(t){return c(t,r.connectionState.connected,r.connectionState.reconnecting)===!0&&n(t).triggerHandler(f.onReconnecting),t.state===r.connectionState.reconnecting},clearReconnectTimeout:function(n){n&&n._.reconnectTimeout&&(t.clearTimeout(n._.reconnectTimeout),delete n._.reconnectTimeout)},verifyLastActive:function(t){if((new Date).getTime()-t._.lastActiveAt>=t.reconnectWindow){var i=r._.format(r.resources.reconnectWindowTimeout,new Date(t._.lastActiveAt),t.reconnectWindow);return t.log(i),n(t).triggerHandler(f.onError,[r._.error(i,"TimeoutException")]),t.stop(!1,!1),!1}return!0},reconnect:function(n,i){var f=r.transports[i];if(u.isConnectedOrReconnecting(n)&&!n._.reconnectTimeout){if(!u.verifyLastActive(n))return;n._.reconnectTimeout=t.setTimeout(function(){u.verifyLastActive(n)&&(f.stop(n),u.ensureReconnectingState(n)&&(n.log(i+" reconnecting."),f.start(n)))},n.reconnectDelay)}},handleParseFailure:function(t,i,u,e,o){var s=r._.transportError(r._.format(r.resources.parseFailed,i),t.transport,u,o);e&&e(s)?t.log("Failed to parse server response while attempting to connect."):(n(t).triggerHandler(f.onError,[s]),t.stop())},initHandler:function(n){return new h(n)},foreverFrame:{count:0,connections:{}}}}(window.jQuery,window),function(n,t){var i=n.signalR,u=n.signalR.events,f=n.signalR.changeState,r=i.transports._logic;i.transports.webSockets={name:"webSockets",supportsKeepAlive:function(){return!0},send:function(t,f){var e=r.stringifySend(t,f);try{t.socket.send(e)}catch(o){n(t).triggerHandler(u.onError,[i._.transportError(i.resources.webSocketsInvalidState,t.transport,o,t.socket),f])}},start:function(e,o,s){var h,c=!1,l=this,a=!o,v=n(e);if(!t.WebSocket){s();return}e.socket||(h=e.webSocketServerUrl?e.webSocketServerUrl:e.wsProtocol+e.host,h+=r.getUrl(e,this.name,a),e.log("Connecting to websocket endpoint '"+h+"'."),e.socket=new t.WebSocket(h),e.socket.onopen=function(){c=!0;e.log("Websocket opened.");r.clearReconnectTimeout(e);f(e,i.connectionState.reconnecting,i.connectionState.connected)===!0&&v.triggerHandler(u.onReconnect)},e.socket.onclose=function(t){var r;this===e.socket&&(c&&typeof t.wasClean!="undefined"&&t.wasClean===!1?(r=i._.transportError(i.resources.webSocketClosed,e.transport,t),e.log("Unclean disconnect from websocket: "+(t.reason||"[no reason given]."))):e.log("Websocket closed."),s&&s(r)||(r&&n(e).triggerHandler(u.onError,[r]),l.reconnect(e)))},e.socket.onmessage=function(n){var t;try{t=e._parseResponse(n.data)}catch(i){r.handleParseFailure(e,n.data,i,s,n);return}t&&r.processMessages(e,t,o)})},reconnect:function(n){r.reconnect(n,this.name)},lostConnection:function(n){this.reconnect(n)},stop:function(n){r.clearReconnectTimeout(n);n.socket&&(n.log("Closing the Websocket."),n.socket.close(),n.socket=null)},abort:function(n,t){r.ajaxAbort(n,t)}}}(window.jQuery,window),function(n,t){var i=n.signalR,u=n.signalR.events,e=n.signalR.changeState,r=i.transports._logic,f=function(n){t.clearTimeout(n._.reconnectAttemptTimeoutHandle);delete n._.reconnectAttemptTimeoutHandle};i.transports.serverSentEvents={name:"serverSentEvents",supportsKeepAlive:function(){return!0},timeOut:3e3,start:function(o,s,h){var c=this,l=!1,a=n(o),v=!s,y;if(o.eventSource&&(o.log("The connection already has an event source. Stopping it."),o.stop()),!t.EventSource){h&&(o.log("This browser doesn't support SSE."),h());return}y=r.getUrl(o,this.name,v);try{o.log("Attempting to connect to SSE endpoint '"+y+"'.");o.eventSource=new t.EventSource(y,{withCredentials:o.withCredentials})}catch(p){o.log("EventSource failed trying to connect with error "+p.Message+".");h?h():(a.triggerHandler(u.onError,[i._.transportError(i.resources.eventSourceFailedToConnect,o.transport,p)]),v&&c.reconnect(o));return}v&&(o._.reconnectAttemptTimeoutHandle=t.setTimeout(function(){l===!1&&o.eventSource.readyState!==t.EventSource.OPEN&&c.reconnect(o)},c.timeOut));o.eventSource.addEventListener("open",function(){o.log("EventSource connected.");f(o);r.clearReconnectTimeout(o);l===!1&&(l=!0,e(o,i.connectionState.reconnecting,i.connectionState.connected)===!0&&a.triggerHandler(u.onReconnect))},!1);o.eventSource.addEventListener("message",function(n){var t;if(n.data!=="initialized"){try{t=o._parseResponse(n.data)}catch(i){r.handleParseFailure(o,n.data,i,h,n);return}r.processMessages(o,t,s)}},!1);o.eventSource.addEventListener("error",function(n){var r=i._.transportError(i.resources.eventSourceError,o.transport,n);this===o.eventSource&&(h&&h(r)||(o.log("EventSource readyState: "+o.eventSource.readyState+"."),n.eventPhase===t.EventSource.CLOSED?(o.log("EventSource reconnecting due to the server connection ending."),c.reconnect(o)):(o.log("EventSource error."),a.triggerHandler(u.onError,[r]))))},!1)},reconnect:function(n){r.reconnect(n,this.name)},lostConnection:function(n){this.reconnect(n)},send:function(n,t){r.ajaxSend(n,t)},stop:function(n){f(n);r.clearReconnectTimeout(n);n&&n.eventSource&&(n.log("EventSource calling close()."),n.eventSource.close(),n.eventSource=null,delete n.eventSource)},abort:function(n,t){r.ajaxAbort(n,t)}}}(window.jQuery,window),function(n,t){var r=n.signalR,e=n.signalR.events,o=n.signalR.changeState,i=r.transports._logic,u=function(){var n=t.document.createElement("iframe");return n.setAttribute("style","position:absolute;top:0;left:0;width:0;height:0;visibility:hidden;"),n},f=function(){var i=null,f=1e3,n=0;return{prevent:function(){r._.ieVersion<=8&&(n===0&&(i=t.setInterval(function(){var n=u();t.document.body.appendChild(n);t.document.body.removeChild(n);n=null},f)),n++)},cancel:function(){n===1&&t.clearInterval(i);n>0&&n--}}}();r.transports.foreverFrame={name:"foreverFrame",supportsKeepAlive:function(){return!0},iframeClearThreshold:50,start:function(n,r,e){if(n.accessToken){e&&(n.log("Forever Frame does not support connections that require a Bearer token to connect, such as the Azure SignalR Service."),e());return}var l=this,s=i.foreverFrame.count+=1,h,o=u(),c=function(){n.log("Forever frame iframe finished loading and is no longer receiving messages.");e&&e()||l.reconnect(n)};if(t.EventSource){e&&(n.log("Forever Frame is not supported by SignalR on browsers with SSE support."),e());return}o.setAttribute("data-signalr-connection-id",n.id);f.prevent();h=i.getUrl(n,this.name);h+="&frameId="+s;t.document.documentElement.appendChild(o);n.log("Binding to iframe's load event.");o.addEventListener?o.addEventListener("load",c,!1):o.attachEvent&&o.attachEvent("onload",c);o.src=h;i.foreverFrame.connections[s]=n;n.frame=o;n.frameId=s;r&&(n.onSuccess=function(){n.log("Iframe transport started.");r()})},reconnect:function(n){var r=this;i.isConnectedOrReconnecting(n)&&i.verifyLastActive(n)&&t.setTimeout(function(){if(i.verifyLastActive(n)&&n.frame&&i.ensureReconnectingState(n)){var u=n.frame,t=i.getUrl(n,r.name,!0)+"&frameId="+n.frameId;n.log("Updating iframe src to '"+t+"'.");u.src=t}},n.reconnectDelay)},lostConnection:function(n){this.reconnect(n)},send:function(n,t){i.ajaxSend(n,t)},receive:function(t,u){var f,e,o;if(t.json!==t._originalJson&&(u=t._originalJson.stringify(u)),o=t._parseResponse(u),i.processMessages(t,o,t.onSuccess),t.state===n.signalR.connectionState.connected&&(t.frameMessageCount=(t.frameMessageCount||0)+1,t.frameMessageCount>r.transports.foreverFrame.iframeClearThreshold&&(t.frameMessageCount=0,f=t.frame.contentWindow||t.frame.contentDocument,f&&f.document&&f.document.body)))for(e=f.document.body;e.firstChild;)e.removeChild(e.firstChild)},stop:function(n){var r=null;if(f.cancel(),n.frame){if(n.frame.stop)n.frame.stop();else try{r=n.frame.contentWindow||n.frame.contentDocument;r.document&&r.document.execCommand&&r.document.execCommand("Stop")}catch(u){n.log("Error occurred when stopping foreverFrame transport. Message = "+u.message+".")}n.frame.parentNode===t.document.documentElement&&t.document.documentElement.removeChild(n.frame);delete i.foreverFrame.connections[n.frameId];n.frame=null;n.frameId=null;delete n.frame;delete n.frameId;delete n.onSuccess;delete n.frameMessageCount;n.log("Stopping forever frame.")}},abort:function(n,t){i.ajaxAbort(n,t)},getConnection:function(n){return i.foreverFrame.connections[n]},started:function(t){o(t,r.connectionState.reconnecting,r.connectionState.connected)===!0&&n(t).triggerHandler(e.onReconnect)}}}(window.jQuery,window),function(n,t){var r=n.signalR,u=n.signalR.events,e=n.signalR.changeState,f=n.signalR.isDisconnecting,i=r.transports._logic;r.transports.longPolling={name:"longPolling",supportsKeepAlive:function(){return!1},reconnectDelay:3e3,start:function(o,s,h){var a=this,v=function(){v=n.noop;o.log("LongPolling connected.");s?s():o.log("WARNING! The client received an init message after reconnecting.")},y=function(n){return h(n)?(o.log("LongPolling failed to connect."),!0):!1},c=o._,l=0,p=function(i){t.clearTimeout(c.reconnectTimeoutId);c.reconnectTimeoutId=null;e(i,r.connectionState.reconnecting,r.connectionState.connected)===!0&&(i.log("Raising the reconnect event"),n(i).triggerHandler(u.onReconnect))},w=36e5;o.pollXhr&&(o.log("Polling xhr requests already exists, aborting."),o.stop());o.messageId=null;c.reconnectTimeoutId=null;c.pollTimeoutId=t.setTimeout(function(){(function e(s,h){var g=s.messageId,nt=g===null,k=!nt,tt=!h,d=i.getUrl(s,a.name,k,tt,!0),b={};(s.messageId&&(b.messageId=s.messageId),s.groupsToken&&(b.groupsToken=s.groupsToken),f(s)!==!0)&&(o.log("Opening long polling request to '"+d+"'."),s.pollXhr=i.ajax(o,{xhrFields:{onprogress:function(){i.markLastMessage(o)}},url:d,type:"POST",contentType:r._.defaultContentType,data:b,timeout:o._.pollTimeout,headers:o.accessToken?{Authorization:"Bearer "+o.accessToken}:{},success:function(r){var h,w=0,u,a;o.log("Long poll complete.");l=0;try{h=o._parseResponse(r)}catch(b){i.handleParseFailure(s,r,b,y,s.pollXhr);return}(c.reconnectTimeoutId!==null&&p(s),h&&(u=i.maximizePersistentResponse(h)),i.processMessages(s,h,v),u&&n.type(u.LongPollDelay)==="number"&&(w=u.LongPollDelay),f(s)!==!0)&&(a=u&&u.ShouldReconnect,!a||i.ensureReconnectingState(s))&&(w>0?c.pollTimeoutId=t.setTimeout(function(){e(s,a)},w):e(s,a))},error:function(f,h){var v=r._.transportError(r.resources.longPollFailed,o.transport,f,s.pollXhr);if(t.clearTimeout(c.reconnectTimeoutId),c.reconnectTimeoutId=null,h==="abort"){o.log("Aborted xhr request.");return}if(!y(v)){if(l++,o.state!==r.connectionState.reconnecting&&(o.log("An error occurred using longPolling. Status = "+h+". Response = "+f.responseText+"."),n(s).triggerHandler(u.onError,[v])),(o.state===r.connectionState.connected||o.state===r.connectionState.reconnecting)&&!i.verifyLastActive(o))return;if(!i.ensureReconnectingState(s))return;c.pollTimeoutId=t.setTimeout(function(){e(s,!0)},a.reconnectDelay)}}}),k&&h===!0&&(c.reconnectTimeoutId=t.setTimeout(function(){p(s)},Math.min(1e3*(Math.pow(2,l)-1),w))))})(o)},250)},lostConnection:function(n){n.pollXhr&&n.pollXhr.abort("lostConnection")},send:function(n,t){i.ajaxSend(n,t)},stop:function(n){t.clearTimeout(n._.pollTimeoutId);t.clearTimeout(n._.reconnectTimeoutId);delete n._.pollTimeoutId;delete n._.reconnectTimeoutId;n.pollXhr&&(n.pollXhr.abort(),n.pollXhr=null,delete n.pollXhr)},abort:function(n,t){i.ajaxAbort(n,t)}}}(window.jQuery,window),function(n){function r(n){return n+s}function c(n,t,i){for(var f=n.length,u=[],r=0;r=0}function i(n,t){return new i.fn.init(n,t)}function t(i,r){var u={qs:null,logging:!1,useDefaultPath:!0};return n.extend(u,r),(!i||u.useDefaultPath)&&(i=(i||"")+"/signalr"),new t.fn.init(i,u)}var o=0,s=".hubProxy",h=n.signalR;i.fn=i.prototype={init:function(n,t){this.state={};this.connection=n;this.hubName=t;this._={callbackMap:{}}},constructor:i,hasSubscriptions:function(){return u(this._.callbackMap)},on:function(t,i,u){var c=this,l=c._.callbackMap,v=!u&&e(i),f,h,s,a;for(u=u||i,u._signalRGuid||(u._signalRGuid=o++),t=t.toLowerCase(),f=l[t],f||(f=[],l[t]=f),s=0;s 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /JavaScriptClient/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /JavaScriptClient/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /JavaScriptClient/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Self Hosted SignalR Samples 2 | 3 | Self hosted sample applications for common SignalR usage scenarios. 4 | 5 | ## Demonstrated Scenarios 6 | - Creating and hosting a SignalR server and a hub 7 | - Connecting to a SignalR server and communicating with a hub on it 8 | - Managing clients on the server side 9 | - Calling methods of the hub from a client 10 | - Client joining to a group, leaving the group 11 | - Sending messages from client to server 12 | - Broadcasting a message from server to all the clients 13 | - Broadcasting a message from server to a group 14 | - Broadcasting a message from server to a a single client 15 | 16 | -------------------------------------------------------------------------------- /SignalRSamples.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.645 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormsServer", "WinFormsServer\WinFormsServer.csproj", "{5F754EF2-5AFD-4E4C-B554-A804568DEED2}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormsClient", "WinFormsClient\WinFormsClient.csproj", "{04389B63-DA2E-4A82-84DC-2B5D43E2363A}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JavaScriptClient", "JavaScriptClient\JavaScriptClient.csproj", "{2112EA45-525D-453C-961B-2BA91F9EFC3F}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {5F754EF2-5AFD-4E4C-B554-A804568DEED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {5F754EF2-5AFD-4E4C-B554-A804568DEED2}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {5F754EF2-5AFD-4E4C-B554-A804568DEED2}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {5F754EF2-5AFD-4E4C-B554-A804568DEED2}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {04389B63-DA2E-4A82-84DC-2B5D43E2363A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {04389B63-DA2E-4A82-84DC-2B5D43E2363A}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {04389B63-DA2E-4A82-84DC-2B5D43E2363A}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {04389B63-DA2E-4A82-84DC-2B5D43E2363A}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {2112EA45-525D-453C-961B-2BA91F9EFC3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {2112EA45-525D-453C-961B-2BA91F9EFC3F}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {2112EA45-525D-453C-961B-2BA91F9EFC3F}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {2112EA45-525D-453C-961B-2BA91F9EFC3F}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {EC7AEE83-921A-4203-A836-C03FCDBEC893} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /WinFormsClient/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /WinFormsClient/FrmClient.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace WinFormsClient 2 | { 3 | partial class FrmClient 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 32 | this.grpMessaging = new System.Windows.Forms.GroupBox(); 33 | this.groupBox3 = new System.Windows.Forms.GroupBox(); 34 | this.btnDisconnect = new System.Windows.Forms.Button(); 35 | this.txtUrl = new System.Windows.Forms.TextBox(); 36 | this.label1 = new System.Windows.Forms.Label(); 37 | this.btnConnect = new System.Windows.Forms.Button(); 38 | this.txtLog = new System.Windows.Forms.TextBox(); 39 | this.label2 = new System.Windows.Forms.Label(); 40 | this.txtMessage = new System.Windows.Forms.TextBox(); 41 | this.btnSend = new System.Windows.Forms.Button(); 42 | this.txtUserName = new System.Windows.Forms.TextBox(); 43 | this.label3 = new System.Windows.Forms.Label(); 44 | this.grpMembership = new System.Windows.Forms.GroupBox(); 45 | this.btnLeaveGroup = new System.Windows.Forms.Button(); 46 | this.btnJoinGroup = new System.Windows.Forms.Button(); 47 | this.txtGroupName = new System.Windows.Forms.TextBox(); 48 | this.label4 = new System.Windows.Forms.Label(); 49 | this.groupBox1.SuspendLayout(); 50 | this.grpMessaging.SuspendLayout(); 51 | this.groupBox3.SuspendLayout(); 52 | this.grpMembership.SuspendLayout(); 53 | this.SuspendLayout(); 54 | // 55 | // groupBox1 56 | // 57 | this.groupBox1.Controls.Add(this.txtUserName); 58 | this.groupBox1.Controls.Add(this.label3); 59 | this.groupBox1.Controls.Add(this.btnDisconnect); 60 | this.groupBox1.Controls.Add(this.txtUrl); 61 | this.groupBox1.Controls.Add(this.label1); 62 | this.groupBox1.Controls.Add(this.btnConnect); 63 | this.groupBox1.Location = new System.Drawing.Point(12, 12); 64 | this.groupBox1.Name = "groupBox1"; 65 | this.groupBox1.Size = new System.Drawing.Size(403, 107); 66 | this.groupBox1.TabIndex = 0; 67 | this.groupBox1.TabStop = false; 68 | this.groupBox1.Text = "Connection"; 69 | // 70 | // grpMessaging 71 | // 72 | this.grpMessaging.Controls.Add(this.btnSend); 73 | this.grpMessaging.Controls.Add(this.label2); 74 | this.grpMessaging.Controls.Add(this.txtMessage); 75 | this.grpMessaging.Enabled = false; 76 | this.grpMessaging.Location = new System.Drawing.Point(12, 125); 77 | this.grpMessaging.Name = "grpMessaging"; 78 | this.grpMessaging.Size = new System.Drawing.Size(403, 79); 79 | this.grpMessaging.TabIndex = 1; 80 | this.grpMessaging.TabStop = false; 81 | this.grpMessaging.Text = "Messaging"; 82 | // 83 | // groupBox3 84 | // 85 | this.groupBox3.Controls.Add(this.txtLog); 86 | this.groupBox3.Location = new System.Drawing.Point(12, 299); 87 | this.groupBox3.Name = "groupBox3"; 88 | this.groupBox3.Size = new System.Drawing.Size(403, 210); 89 | this.groupBox3.TabIndex = 2; 90 | this.groupBox3.TabStop = false; 91 | this.groupBox3.Text = "Log"; 92 | // 93 | // btnDisconnect 94 | // 95 | this.btnDisconnect.Enabled = false; 96 | this.btnDisconnect.Location = new System.Drawing.Point(213, 71); 97 | this.btnDisconnect.Name = "btnDisconnect"; 98 | this.btnDisconnect.Size = new System.Drawing.Size(108, 23); 99 | this.btnDisconnect.TabIndex = 7; 100 | this.btnDisconnect.Text = "Disconnect"; 101 | this.btnDisconnect.UseVisualStyleBackColor = true; 102 | this.btnDisconnect.Click += new System.EventHandler(this.btnDisconnect_Click); 103 | // 104 | // txtUrl 105 | // 106 | this.txtUrl.Location = new System.Drawing.Point(79, 19); 107 | this.txtUrl.Name = "txtUrl"; 108 | this.txtUrl.Size = new System.Drawing.Size(242, 20); 109 | this.txtUrl.TabIndex = 6; 110 | this.txtUrl.Text = "http://localhost:8080/signalr"; 111 | // 112 | // label1 113 | // 114 | this.label1.AutoSize = true; 115 | this.label1.Location = new System.Drawing.Point(8, 24); 116 | this.label1.Name = "label1"; 117 | this.label1.Size = new System.Drawing.Size(20, 13); 118 | this.label1.TabIndex = 5; 119 | this.label1.Text = "Url"; 120 | // 121 | // btnConnect 122 | // 123 | this.btnConnect.Location = new System.Drawing.Point(79, 71); 124 | this.btnConnect.Name = "btnConnect"; 125 | this.btnConnect.Size = new System.Drawing.Size(108, 23); 126 | this.btnConnect.TabIndex = 4; 127 | this.btnConnect.Text = "Connect"; 128 | this.btnConnect.UseVisualStyleBackColor = true; 129 | this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click); 130 | // 131 | // txtLog 132 | // 133 | this.txtLog.Location = new System.Drawing.Point(6, 19); 134 | this.txtLog.Multiline = true; 135 | this.txtLog.Name = "txtLog"; 136 | this.txtLog.ReadOnly = true; 137 | this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both; 138 | this.txtLog.Size = new System.Drawing.Size(391, 185); 139 | this.txtLog.TabIndex = 5; 140 | // 141 | // label2 142 | // 143 | this.label2.AutoSize = true; 144 | this.label2.Location = new System.Drawing.Point(9, 26); 145 | this.label2.Name = "label2"; 146 | this.label2.Size = new System.Drawing.Size(50, 13); 147 | this.label2.TabIndex = 8; 148 | this.label2.Text = "Message"; 149 | // 150 | // txtMessage 151 | // 152 | this.txtMessage.Location = new System.Drawing.Point(77, 19); 153 | this.txtMessage.Name = "txtMessage"; 154 | this.txtMessage.Size = new System.Drawing.Size(238, 20); 155 | this.txtMessage.TabIndex = 7; 156 | // 157 | // btnSend 158 | // 159 | this.btnSend.Location = new System.Drawing.Point(76, 45); 160 | this.btnSend.Name = "btnSend"; 161 | this.btnSend.Size = new System.Drawing.Size(239, 23); 162 | this.btnSend.TabIndex = 9; 163 | this.btnSend.Text = "Send"; 164 | this.btnSend.UseVisualStyleBackColor = true; 165 | this.btnSend.Click += new System.EventHandler(this.btnSend_Click); 166 | // 167 | // txtUserName 168 | // 169 | this.txtUserName.Location = new System.Drawing.Point(79, 45); 170 | this.txtUserName.Name = "txtUserName"; 171 | this.txtUserName.Size = new System.Drawing.Size(242, 20); 172 | this.txtUserName.TabIndex = 9; 173 | this.txtUserName.Text = "user"; 174 | // 175 | // label3 176 | // 177 | this.label3.AutoSize = true; 178 | this.label3.Location = new System.Drawing.Point(8, 50); 179 | this.label3.Name = "label3"; 180 | this.label3.Size = new System.Drawing.Size(58, 13); 181 | this.label3.TabIndex = 8; 182 | this.label3.Text = "User name"; 183 | // 184 | // grpMembership 185 | // 186 | this.grpMembership.Controls.Add(this.label4); 187 | this.grpMembership.Controls.Add(this.btnLeaveGroup); 188 | this.grpMembership.Controls.Add(this.btnJoinGroup); 189 | this.grpMembership.Controls.Add(this.txtGroupName); 190 | this.grpMembership.Enabled = false; 191 | this.grpMembership.Location = new System.Drawing.Point(12, 210); 192 | this.grpMembership.Name = "grpMembership"; 193 | this.grpMembership.Size = new System.Drawing.Size(403, 84); 194 | this.grpMembership.TabIndex = 3; 195 | this.grpMembership.TabStop = false; 196 | this.grpMembership.Text = "Group Membership"; 197 | // 198 | // btnLeaveGroup 199 | // 200 | this.btnLeaveGroup.Location = new System.Drawing.Point(212, 53); 201 | this.btnLeaveGroup.Name = "btnLeaveGroup"; 202 | this.btnLeaveGroup.Size = new System.Drawing.Size(102, 23); 203 | this.btnLeaveGroup.TabIndex = 10; 204 | this.btnLeaveGroup.Text = "Leave"; 205 | this.btnLeaveGroup.UseVisualStyleBackColor = true; 206 | this.btnLeaveGroup.Click += new System.EventHandler(this.btnLeaveGroup_Click); 207 | // 208 | // btnJoinGroup 209 | // 210 | this.btnJoinGroup.Location = new System.Drawing.Point(75, 53); 211 | this.btnJoinGroup.Name = "btnJoinGroup"; 212 | this.btnJoinGroup.Size = new System.Drawing.Size(111, 23); 213 | this.btnJoinGroup.TabIndex = 9; 214 | this.btnJoinGroup.Text = "Join"; 215 | this.btnJoinGroup.UseVisualStyleBackColor = true; 216 | this.btnJoinGroup.Click += new System.EventHandler(this.btnJoinGroup_Click); 217 | // 218 | // txtGroupName 219 | // 220 | this.txtGroupName.Location = new System.Drawing.Point(76, 27); 221 | this.txtGroupName.Name = "txtGroupName"; 222 | this.txtGroupName.Size = new System.Drawing.Size(238, 20); 223 | this.txtGroupName.TabIndex = 8; 224 | // 225 | // label4 226 | // 227 | this.label4.AutoSize = true; 228 | this.label4.Location = new System.Drawing.Point(9, 32); 229 | this.label4.Name = "label4"; 230 | this.label4.Size = new System.Drawing.Size(65, 13); 231 | this.label4.TabIndex = 11; 232 | this.label4.Text = "Group name"; 233 | // 234 | // FrmClient 235 | // 236 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 237 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 238 | this.ClientSize = new System.Drawing.Size(427, 519); 239 | this.Controls.Add(this.grpMembership); 240 | this.Controls.Add(this.groupBox3); 241 | this.Controls.Add(this.grpMessaging); 242 | this.Controls.Add(this.groupBox1); 243 | this.Name = "FrmClient"; 244 | this.Text = "Client"; 245 | this.groupBox1.ResumeLayout(false); 246 | this.groupBox1.PerformLayout(); 247 | this.grpMessaging.ResumeLayout(false); 248 | this.grpMessaging.PerformLayout(); 249 | this.groupBox3.ResumeLayout(false); 250 | this.groupBox3.PerformLayout(); 251 | this.grpMembership.ResumeLayout(false); 252 | this.grpMembership.PerformLayout(); 253 | this.ResumeLayout(false); 254 | 255 | } 256 | 257 | #endregion 258 | 259 | private System.Windows.Forms.GroupBox groupBox1; 260 | private System.Windows.Forms.GroupBox grpMessaging; 261 | private System.Windows.Forms.GroupBox groupBox3; 262 | private System.Windows.Forms.Button btnDisconnect; 263 | private System.Windows.Forms.TextBox txtUrl; 264 | private System.Windows.Forms.Label label1; 265 | private System.Windows.Forms.Button btnConnect; 266 | private System.Windows.Forms.TextBox txtLog; 267 | private System.Windows.Forms.Button btnSend; 268 | private System.Windows.Forms.Label label2; 269 | private System.Windows.Forms.TextBox txtMessage; 270 | private System.Windows.Forms.TextBox txtUserName; 271 | private System.Windows.Forms.Label label3; 272 | private System.Windows.Forms.GroupBox grpMembership; 273 | private System.Windows.Forms.Label label4; 274 | private System.Windows.Forms.Button btnLeaveGroup; 275 | private System.Windows.Forms.Button btnJoinGroup; 276 | private System.Windows.Forms.TextBox txtGroupName; 277 | } 278 | } 279 | 280 | -------------------------------------------------------------------------------- /WinFormsClient/FrmClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.SignalR.Client; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Data; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace WinFormsClient 13 | { 14 | public partial class FrmClient : Form 15 | { 16 | //Connection to a SignalR server 17 | HubConnection _signalRConnection; 18 | 19 | //Proxy object for a hub hosted on the SignalR server 20 | IHubProxy _hubProxy; 21 | 22 | public FrmClient() 23 | { 24 | InitializeComponent(); 25 | } 26 | 27 | private async void btnConnect_Click(object sender, EventArgs e) 28 | { 29 | await connectAsync(); 30 | } 31 | 32 | private void btnDisconnect_Click(object sender, EventArgs e) 33 | { 34 | //Close the server connection if exists 35 | if(_signalRConnection != null) 36 | { 37 | _signalRConnection.Stop(); 38 | _signalRConnection.Dispose(); 39 | _signalRConnection = null; 40 | 41 | btnConnect.Enabled = true; 42 | txtUrl.Enabled = true; 43 | txtUserName.Enabled = true; 44 | btnDisconnect.Enabled = false; 45 | grpMessaging.Enabled = false; 46 | grpMembership.Enabled = false; 47 | } 48 | } 49 | 50 | private void btnSend_Click(object sender, EventArgs e) 51 | { 52 | //Call the "Send" method on the hub (on the server) with the given parameters 53 | _hubProxy.Invoke("Send", txtMessage.Text); 54 | } 55 | 56 | private void btnJoinGroup_Click(object sender, EventArgs e) 57 | { 58 | //Call the "JoinGroup" method on the hub (on the server) 59 | _hubProxy.Invoke("JoinGroup", txtGroupName.Text); 60 | } 61 | 62 | private void btnLeaveGroup_Click(object sender, EventArgs e) 63 | { 64 | //Call the "LeaveGroup" method on the hub (on the server) 65 | _hubProxy.Invoke("LeaveGroup", txtGroupName.Text); 66 | } 67 | 68 | private async Task connectAsync() 69 | { 70 | //Create a connection for the SignalR server 71 | _signalRConnection = new HubConnection(txtUrl.Text); 72 | _signalRConnection.StateChanged += HubConnection_StateChanged; 73 | 74 | //Get a proxy object that will be used to interact with the specific hub on the server 75 | //Ther may be many hubs hosted on the server, so provide the type name for the hub 76 | _hubProxy = _signalRConnection.CreateHubProxy("SimpleHub"); 77 | 78 | //Reigster to the "AddMessage" callback method of the hub 79 | //This method is invoked by the hub 80 | _hubProxy.On("AddMessage", (name, message) => writeToLog($"{name}:{message}")); 81 | 82 | btnConnect.Enabled = false; 83 | 84 | try 85 | { 86 | //Connect to the server 87 | await _signalRConnection.Start(); 88 | 89 | //Send user name for this client, so we won't need to send it with every message 90 | await _hubProxy.Invoke("SetUserName", txtUserName.Text); 91 | 92 | txtUrl.Enabled = false; 93 | txtUserName.Enabled = false; 94 | btnDisconnect.Enabled = true; 95 | grpMessaging.Enabled = true; 96 | grpMembership.Enabled = true; 97 | } 98 | catch (Exception ex) 99 | { 100 | writeToLog($"Error:{ex.Message}"); 101 | btnConnect.Enabled = true; 102 | } 103 | } 104 | 105 | private void HubConnection_StateChanged(StateChange obj) 106 | { 107 | if(obj.NewState == Microsoft.AspNet.SignalR.Client.ConnectionState.Connected) 108 | writeToLog("Connected"); 109 | else if(obj.NewState == Microsoft.AspNet.SignalR.Client.ConnectionState.Disconnected) 110 | writeToLog("Disconnected"); 111 | } 112 | 113 | private void writeToLog(string log) 114 | { 115 | if (this.InvokeRequired) 116 | this.BeginInvoke(new Action(() => txtLog.AppendText(log + Environment.NewLine))); 117 | else 118 | txtLog.AppendText(log + Environment.NewLine); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /WinFormsClient/FrmClient.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /WinFormsClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace WinFormsClient 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new FrmClient()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WinFormsClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SgClient1")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SgClient1")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("04389b63-da2e-4a82-84dc-2b5d43e2363a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /WinFormsClient/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SgClient1.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SgClient1.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /WinFormsClient/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /WinFormsClient/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SgClient1.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /WinFormsClient/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WinFormsClient/WinFormsClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {04389B63-DA2E-4A82-84DC-2B5D43E2363A} 8 | WinExe 9 | SgClient1 10 | SgClient1 11 | v4.5.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\Microsoft.AspNet.SignalR.Client.2.4.1\lib\net45\Microsoft.AspNet.SignalR.Client.dll 38 | 39 | 40 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Form 58 | 59 | 60 | FrmClient.cs 61 | 62 | 63 | 64 | 65 | FrmClient.cs 66 | 67 | 68 | ResXFileCodeGenerator 69 | Resources.Designer.cs 70 | Designer 71 | 72 | 73 | True 74 | Resources.resx 75 | 76 | 77 | 78 | SettingsSingleFileGenerator 79 | Settings.Designer.cs 80 | 81 | 82 | True 83 | Settings.settings 84 | True 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /WinFormsClient/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /WinFormsServer/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /WinFormsServer/ClientItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace WinFormsServer 9 | { 10 | class ClientItem : INotifyPropertyChanged 11 | { 12 | private string _name; 13 | 14 | public event PropertyChangedEventHandler PropertyChanged; 15 | 16 | public string Id { get; set; } 17 | 18 | public string Name 19 | { 20 | get { return _name; } 21 | set 22 | { 23 | _name = value; 24 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WinFormsServer/FrmServer.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace WinFormsServer 2 | { 3 | partial class FrmServer 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.btnStartServer = new System.Windows.Forms.Button(); 32 | this.label1 = new System.Windows.Forms.Label(); 33 | this.txtUrl = new System.Windows.Forms.TextBox(); 34 | this.btnStop = new System.Windows.Forms.Button(); 35 | this.txtLog = new System.Windows.Forms.TextBox(); 36 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 37 | this.groupBox2 = new System.Windows.Forms.GroupBox(); 38 | this.grpBroadcast = new System.Windows.Forms.GroupBox(); 39 | this.rdToAll = new System.Windows.Forms.RadioButton(); 40 | this.rdToGroup = new System.Windows.Forms.RadioButton(); 41 | this.rdToClient = new System.Windows.Forms.RadioButton(); 42 | this.cmbGroups = new System.Windows.Forms.ComboBox(); 43 | this.cmbClients = new System.Windows.Forms.ComboBox(); 44 | this.label2 = new System.Windows.Forms.Label(); 45 | this.txtMessage = new System.Windows.Forms.TextBox(); 46 | this.btnSend = new System.Windows.Forms.Button(); 47 | this.groupBox1.SuspendLayout(); 48 | this.groupBox2.SuspendLayout(); 49 | this.grpBroadcast.SuspendLayout(); 50 | this.SuspendLayout(); 51 | // 52 | // btnStartServer 53 | // 54 | this.btnStartServer.Location = new System.Drawing.Point(44, 45); 55 | this.btnStartServer.Name = "btnStartServer"; 56 | this.btnStartServer.Size = new System.Drawing.Size(108, 23); 57 | this.btnStartServer.TabIndex = 0; 58 | this.btnStartServer.Text = "Start"; 59 | this.btnStartServer.UseVisualStyleBackColor = true; 60 | this.btnStartServer.Click += new System.EventHandler(this.btnStartServer_Click); 61 | // 62 | // label1 63 | // 64 | this.label1.AutoSize = true; 65 | this.label1.Location = new System.Drawing.Point(15, 22); 66 | this.label1.Name = "label1"; 67 | this.label1.Size = new System.Drawing.Size(20, 13); 68 | this.label1.TabIndex = 1; 69 | this.label1.Text = "Url"; 70 | // 71 | // txtUrl 72 | // 73 | this.txtUrl.Location = new System.Drawing.Point(44, 19); 74 | this.txtUrl.Name = "txtUrl"; 75 | this.txtUrl.Size = new System.Drawing.Size(242, 20); 76 | this.txtUrl.TabIndex = 2; 77 | this.txtUrl.Text = "http://localhost:8080"; 78 | // 79 | // btnStop 80 | // 81 | this.btnStop.Enabled = false; 82 | this.btnStop.Location = new System.Drawing.Point(178, 45); 83 | this.btnStop.Name = "btnStop"; 84 | this.btnStop.Size = new System.Drawing.Size(108, 23); 85 | this.btnStop.TabIndex = 3; 86 | this.btnStop.Text = "Stop"; 87 | this.btnStop.UseVisualStyleBackColor = true; 88 | this.btnStop.Click += new System.EventHandler(this.btnStop_Click); 89 | // 90 | // txtLog 91 | // 92 | this.txtLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 93 | | System.Windows.Forms.AnchorStyles.Left) 94 | | System.Windows.Forms.AnchorStyles.Right))); 95 | this.txtLog.Location = new System.Drawing.Point(8, 29); 96 | this.txtLog.Multiline = true; 97 | this.txtLog.Name = "txtLog"; 98 | this.txtLog.ReadOnly = true; 99 | this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both; 100 | this.txtLog.Size = new System.Drawing.Size(418, 205); 101 | this.txtLog.TabIndex = 4; 102 | // 103 | // groupBox1 104 | // 105 | this.groupBox1.Controls.Add(this.txtLog); 106 | this.groupBox1.Location = new System.Drawing.Point(10, 236); 107 | this.groupBox1.Name = "groupBox1"; 108 | this.groupBox1.Size = new System.Drawing.Size(444, 257); 109 | this.groupBox1.TabIndex = 5; 110 | this.groupBox1.TabStop = false; 111 | this.groupBox1.Text = "Log"; 112 | // 113 | // groupBox2 114 | // 115 | this.groupBox2.Controls.Add(this.txtUrl); 116 | this.groupBox2.Controls.Add(this.btnStartServer); 117 | this.groupBox2.Controls.Add(this.btnStop); 118 | this.groupBox2.Controls.Add(this.label1); 119 | this.groupBox2.Location = new System.Drawing.Point(10, 12); 120 | this.groupBox2.Name = "groupBox2"; 121 | this.groupBox2.Size = new System.Drawing.Size(444, 82); 122 | this.groupBox2.TabIndex = 6; 123 | this.groupBox2.TabStop = false; 124 | this.groupBox2.Text = "Server"; 125 | // 126 | // grpBroadcast 127 | // 128 | this.grpBroadcast.Controls.Add(this.btnSend); 129 | this.grpBroadcast.Controls.Add(this.label2); 130 | this.grpBroadcast.Controls.Add(this.txtMessage); 131 | this.grpBroadcast.Controls.Add(this.cmbClients); 132 | this.grpBroadcast.Controls.Add(this.cmbGroups); 133 | this.grpBroadcast.Controls.Add(this.rdToClient); 134 | this.grpBroadcast.Controls.Add(this.rdToGroup); 135 | this.grpBroadcast.Controls.Add(this.rdToAll); 136 | this.grpBroadcast.Enabled = false; 137 | this.grpBroadcast.Location = new System.Drawing.Point(10, 100); 138 | this.grpBroadcast.Name = "grpBroadcast"; 139 | this.grpBroadcast.Size = new System.Drawing.Size(444, 130); 140 | this.grpBroadcast.TabIndex = 7; 141 | this.grpBroadcast.TabStop = false; 142 | this.grpBroadcast.Text = "Broadcast Message"; 143 | // 144 | // rdToAll 145 | // 146 | this.rdToAll.AutoSize = true; 147 | this.rdToAll.Checked = true; 148 | this.rdToAll.Location = new System.Drawing.Point(12, 22); 149 | this.rdToAll.Name = "rdToAll"; 150 | this.rdToAll.Size = new System.Drawing.Size(52, 17); 151 | this.rdToAll.TabIndex = 0; 152 | this.rdToAll.TabStop = true; 153 | this.rdToAll.Text = "To All"; 154 | this.rdToAll.UseVisualStyleBackColor = true; 155 | // 156 | // rdToGroup 157 | // 158 | this.rdToGroup.AutoSize = true; 159 | this.rdToGroup.Location = new System.Drawing.Point(12, 50); 160 | this.rdToGroup.Name = "rdToGroup"; 161 | this.rdToGroup.Size = new System.Drawing.Size(70, 17); 162 | this.rdToGroup.TabIndex = 1; 163 | this.rdToGroup.Text = "To Group"; 164 | this.rdToGroup.UseVisualStyleBackColor = true; 165 | // 166 | // rdToClient 167 | // 168 | this.rdToClient.AutoSize = true; 169 | this.rdToClient.Location = new System.Drawing.Point(12, 76); 170 | this.rdToClient.Name = "rdToClient"; 171 | this.rdToClient.Size = new System.Drawing.Size(67, 17); 172 | this.rdToClient.TabIndex = 2; 173 | this.rdToClient.Text = "To Client"; 174 | this.rdToClient.UseVisualStyleBackColor = true; 175 | // 176 | // cmbGroups 177 | // 178 | this.cmbGroups.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 179 | this.cmbGroups.FormattingEnabled = true; 180 | this.cmbGroups.Location = new System.Drawing.Point(98, 44); 181 | this.cmbGroups.Name = "cmbGroups"; 182 | this.cmbGroups.Size = new System.Drawing.Size(188, 21); 183 | this.cmbGroups.TabIndex = 3; 184 | // 185 | // cmbClients 186 | // 187 | this.cmbClients.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 188 | this.cmbClients.FormattingEnabled = true; 189 | this.cmbClients.Location = new System.Drawing.Point(98, 71); 190 | this.cmbClients.Name = "cmbClients"; 191 | this.cmbClients.Size = new System.Drawing.Size(188, 21); 192 | this.cmbClients.TabIndex = 4; 193 | // 194 | // label2 195 | // 196 | this.label2.AutoSize = true; 197 | this.label2.Location = new System.Drawing.Point(11, 103); 198 | this.label2.Name = "label2"; 199 | this.label2.Size = new System.Drawing.Size(50, 13); 200 | this.label2.TabIndex = 8; 201 | this.label2.Text = "Message"; 202 | // 203 | // txtMessage 204 | // 205 | this.txtMessage.Location = new System.Drawing.Point(98, 98); 206 | this.txtMessage.Name = "txtMessage"; 207 | this.txtMessage.Size = new System.Drawing.Size(188, 20); 208 | this.txtMessage.TabIndex = 7; 209 | // 210 | // btnSend 211 | // 212 | this.btnSend.Location = new System.Drawing.Point(301, 42); 213 | this.btnSend.Name = "btnSend"; 214 | this.btnSend.Size = new System.Drawing.Size(131, 76); 215 | this.btnSend.TabIndex = 9; 216 | this.btnSend.Text = "Send"; 217 | this.btnSend.UseVisualStyleBackColor = true; 218 | this.btnSend.Click += new System.EventHandler(this.btnSend_Click); 219 | // 220 | // FrmServer 221 | // 222 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 223 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 224 | this.ClientSize = new System.Drawing.Size(467, 508); 225 | this.Controls.Add(this.grpBroadcast); 226 | this.Controls.Add(this.groupBox2); 227 | this.Controls.Add(this.groupBox1); 228 | this.Name = "FrmServer"; 229 | this.Text = "Server"; 230 | this.groupBox1.ResumeLayout(false); 231 | this.groupBox1.PerformLayout(); 232 | this.groupBox2.ResumeLayout(false); 233 | this.groupBox2.PerformLayout(); 234 | this.grpBroadcast.ResumeLayout(false); 235 | this.grpBroadcast.PerformLayout(); 236 | this.ResumeLayout(false); 237 | 238 | } 239 | 240 | #endregion 241 | 242 | private System.Windows.Forms.Button btnStartServer; 243 | private System.Windows.Forms.Label label1; 244 | private System.Windows.Forms.TextBox txtUrl; 245 | private System.Windows.Forms.Button btnStop; 246 | private System.Windows.Forms.TextBox txtLog; 247 | private System.Windows.Forms.GroupBox groupBox1; 248 | private System.Windows.Forms.GroupBox groupBox2; 249 | private System.Windows.Forms.GroupBox grpBroadcast; 250 | private System.Windows.Forms.Button btnSend; 251 | private System.Windows.Forms.Label label2; 252 | private System.Windows.Forms.TextBox txtMessage; 253 | private System.Windows.Forms.ComboBox cmbClients; 254 | private System.Windows.Forms.ComboBox cmbGroups; 255 | private System.Windows.Forms.RadioButton rdToClient; 256 | private System.Windows.Forms.RadioButton rdToGroup; 257 | private System.Windows.Forms.RadioButton rdToAll; 258 | } 259 | } 260 | 261 | -------------------------------------------------------------------------------- /WinFormsServer/FrmServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | using Owin; 7 | using Microsoft.Owin.Cors; 8 | using Microsoft.Owin.Hosting; 9 | using Microsoft.AspNet.SignalR; 10 | using System.ComponentModel; 11 | 12 | namespace WinFormsServer 13 | { 14 | public partial class FrmServer : Form 15 | { 16 | private IDisposable _signalR; 17 | private BindingList _clients = new BindingList(); 18 | private BindingList _groups = new BindingList(); 19 | 20 | public FrmServer() 21 | { 22 | InitializeComponent(); 23 | 24 | bindListsToControls(); 25 | 26 | //Register to static hub events 27 | SimpleHub.ClientConnected += SimpleHub_ClientConnected; 28 | SimpleHub.ClientDisconnected += SimpleHub_ClientDisconnected; 29 | SimpleHub.ClientNameChanged += SimpleHub_ClientNameChanged; 30 | SimpleHub.ClientJoinedToGroup += SimpleHub_ClientJoinedToGroup; 31 | SimpleHub.ClientLeftGroup += SimpleHub_ClientLeftGroup; 32 | SimpleHub.MessageReceived += SimpleHub_MessageReceived; 33 | } 34 | 35 | private void bindListsToControls() 36 | { 37 | //Clients list 38 | cmbClients.DisplayMember = "Name"; 39 | cmbClients.ValueMember = "Id"; 40 | cmbClients.DataSource = _clients; 41 | 42 | //Groups list 43 | cmbGroups.DataSource = _groups; 44 | } 45 | 46 | private void SimpleHub_ClientConnected(string clientId) 47 | { 48 | //Add client to our clients list 49 | this.BeginInvoke(new Action(() => _clients.Add(new ClientItem() { Id = clientId, Name = clientId }))); 50 | 51 | writeToLog($"Client connected:{clientId}"); 52 | } 53 | 54 | private void SimpleHub_ClientDisconnected(string clientId) 55 | { 56 | //Remove client from the list 57 | this.BeginInvoke(new Action(() => 58 | { 59 | var client = _clients.FirstOrDefault(x => x.Id == clientId); 60 | if (client != null) 61 | _clients.Remove(client); 62 | })); 63 | 64 | writeToLog($"Client disconnected:{clientId}"); 65 | } 66 | 67 | private void SimpleHub_ClientNameChanged(string clientId, string newName) 68 | { 69 | //Update the client's name if it exists 70 | this.BeginInvoke(new Action(() => 71 | { 72 | var client = _clients.FirstOrDefault(x => x.Id == clientId); 73 | if (client != null) 74 | client.Name = newName; 75 | })); 76 | 77 | writeToLog($"Client name changed. Id:{clientId}, Name:{newName}"); 78 | } 79 | 80 | private void SimpleHub_ClientJoinedToGroup(string clientId, string groupName) 81 | { 82 | //Only add the groups name to our groups list 83 | this.BeginInvoke(new Action(() => 84 | { 85 | var group = _groups.FirstOrDefault(x => x == groupName); 86 | if (group == null) 87 | _groups.Add(groupName); 88 | })); 89 | 90 | writeToLog($"Client joined to group. Id:{clientId}, Group:{groupName}"); 91 | } 92 | 93 | private void SimpleHub_ClientLeftGroup(string clientId, string groupName) 94 | { 95 | writeToLog($"Client left group. Id:{clientId}, Group:{groupName}"); 96 | } 97 | 98 | private void SimpleHub_MessageReceived(string senderClientId, string message) 99 | { 100 | //One of the clients sent a message, log it 101 | this.BeginInvoke(new Action(() => 102 | { 103 | string clientName = _clients.FirstOrDefault(x => x.Id == senderClientId)?.Name; 104 | 105 | writeToLog($"{clientName}:{message}"); 106 | })); 107 | } 108 | 109 | private void btnStartServer_Click(object sender, EventArgs e) 110 | { 111 | txtLog.Clear(); 112 | 113 | try 114 | { 115 | //Start SignalR server with the give URL address 116 | //Final server address will be "URL/signalr" 117 | //Startup.Configuration is called automatically 118 | _signalR = WebApp.Start(txtUrl.Text); 119 | 120 | btnStartServer.Enabled = false; 121 | txtUrl.Enabled = false; 122 | btnStop.Enabled = true; 123 | grpBroadcast.Enabled = true; 124 | 125 | writeToLog($"Server started at:{txtUrl.Text}"); 126 | } 127 | catch (Exception ex) 128 | { 129 | MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 130 | } 131 | } 132 | 133 | private void btnStop_Click(object sender, EventArgs e) 134 | { 135 | _clients.Clear(); 136 | _groups.Clear(); 137 | 138 | SimpleHub.ClearState(); 139 | 140 | if (_signalR != null) 141 | { 142 | _signalR.Dispose(); 143 | _signalR = null; 144 | 145 | btnStop.Enabled = false; 146 | btnStartServer.Enabled = true; 147 | txtUrl.Enabled = true; 148 | grpBroadcast.Enabled = false; 149 | 150 | writeToLog("Server stopped."); 151 | } 152 | } 153 | 154 | private void btnSend_Click(object sender, EventArgs e) 155 | { 156 | var hubContext = GlobalHost.ConnectionManager.GetHubContext(); 157 | 158 | if (rdToAll.Checked) 159 | { 160 | hubContext.Clients.All.addMessage("SERVER", txtMessage.Text); 161 | } 162 | else if (rdToGroup.Checked) 163 | { 164 | hubContext.Clients.Group(cmbGroups.Text).addMessage("SERVER", txtMessage.Text); 165 | } 166 | else if (rdToClient.Checked) 167 | { 168 | hubContext.Clients.Client((string)cmbClients.SelectedValue).addMessage("SERVER", txtMessage.Text); 169 | } 170 | } 171 | 172 | private void writeToLog(string log) 173 | { 174 | if (this.InvokeRequired) 175 | this.BeginInvoke(new Action(() => txtLog.AppendText(log + Environment.NewLine))); 176 | else 177 | txtLog.AppendText(log + Environment.NewLine); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /WinFormsServer/FrmServer.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /WinFormsServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace WinFormsServer 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new FrmServer()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WinFormsServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SgTest3")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SgTest3")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("5f754ef2-5afd-4e4c-b554-a804568deed2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /WinFormsServer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SgTest3.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SgTest3.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /WinFormsServer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /WinFormsServer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SgTest3.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /WinFormsServer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WinFormsServer/SimpleHub.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Collections.Concurrent; 4 | using Microsoft.AspNet.SignalR; 5 | 6 | namespace WinFormsServer 7 | { 8 | public delegate void ClientConnectionEventHandler(string clientId); 9 | public delegate void ClientNameChangedEventHandler(string clientId, string newName); 10 | public delegate void ClientGroupEventHandler(string clientId, string groupName); 11 | 12 | public delegate void MessageReceivedEventHandler(string senderClientId, string message); 13 | 14 | public class SimpleHub : Hub 15 | { 16 | static ConcurrentDictionary _users = new ConcurrentDictionary(); 17 | 18 | public static event ClientConnectionEventHandler ClientConnected; 19 | public static event ClientConnectionEventHandler ClientDisconnected; 20 | public static event ClientNameChangedEventHandler ClientNameChanged; 21 | 22 | public static event ClientGroupEventHandler ClientJoinedToGroup; 23 | public static event ClientGroupEventHandler ClientLeftGroup; 24 | 25 | public static event MessageReceivedEventHandler MessageReceived; 26 | 27 | public static void ClearState() 28 | { 29 | _users.Clear(); 30 | } 31 | 32 | //Called when a client is connected 33 | public override Task OnConnected() 34 | { 35 | _users.TryAdd(Context.ConnectionId, Context.ConnectionId); 36 | 37 | ClientConnected?.Invoke(Context.ConnectionId); 38 | 39 | return base.OnConnected(); 40 | } 41 | 42 | //Called when a client is disconnected 43 | public override Task OnDisconnected(bool stopCalled) 44 | { 45 | string userName; 46 | _users.TryRemove(Context.ConnectionId, out userName); 47 | 48 | ClientDisconnected?.Invoke(Context.ConnectionId); 49 | 50 | return base.OnDisconnected(stopCalled); 51 | } 52 | 53 | #region Client Methods 54 | 55 | public void SetUserName(string userName) 56 | { 57 | _users[Context.ConnectionId] = userName; 58 | 59 | ClientNameChanged?.Invoke(Context.ConnectionId, userName); 60 | } 61 | 62 | public async Task JoinGroup(string groupName) 63 | { 64 | await Groups.Add(Context.ConnectionId, groupName); 65 | 66 | ClientJoinedToGroup?.Invoke(Context.ConnectionId, groupName); 67 | } 68 | 69 | public async Task LeaveGroup(string groupName) 70 | { 71 | await Groups.Remove(Context.ConnectionId, groupName); 72 | 73 | ClientLeftGroup?.Invoke(Context.ConnectionId, groupName); 74 | } 75 | 76 | public void Send(string msg) 77 | { 78 | Clients.All.addMessage(_users[Context.ConnectionId], msg); 79 | 80 | MessageReceived?.Invoke(Context.ConnectionId, msg); 81 | } 82 | 83 | #endregion 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /WinFormsServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using Owin; 2 | using Microsoft.Owin.Cors; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace WinFormsServer 10 | { 11 | class Startup 12 | { 13 | public void Configuration(IAppBuilder app) 14 | { 15 | //CORS need to be enabled for calling SignalR service 16 | app.UseCors(CorsOptions.AllowAll); 17 | //Find and reigster SignalR hubs 18 | app.MapSignalR(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /WinFormsServer/WinFormsServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5F754EF2-5AFD-4E4C-B554-A804568DEED2} 8 | WinExe 9 | SgTest3 10 | SgTest3 11 | v4.5.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\Microsoft.AspNet.SignalR.Core.2.4.1\lib\net45\Microsoft.AspNet.SignalR.Core.dll 38 | 39 | 40 | ..\packages\Microsoft.Owin.4.0.1\lib\net45\Microsoft.Owin.dll 41 | 42 | 43 | ..\packages\Microsoft.Owin.Cors.4.0.1\lib\net45\Microsoft.Owin.Cors.dll 44 | 45 | 46 | ..\packages\Microsoft.Owin.Diagnostics.4.0.1\lib\net45\Microsoft.Owin.Diagnostics.dll 47 | 48 | 49 | ..\packages\Microsoft.Owin.Host.HttpListener.4.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll 50 | 51 | 52 | ..\packages\Microsoft.Owin.Hosting.4.0.1\lib\net45\Microsoft.Owin.Hosting.dll 53 | 54 | 55 | ..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll 56 | 57 | 58 | ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll 59 | 60 | 61 | ..\packages\Owin.1.0\lib\net40\Owin.dll 62 | 63 | 64 | 65 | 66 | 67 | ..\packages\Microsoft.AspNet.Cors.5.0.0\lib\net45\System.Web.Cors.dll 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Form 83 | 84 | 85 | FrmServer.cs 86 | 87 | 88 | 89 | 90 | 91 | 92 | FrmServer.cs 93 | 94 | 95 | ResXFileCodeGenerator 96 | Resources.Designer.cs 97 | Designer 98 | 99 | 100 | True 101 | Resources.resx 102 | 103 | 104 | 105 | SettingsSingleFileGenerator 106 | Settings.Designer.cs 107 | 108 | 109 | True 110 | Settings.settings 111 | True 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /WinFormsServer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /images/js_client2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nthdeveloper/SignalRSamples/a07b83621d41b7582d6ff3ab54193dfe612a79f4/images/js_client2.png -------------------------------------------------------------------------------- /images/winforms_client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nthdeveloper/SignalRSamples/a07b83621d41b7582d6ff3ab54193dfe612a79f4/images/winforms_client.png -------------------------------------------------------------------------------- /images/winforms_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nthdeveloper/SignalRSamples/a07b83621d41b7582d6ff3ab54193dfe612a79f4/images/winforms_server.png --------------------------------------------------------------------------------