├── angular-to-coldfusion
├── src
│ ├── assets
│ │ ├── .gitkeep
│ │ └── cfwebsocket.js
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── styles.css
│ ├── favicon.ico
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ ├── tslint.json
│ ├── index.html
│ ├── main.ts
│ ├── browserslist
│ ├── app
│ │ ├── app.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.css
│ │ ├── app.component.spec.ts
│ │ └── app.component.ts
│ ├── test.ts
│ ├── karma.conf.js
│ └── polyfills.ts
├── e2e
│ ├── src
│ │ ├── app.po.ts
│ │ └── app.e2e-spec.ts
│ ├── tsconfig.e2e.json
│ └── protractor.conf.js
├── .editorconfig
├── tsconfig.json
├── .gitignore
├── README.md
├── package.json
├── tslint.json
└── angular.json
├── .gitignore
├── html-to-coldfusion
├── style.css
├── index.html
├── readme.md
├── script.js
└── cfwebsocket.js
├── .github
└── FUNDING.yml
├── chat
├── subscribers.cfm
├── ping.cfm
├── Application.cfc
├── broadcast.cfm
├── assets
│ ├── chat.css
│ └── chat.js
├── index.cfm
└── ChatListener.cfc
├── favicon.ico
├── favicon.png
├── other-demo-files
├── socket.io_site
│ ├── favicon.ico
│ ├── .htaccess
│ ├── server.json
│ ├── Application.cfc
│ ├── info.cfm
│ ├── index.html
│ ├── web.config
│ ├── broadcast.cfm
│ ├── styles.css
│ ├── form.cfm
│ └── socketio.client.js
└── socket.io
│ ├── public
│ ├── favicon.ico
│ ├── assets
│ │ ├── app.css
│ │ └── app.js
│ └── index.html
│ ├── package.json
│ ├── server.js
│ └── package-lock.json
├── .htaccess
├── instructions.md
├── live-demos
├── demo-00
│ ├── RestrictedListener.cfc
│ ├── index.cfm
│ ├── advanced.cfm
│ └── restricted.cfm
├── ping.cfm
├── subscribers.cfm
├── assets
│ ├── css
│ │ ├── advanced.css
│ │ └── styles.css
│ └── js
│ │ ├── advanced.js
│ │ └── script.js
├── services
│ └── communicate.cfc
├── demo-01
│ ├── index.cfm
│ └── advanced.cfm
├── Application.cfc
├── html
│ ├── index.html
│ └── cfwebsocket.js
└── broadcast.cfm
├── server.11.json
├── server.2021.json
├── server.2016.json
├── server.2018.json
├── server.2023.json
├── .editorconfig
├── shared
└── fetch-joke.cfm
├── web.config
├── Application.cfc
├── LICENSE
├── swift-to-coldfusion
├── ViewController.swift
└── Main.storyboard
├── readme.md
└── index.cfm
/angular-to-coldfusion/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | node_modules
3 | logs
4 | console
--------------------------------------------------------------------------------
/html-to-coldfusion/style.css:
--------------------------------------------------------------------------------
1 | button {
2 | opacity: 0;
3 | }
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: giancarlogomez
2 | patreon: giancarlogomez
3 |
--------------------------------------------------------------------------------
/chat/subscribers.cfm:
--------------------------------------------------------------------------------
1 |
2 | writeDump(wsGetSubscribers("chat"));
3 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/favicon.ico
--------------------------------------------------------------------------------
/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/favicon.png
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/angular-to-coldfusion/src/favicon.ico
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/other-demo-files/socket.io_site/favicon.ico
--------------------------------------------------------------------------------
/other-demo-files/socket.io/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets-Demo-Code/HEAD/other-demo-files/socket.io/public/favicon.ico
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine On
2 |
3 | Header set Access-Control-Allow-Origin "*"
4 |
5 | RewriteCond %{REQUEST_FILENAME} !-f
6 | RewriteCond %{REQUEST_FILENAME} !-d
7 | RewriteRule ^(.*)/$ $1.cfm [QSA,L]
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "exclude": [
8 | "test.ts",
9 | "**/*.spec.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/.htaccess:
--------------------------------------------------------------------------------
1 | RewriteEngine On
2 |
3 | Header set Access-Control-Allow-Origin "*"
4 | Header set Access-Control-Allow-Headers "*"
5 |
6 | RewriteCond %{REQUEST_FILENAME} !-f
7 | RewriteCond %{REQUEST_FILENAME} !-d
8 | RewriteRule ^(.*)/$ $1.cfm [QSA,L]
--------------------------------------------------------------------------------
/instructions.md:
--------------------------------------------------------------------------------
1 | ## Server 1
2 | box start server
3 |
4 | ## Server 2
5 | box start ./other-demo-files/socket.io_site/server
6 |
7 | ## Socket IO Server
8 | ./other-demo-files/socket.io/node server
9 |
10 | ## Angular Project
11 | ./angular-to-coldfusion/ng serve
12 |
13 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sample-socket-io",
3 | "version": "0.0.1",
4 | "description": "My demo socket.io app",
5 | "type": "module",
6 | "dependencies": {
7 | "express": "^4.19.2",
8 | "socket.io": "^4.7.5"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/server.json:
--------------------------------------------------------------------------------
1 | {
2 | "app":{
3 | "cfengine":"lucee"
4 | },
5 | "web":{
6 | "gzipEnable":true,
7 | "rewrites":{
8 | "enable":true,
9 | "config":".htaccess"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/angular-to-coldfusion/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/angular-to-coldfusion/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/live-demos/demo-00/RestrictedListener.cfc:
--------------------------------------------------------------------------------
1 | component extends="CFIDE.websocket.ChannelListener" {
2 |
3 | public boolean function allowSubscribe(struct subscriberInfo) {
4 | // I am crucial. You must use me. Or else.
5 | if( !arguments.subscriberInfo.connectionInfo.authenticated ) return false;
6 |
7 | return true;
8 | }
9 |
10 | }
--------------------------------------------------------------------------------
/chat/ping.cfm:
--------------------------------------------------------------------------------
1 |
2 | param name="url.id" default="";
3 |
4 | clients = wsGetSubscribers("chat");
5 | clientIDs = [];
6 |
7 | arrayEach(clients, function(rec){
8 | arrayAppend(clientIDs,rec.clientID);
9 | });
10 |
11 | rtn = { "success" : arrayFind(clientIDs,url.id) ? true : false };
12 |
13 | writeOutput( serializeJSON(rtn) );
14 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "test.ts",
12 | "polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/server.11.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"realtime-ws-demo-code-@11",
3 | "trayicon":"favicon.png",
4 | "app":{
5 | "cfengine":"adobe@11.0.19+314546"
6 | },
7 | "jvm":{
8 | "heapSize":512,
9 | "minHeapSize":512
10 | },
11 | "web":{
12 | "rewrites":{
13 | "enable":true,
14 | "config":".htaccess"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server.2021.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"realtime-ws-demo-code-@2021",
3 | "trayicon":"favicon.png",
4 | "app":{
5 | "cfengine":"adobe@2021.0.2+328618"
6 | },
7 | "jvm":{
8 | "heapSize":512,
9 | "minHeapSize":512
10 | },
11 | "web":{
12 | "rewrites":{
13 | "enable":true,
14 | "config":".htaccess"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('workspace-project App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to websockets!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/live-demos/ping.cfm:
--------------------------------------------------------------------------------
1 |
2 | param name="url.id" default="";
3 | // get all clients
4 | clients = wsGetSubscribers("demo");
5 | match = false;
6 | // go thru each until a match is found
7 | for (rec in clients){
8 | if(!compare(rec.clientid,url.id)){
9 | match = true;
10 | break;
11 | }
12 | }
13 | // return response
14 | writeOutput(serializeJSON({"success":match}));
15 |
--------------------------------------------------------------------------------
/server.2016.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"realtime-ws-demo-code-@2016",
3 | "trayicon":"favicon.png",
4 | "app":{
5 | "cfengine":"adobe@2016.0.17+325979"
6 | },
7 | "jvm":{
8 | "heapSize":512,
9 | "minHeapSize":512
10 | },
11 | "web":{
12 | "rewrites":{
13 | "enable":true,
14 | "config":".htaccess"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server.2018.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"realtime-ws-demo-code-@2018",
3 | "trayicon":"favicon.png",
4 | "app":{
5 | "cfengine":"adobe@2018.0.19+330149"
6 | },
7 | "jvm":{
8 | "heapSize":512,
9 | "minHeapSize":512
10 | },
11 | "web":{
12 | "rewrites":{
13 | "enable":true,
14 | "config":".htaccess"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/live-demos/subscribers.cfm:
--------------------------------------------------------------------------------
1 |
2 | topLevelChannels = wsGetAllChannels();
3 |
4 | for (channel in topLevelChannels){
5 |
6 | subChannels = wsGetAllChannels(channel);
7 |
8 | if (subChannels.len()){
9 | for (subChannel in subChannels)
10 | writeDump(label:subChannel,var:wsGetSubscribers(subChannel))
11 | } else {
12 | writeDump(label:channel,var:wsGetSubscribers(channel));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Websockets
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.error(err));
13 |
14 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | #
5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
6 |
7 | > 0.5%
8 | last 2 versions
9 | Firefox ESR
10 | not dead
11 | not IE 9-11
--------------------------------------------------------------------------------
/server.2023.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"realtime-ws-demo-code-@2023",
3 | "trayEnable":true,
4 | "trayicon":"favicon.png",
5 | "dockEnable":false,
6 | "app":{
7 | "cfengine":"adobe@2023.0.10+330680"
8 | },
9 | "jvm":{
10 | "heapSize":512,
11 | "minHeapSize":512
12 | },
13 | "web":{
14 | "rewrites":{
15 | "enable":true,
16 | "config":".htaccess"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/Application.cfc:
--------------------------------------------------------------------------------
1 | component {
2 | this.name = "socketio_demo";
3 |
4 | function onError(exception,eventName){
5 | writeOutput(arguments.exception.message);
6 | }
7 |
8 | function onRequestStart(){
9 | cfheader(name:"Access-Control-Allow-Origin",value:"*");
10 | cfheader(name:"Access-Control-Allow-Headers",value:"*");
11 | request.socketio_server = "http://localhost";
12 | request.socketio_port = 8080;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 |
5 | import { AppComponent } from './app.component';
6 |
7 | @NgModule({
8 | declarations: [
9 | AppComponent
10 | ],
11 | imports: [
12 | BrowserModule,
13 | FormsModule
14 | ],
15 | providers: [],
16 | bootstrap: [AppComponent]
17 | })
18 | export class AppModule { }
19 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "module": "es2015",
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "target": "es5",
13 | "typeRoots": [
14 | "node_modules/@types"
15 | ],
16 | "lib": [
17 | "es2017",
18 | "dom"
19 | ]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = false
9 | indent_style = tab
10 | indent_size = 4
11 | tab_width = 4
12 |
13 | [*.js]
14 | indent_style = space
15 | indent_size = 4
16 | tab_width = 4
17 | charset = utf-8
18 | trim_trailing_whitespace = true
19 | insert_final_newline = false
20 |
21 | [*.yml]
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [*.{md,markdown}]
26 | trim_trailing_whitespace = false
27 | insert_final_newline = false
--------------------------------------------------------------------------------
/live-demos/assets/css/advanced.css:
--------------------------------------------------------------------------------
1 | #status {
2 | color:#fff;
3 | left:50%;
4 | line-height: 50px;
5 | min-width: 200px;
6 | opacity: 0;
7 | padding: 0 1em;
8 | position: absolute;
9 | text-align: center;
10 | text-transform: uppercase;
11 | top:0;
12 | transform: translateX(-50%);
13 | transition: opacity .25s;
14 | z-index: 100;
15 | }
16 | #status.on {opacity:1;}
17 | #status.connected {background-color: rgba(0,255,0,.5);}
18 | #status.disconnected {background-color: rgba(255,0,0,.5);}
19 | #status.connecting {background-color: rgba(0,0,255,.5);}
--------------------------------------------------------------------------------
/other-demo-files/socket.io/public/assets/app.css:
--------------------------------------------------------------------------------
1 |
2 | .btn:focus-visible,
3 | .form-control:focus{
4 | box-shadow: none;
5 | }
6 | .form-control:focus{
7 | border-color: var(--bs-border-color);
8 | }
9 | section {
10 | height: calc( 100vh - 3.5625rem ) ;
11 | ul {
12 | flex-grow: 1;
13 |
14 | li { padding : 1rem; }
15 | li:nth-child(odd) { background: #efefef; }
16 | }
17 | }
18 | pre {
19 | font-family: 'JetBrains Mono', monospace;
20 | font-weight: 300;
21 | line-height: 1.42;
22 | margin: 0;
23 | }
--------------------------------------------------------------------------------
/live-demos/assets/js/advanced.js:
--------------------------------------------------------------------------------
1 | var statusDiv = document.getElementById('status');
2 |
3 | AdvancedSocket.disconnected = function(){
4 | statusDiv.innerHTML = 'Disconnected';
5 | showStatus('disconnected');
6 | }
7 |
8 | AdvancedSocket.connected = function(){
9 | statusDiv.innerHTML = 'Connected';
10 | showStatus('connected');
11 | }
12 |
13 | AdvancedSocket.connecting = function(){
14 | statusDiv.innerHTML = 'Connecting ...';
15 | showStatus('connecting');
16 | }
17 |
18 | function showStatus(statusClass){
19 | statusDiv.className = 'on ' + statusClass;
20 | }
--------------------------------------------------------------------------------
/angular-to-coldfusion/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/info.cfm:
--------------------------------------------------------------------------------
1 |
2 | rtn = {"success":true,"message":""};
3 | if( !structKeyExists(request,"socketio_server") )
4 | rtn.message = "You must set the URL to the Socket.IO server - review instructions in Application.onRequestStart()";
5 | if( !structKeyExists(request,"socketio_port") )
6 | rtn.message = "You must set the port to your Socket.IO server - review instructions in Application.onRequestStart()";
7 | rtn.success = len(rtn.message) ? false : true;
8 | if ( rtn.success ){
9 | rtn["data"] = {
10 | "socketio_server" : request.socketio_server,
11 | "socketio_port" : request.socketio_port
12 | };
13 | }
14 | cfcontent(reset="true",type="application/json");
15 | writeOutput( serializeJSON(rtn) );
16 |
--------------------------------------------------------------------------------
/shared/fetch-joke.cfm:
--------------------------------------------------------------------------------
1 |
2 | joke = "";
3 | try {
4 | categories = [
5 | "animal",
6 | "career",
7 | "celebrity",
8 | "dev",
9 | // "explicit",
10 | "fashion",
11 | "food",
12 | "history",
13 | "money",
14 | "movie",
15 | "music",
16 | "political",
17 | "religion",
18 | "science",
19 | "sport",
20 | "travel"
21 | ];
22 | cfhttp(
23 | url = "https://api.chucknorris.io/jokes/random?category=" & categories[ randRange(1,categories.len()) ],
24 | timeout = 5,
25 | result = "apiRequest"
26 | );
27 | joke = deserializeJSON( apiRequest.fileContent ).value
28 | }
29 | catch ( any e ){
30 | joke = "We could not fetch a funny joke so this is just the server responding";
31 | }
32 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/live-demos/services/communicate.cfc:
--------------------------------------------------------------------------------
1 | component {
2 | remote any function publish(){
3 | sleep( 1000 );
4 | return "This is the value returned from publish()";
5 | }
6 |
7 | remote any function p2p( string value = 5 ){
8 | if ( !isNumeric( arguments.value ) )
9 | arguments.value = 5;
10 | cfthread ( action="run", name="backToInvoker_#createUUID()#", total=arguments.value ) {
11 | for ( i = 1; i <= total; i++ ){
12 | sleep( 500 );
13 | wsSendMessage( i & " of " & total & " : I am only returning this to the P2P Client" );
14 | }
15 | }
16 | return "This is the value returned from p2p()";
17 | }
18 |
19 | remote any function doIt( required string name, required string message ){
20 | return arguments.name & " says """ & arguments.message & """";
21 | }
22 | }
--------------------------------------------------------------------------------
/html-to-coldfusion/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML TO CF
6 |
7 |
8 |
9 |
10 |
11 | HTML TO CF
12 | MESSAGE
13 | CLEAR LOG
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/live-demos/demo-01/index.cfm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WebSocket Demo - Simple
6 |
7 |
8 |
9 |
10 |
11 | DEMO - SIMPLE
12 | MESSAGE
13 | CLEAR LOG
14 |
15 |
16 |
17 |
18 |
19 |
20 | Send Message
21 |
22 |
23 |
24 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Socket.IO Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | SOCKET IO
14 | MESSAGE
15 | CLEAR LOG
16 |
17 |
18 |
19 |
20 |
21 |
22 | Send Message
23 |
24 |
25 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 | Welcome to {{ title }} on Angular
3 |
4 | Send Message
5 | {{message}}
6 |
7 |
8 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './src/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: require('path').join(__dirname, './tsconfig.e2e.json')
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
--------------------------------------------------------------------------------
/Application.cfc:
--------------------------------------------------------------------------------
1 | component {
2 | this.name = "websockets_demo";
3 |
4 | function onError(exception,eventName){
5 | writeOutput("" & arguments.exception.message & "
");
6 | }
7 |
8 | function onRequestStart(){
9 | /*
10 | * Set the request parameters to the IIS site running the code in
11 | * /other-demo-files/socket.io_site/
12 | */
13 | // request.socketio_server = "http://localhost:{{port}}";
14 | // request.socketio_broadcast = "https://localhost:{{port}}/broadcast/";
15 | cfheader(name:"Access-Control-Allow-Origin",value:"*");
16 | cfheader(name:"Access-Control-Allow-Headers",value:"*");
17 | request.socketio_server = "http://localhost:51095/";
18 | request.socketio_broadcast = "http://localhost:51095/broadcast/";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io/public/assets/app.js:
--------------------------------------------------------------------------------
1 | const socket = io('',{ query: { channels : 'general,chat' }});
2 | const form = document.getElementById('form');
3 | const message = document.getElementById('message');
4 | const messages = document.getElementById('messages');
5 |
6 | form.addEventListener('submit', event => {
7 | event.preventDefault();
8 | if ( message.value.toString().trim() ) {
9 | socket.emit('publish',{ channel:'chat', message:message.value});
10 | message.value = '';
11 | }
12 | });
13 |
14 | socket.on( 'message', data => {
15 | const item = document.createElement('li'),
16 | pre = document.createElement('pre');
17 |
18 | pre.textContent = JSON.stringify( data ).replace(/,"/g,',\n "').replace('{','{\n ').replace('}','\n}');
19 |
20 | item.appendChild( pre )
21 | messages.appendChild( item );
22 | messages.scrollTo( { top:document.body.scrollHeight,left:0,behavior:'smooth' } );
23 | });
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | *{
2 | box-sizing: border-box;
3 | }
4 | h1,div,textarea {
5 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
6 | }
7 | h1 {
8 | text-align: center;
9 | font-weight: 300;
10 | }
11 | textarea {
12 | resize: none;
13 | width:100%;
14 | height:3em;
15 | font-size: 14px;
16 | text-align: center;
17 | padding:1em;
18 | line-height: 1;
19 | border:1px solid #ddd;
20 | display: block;
21 | }
22 | button {
23 | width:100%;
24 | border:none;
25 | display: block;
26 | line-height: 44px;
27 | text-transform: uppercase;
28 | text-align: center;
29 | margin: 1em 0;
30 | }
31 | .message_box{
32 | border:1px solid #ddd;
33 | background-color: #f5f5f5;
34 | min-height: 300px;
35 | display: flex;
36 | align-items: center;
37 | justify-content: center;
38 | color: #444;
39 | padding:20px;
40 | text-align: center;
41 | }
42 |
43 | img {
44 | display: block;
45 | margin:0 auto;
46 | }
--------------------------------------------------------------------------------
/html-to-coldfusion/readme.md:
--------------------------------------------------------------------------------
1 | # Connecting without ColdFusion JavaScript
2 |
3 | This is a simple example on how to connect to the WebSockets Server without ColdFusion Team's JavaScript files.
4 |
5 | The WebSocket API is simple and straight forward but subscribing, unsubscribing, authenticating, etc is all covered in the ColdFusion JS files as there is custom data that is prepared and sent to the server from the client.
6 |
7 | In this example I created a cfwebsocket.js file that prepares the object necessary for subscribing, unsubscribing and publishing messages to a channel.
8 |
9 | The ColdFusion Team's JavaScript files where I was able to reverse engineer this from are:
10 | * /CFIDE/scripts/ajax/package/cfwebsocketCore.js
11 | * /CFIDE/scripts/ajax/package/cfwebsocketChannel.js
12 |
13 | __REQUIRED SETUP IN script.js__
14 | Make sure to edit the parameters when creating the connection based on the server and ports you are running.
15 |
16 | __IMPORTANT__
17 | Please make sure to run the first example under `../live-demos/demo-01/` as this is the application used.
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
--------------------------------------------------------------------------------
/other-demo-files/socket.io/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Socket.IO Chat
6 |
7 |
8 |
9 |
10 |
11 | Socket.IO Chat
12 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Giancarlo Gomez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/README.md:
--------------------------------------------------------------------------------
1 | # Websockets
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.2.4.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | declarations: [
8 | AppComponent
9 | ],
10 | }).compileComponents();
11 | }));
12 |
13 | it('should create the app', () => {
14 | const fixture = TestBed.createComponent(AppComponent);
15 | const app = fixture.debugElement.componentInstance;
16 | expect(app).toBeTruthy();
17 | });
18 |
19 | it(`should have as title 'websockets'`, () => {
20 | const fixture = TestBed.createComponent(AppComponent);
21 | const app = fixture.debugElement.componentInstance;
22 | expect(app.title).toEqual('websockets');
23 | });
24 |
25 | it('should render title in a h1 tag', () => {
26 | const fixture = TestBed.createComponent(AppComponent);
27 | fixture.detectChanges();
28 | const compiled = fixture.debugElement.nativeElement;
29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to websockets!');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/live-demos/Application.cfc:
--------------------------------------------------------------------------------
1 | component {
2 | this.name = "websockets_2021_demo";
3 | this.sessionmanagement = true;
4 | this.sessiontimeout = createTimeSpan(1,0,0,0);
5 | this.serialization.preservecaseforstructkey = true;
6 |
7 | this.wschannels = [
8 | {name:"demo"},
9 | {name:"conference"},
10 | {name:"restricted",cfclistener:"demo-00.RestrictedListener"}
11 | ];
12 |
13 | public void function onRequestStart() {
14 | if (structKeyExists(url, "reload")){
15 | wsPublish("demo","FORCE-RECONNECT");
16 | applicationStop();
17 | location(cgi.script_name,false);
18 | }
19 | }
20 |
21 | public void function onError(exception,eventName){
22 | writeDump(arguments.exception);
23 | }
24 |
25 | public boolean function onWSAuthenticate(string username, string password, struct connectionInfo) {
26 | var usersAllowed = ["JC","Maria","Mailang","Jonah","Gia"];
27 | var authenticated = arrayFindNoCase(usersAllowed,arguments.username);
28 | if (authenticated){
29 | arguments.connectionInfo.authenticated = true;
30 | arguments.connectionInfo.username = arguments.username;
31 | } else {
32 | arguments.connectionInfo.authenticated = false;
33 | }
34 | return authenticated ;
35 | }
36 | }
--------------------------------------------------------------------------------
/html-to-coldfusion/script.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This example is based on the application found under the /live-demos/demo-01/Application.cfc
3 | * which is where the channel and appName (Application name) comes from
4 | * The objects defined as subscribe,unsubscribe and message is how the WebSocket server expects to receive data in
5 | * a send() call.
6 | */
7 | var cfws = new cfwebsocket(
8 | 'websockets_demo1', // appName
9 | 'demo', // channel
10 | 'http://127.0.0.1:50320/live-demos/demo-01/runme/', // appStartPath
11 | parseMessage, // onMessage
12 | subscribed, // onSubscribe
13 | false, // isProxy
14 | 8581, // wsPort
15 | '127.0.0.1', // server
16 | false, // secure,
17 | {device:'Mobile'} // customOptions
18 | );
19 |
20 | // overwrite from /assets/js/script.js
21 | sendMessage.removeEventListener('click',doSendMessage);
22 | sendMessage.addEventListener('click',publish);
23 |
24 | function publish(){
25 | if (cfws.publish(messageText.value.trim()))
26 | doToggleMessageForm();
27 | }
28 |
29 | function subscribed(){
30 | // lets show our buttons
31 | Array.from(document.getElementsByTagName('button')).forEach(function(button){
32 | button.style.opacity = 1;
33 | button.disabled = false;
34 | });
35 | }
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.css']
7 | })
8 | export class AppComponent {
9 | title = 'WebSockets'
10 | message = '...'
11 | cfwebsocket:any;
12 | user_message:string = ''
13 |
14 | constructor() {
15 | if (typeof window['cfwebsocket'] === 'function'){
16 | this.cfwebsocket = new window['cfwebsocket'](
17 | 'websockets_demo1', // appName
18 | 'demo', // channel
19 | 'http://localhost:50320/live-demos/demo-01/runme/', // appStartPath
20 | this, // onMessage
21 | this, // onSubscribe
22 | false, // isProxy
23 | 8581, // wsPort
24 | 'localhost', // server
25 | false // secure
26 | )
27 | }
28 | }
29 |
30 | parseMessage(msg){
31 | if (msg.data)
32 | this.message = msg.data;
33 | }
34 |
35 | subscribed(){
36 | this.message = 'We are subscribed!'
37 | }
38 |
39 | publish(){
40 | if (this.cfwebsocket.publish(this.user_message.trim()))
41 | this.user_message = '';
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/live-demos/demo-00/index.cfm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | WebSocket Simple Example
8 |
9 |
10 |
11 |
12 |
13 | #application.applicationname#
14 | Simple Example Everything is in the console
15 |
16 |
17 |
18 |
25 |
39 |
40 |
--------------------------------------------------------------------------------
/live-demos/demo-01/advanced.cfm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WebSocket Demo - Advanced
6 |
7 |
8 |
9 |
16 |
17 |
18 |
19 | DEMO - ADVANCED
20 | MESSAGE
21 | CLEAR LOG
22 |
23 |
24 |
25 |
26 |
27 |
28 | Send Message
29 |
30 |
31 |
32 |
33 |
34 |
35 |
40 |
41 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "websockets",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular/animations": "^6.1.0",
15 | "@angular/common": "^6.1.0",
16 | "@angular/compiler": "^6.1.0",
17 | "@angular/core": "^6.1.0",
18 | "@angular/forms": "^6.1.0",
19 | "@angular/http": "^6.1.0",
20 | "@angular/platform-browser": "^6.1.0",
21 | "@angular/platform-browser-dynamic": "^6.1.0",
22 | "@angular/router": "^6.1.0",
23 | "core-js": "^2.5.4",
24 | "rxjs": "~6.2.0",
25 | "zone.js": "~0.8.26"
26 | },
27 | "devDependencies": {
28 | "@angular-devkit/build-angular": "~0.8.0",
29 | "@angular/cli": "~6.2.4",
30 | "@angular/compiler-cli": "^6.1.0",
31 | "@angular/language-service": "^6.1.0",
32 | "@types/jasmine": "~2.8.8",
33 | "@types/jasminewd2": "~2.0.3",
34 | "@types/node": "~8.9.4",
35 | "codelyzer": "~4.3.0",
36 | "jasmine-core": "~2.99.1",
37 | "jasmine-spec-reporter": "~4.2.1",
38 | "karma": "~3.0.0",
39 | "karma-chrome-launcher": "~2.2.0",
40 | "karma-coverage-istanbul-reporter": "~2.0.1",
41 | "karma-jasmine": "~1.1.2",
42 | "karma-jasmine-html-reporter": "^0.2.2",
43 | "protractor": "~5.4.0",
44 | "ts-node": "~7.0.0",
45 | "tslint": "~5.11.0",
46 | "typescript": "~2.9.2"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/live-demos/demo-00/advanced.cfm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | WebSocket Advanced Example
8 |
9 |
10 |
19 |
20 |
21 |
22 | #application.applicationname#
23 | Advanced Example Everything is in the console
24 |
25 |
26 |
27 |
28 |
29 |
34 |
35 |
40 |
41 |
--------------------------------------------------------------------------------
/live-demos/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | HTML Test
8 |
9 |
10 |
11 |
12 | HTML Example
13 | Nothing to see here Everything is in the console
14 |
15 |
16 |
46 |
47 |
--------------------------------------------------------------------------------
/chat/Application.cfc:
--------------------------------------------------------------------------------
1 | component {
2 | this.name = "webSocketsChat";
3 | this.sessionmanagement = true;
4 | this.sessiontimeout = createTimeSpan(1,0,0,0);
5 | this.serialization.preservecaseforstructkey = true;
6 |
7 | // websockets
8 | this.wschannels = [
9 | {name:"chat",cfclistener:"ChatListener"}
10 | ];
11 |
12 | public boolean function onApplicationStart(){
13 | application.timestamp = getHttpTimeString();
14 | application.publishedMessages = 0;
15 | application.publishedPreviousMessages = 0;
16 | return true;
17 | }
18 |
19 | public boolean function onRequestStart(targetPage){
20 | if (structKeyExists(url,"reload")){
21 | // tell everyone to reconnect ( AdvancedSocket Feature )
22 | WsPublish("chat","FORCE-RECONNECT");
23 | applicationStop();
24 | location('./',false);
25 | }
26 | return true;
27 | }
28 |
29 | public boolean function onWSAuthenticate(string username, string password, struct connectionInfo) {
30 | // Demo User Authentication - very simple here you would do real work
31 | var usersAllowed = ["Admin","JC","Maria","Mailang","Jonah","Gia"];
32 | var authenticated = arrayFindNoCase(usersAllowed,arguments.username);
33 | if (authenticated){
34 | arguments.connectionInfo.authenticated = true;
35 | arguments.connectionInfo.username = arguments.username;
36 | } else {
37 | connectionInfo.authenticated = false;
38 | }
39 | return authenticated;
40 | }
41 | }
--------------------------------------------------------------------------------
/live-demos/demo-00/restricted.cfm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | WebSocket Example Auth
8 |
9 |
10 |
11 |
12 |
13 | #application.applicationname#
14 | Restricted Example Everything is in the console
15 |
16 |
17 |
18 |
24 |
53 |
54 |
--------------------------------------------------------------------------------
/chat/broadcast.cfm:
--------------------------------------------------------------------------------
1 |
2 | // this stops an OPTIONS request from continuing to process
3 | if ( !compare(cgi.request_method,"OPTIONS") )
4 | exit;
5 |
6 | isAjax = !compareNoCase(cgi["X-Requested-With"],"XMLHttpRequest");
7 | threadName = "ws_msg_" & createUUID();
8 |
9 | params = {
10 | "channel" : "chat",
11 | "message" : url.message ?: ( form.message ?: "" )
12 | };
13 |
14 | asJSON = val( url.json ?: ( form.json ?: 0 ) );
15 |
16 | if ( !params.message.len() ){
17 | include "../shared/fetch-joke.cfm";
18 | params.message = joke;
19 | }
20 | // remove HTML entities from string because it bombs out
21 | params.message = params.message .replaceAll("\&[^\;]+\;","");
22 | // publish via thread, specially helpful when using Frameworks or calling in the middle of an Ajax Request
23 | cfthread( action="run",name=threadName,params=params,asJSON=asJSON){
24 | wsPublish(
25 | attributes.params.channel,
26 | attributes.asJSON ? serializeJSON(attributes.params) : attributes.params.message
27 | );
28 | }
29 | // for XHR response
30 | if ( isAjax ){
31 | writeOutput( params.message );
32 | exit;
33 | }
34 |
35 |
36 |
37 |
38 |
39 |
40 | Message Posted
41 |
42 |
43 |
44 |
45 | The message you posted is as follows
46 | #params.message#
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/live-demos/broadcast.cfm:
--------------------------------------------------------------------------------
1 |
2 | // this stops an OPTIONS request from continuing to process
3 | if ( !compare(cgi.request_method,"OPTIONS") )
4 | exit;
5 |
6 | isAjax = !compareNoCase(cgi["X-Requested-With"],"XMLHttpRequest");
7 | threadName = "ws_msg_" & createUUID();
8 |
9 | params = {
10 | "channel" : lcase( url.channel ?: ( form.channel ?: "demo" ) ),
11 | "message" : url.message ?: ( form.message ?: "" )
12 | };
13 |
14 | asJSON = val( url.json ?: ( form.json ?: 0 ) );
15 |
16 | if ( !params.message.len() ){
17 | include "../shared/fetch-joke.cfm";
18 | params.message = joke;
19 | }
20 | // remove HTML entities from string because it bombs out
21 | params.message = params.message .replaceAll("\&[^\;]+\;","");
22 | // publish via thread, specially helpful when using Frameworks or calling in the middle of an Ajax Request
23 | cfthread( action="run",name=threadName,params=params,asJSON=asJSON){
24 | wsPublish(
25 | attributes.params.channel,
26 | attributes.asJSON ? serializeJSON(attributes.params) : attributes.params.message
27 | );
28 | }
29 | // for XHR response
30 | if ( isAjax ){
31 | writeOutput( params.message );
32 | exit;
33 | }
34 |
35 |
36 |
37 |
38 |
39 |
40 | Message Posted
41 |
42 |
43 |
44 |
45 | The message you posted is as follows
46 | #params.message#
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/broadcast.cfm:
--------------------------------------------------------------------------------
1 |
2 | if( !structKeyExists(request,"socketio_server") )
3 | throw("You must set the URL to the Socket.IO server - review instructions in Application.onRequestStart()");
4 | if( !structKeyExists(request,"socketio_port") )
5 | throw("You must set the port to your Socket.IO server - review instructions in Application.onRequestStart()");
6 |
7 | // this stops an OPTIONS request from continuing to process
8 | if ( !compare(cgi.request_method,"OPTIONS") )
9 | exit;
10 |
11 | isAjax = !compareNoCase(cgi["X-Requested-With"] ?: "","XMLHttpRequest");
12 |
13 | params = {
14 | "channel" : lcase( url.channel ?: ( form.channel ?: "demo" ) ),
15 | "message" : url.message ?: ( form.message ?: "" )
16 | };
17 |
18 | asJSON = val( url.json ?: ( form.json ?: 0 ) );
19 |
20 | if ( !params.message.len() ){
21 | include "../shared/fetch-joke.cfm";
22 | params.message = joke;
23 | }
24 | // remove HTML entities from string because it bombs out
25 | params.message = params.message.replaceAll("\&[^\;]+\;","");
26 | // create new request
27 | cfhttp(
28 | url = request.socketio_server & "/publish",
29 | method = "POST",
30 | port = request.socketio_port
31 | ){
32 | cfhttpparam(type:"formField",name:"channel",value:params.channel);
33 | cfhttpparam(type:"formField",name:"message",value: asJSON ? serializeJSON(params) : params.message);
34 | }
35 | // for XHR response
36 | if ( isAjax ){
37 | writeOutput( params.message );
38 | exit;
39 | }
40 |
41 |
42 |
43 |
44 |
45 |
46 | Message Posted
47 |
48 |
49 |
50 |
51 | The message you posted is as follows
52 | #params.message#
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/chat/assets/chat.css:
--------------------------------------------------------------------------------
1 | :root{
2 | --font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
3 | }
4 | body{
5 | font-family:var(--font-family) !important;
6 | height:100vh;
7 | min-height:400px;
8 | }
9 | main {
10 | border:1px solid #ddd;
11 | border-width: 1px 0;
12 | height:calc(100vh - 13em);
13 | }
14 | #messages,
15 | #users{
16 | margin: 0;
17 | overflow-y:scroll;
18 | }
19 | #users{
20 | width:200px;
21 | border-right:1px solid #ddd;
22 | }
23 | #users ul{
24 | margin:0;
25 | padding-left: 0;
26 | list-style: none;
27 | }
28 | #users ul li {
29 | background-color:rgba(0,0,0,.1);
30 | padding:.5em;
31 | margin: 2px;
32 | }
33 | #users ul li.me{
34 | background-color:rgba(25, 135, 84,.1);
35 | }
36 | #users ul small{
37 | display: block;
38 | color: #555;
39 | font-size: .875em;
40 | }
41 | #chat-message-form{
42 | bottom: 52px;
43 | padding:10px;
44 | }
45 | #status-message{
46 | border-width: 1px 0 0 0;
47 | margin:0;
48 | }
49 | /* view state */
50 | .navbar,
51 | main,
52 | #chat-message-form,
53 | #status-message{
54 | opacity: 1;
55 | transition:opacity .5s;
56 | }
57 | body.off .navbar,
58 | body.off main,
59 | body.off #chat-message-form,
60 | body.off #status-message{
61 | opacity: 0;
62 | }
63 | /* message styles */
64 | .message{
65 | background-color:#eee;
66 | margin:3px;
67 | border-radius: .4em;
68 | border:1px solid #ddd;
69 | overflow: hidden;
70 | }
71 | .message .sender{
72 | background-color: rgba(255,255,255,.7);
73 | padding: .3em;
74 | color:#888;
75 | font-size:.9em;
76 | }
77 | .message .content{
78 | padding: .3em;
79 | }
80 | .message.me{
81 | background-color: #b5d5f8;
82 | border-color: #a6c4e4;
83 | }
84 | .message.me .sender{
85 | color: #7C8DA5;
86 | }
87 | .message.system{
88 | background-color: #deefd8;
89 | border-color: #d6e7c6;
90 | }
91 | .message.welcome{
92 | padding: .3em;
93 | color: #664d03;
94 | background-color: #fff3cd;
95 | border-color: #ffecb5;
96 | }
97 | .message.private{
98 | background-color: #f8d7da;
99 | border-color: #f5c2c7;
100 | }
101 | .message.private .sender{
102 | color: #842029;
103 | }
104 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin:0;
3 | padding: 51px 0 0;
4 | }
5 | body, button, textarea {
6 | font: 400 14px/1.4em -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
7 | }
8 | pre{
9 | font: 400 14px/1.4em 'Operator Mono Ssm', Menlo, Consolas, monospace;
10 | border-radius: 0;
11 | background-color: transparent;
12 | margin:0;
13 | border:0;
14 | border-bottom:1px solid #ddd;
15 | padding:9.5px 220px 9.5px 9.5px;
16 | white-space: pre-wrap;
17 | min-height: 4em;
18 | display: flex;
19 | align-items: center;
20 | }
21 | * {
22 | box-sizing: border-box;
23 | }
24 | nav {
25 | background-color: #f8f8f8;
26 | border-bottom:1px solid #e7e7e7;
27 | padding:0 15px;
28 | position: fixed;
29 | width:100%;
30 | top:0;
31 | text-align: right;
32 | z-index: 1;
33 | }
34 | nav span,
35 | button {
36 | background-color:transparent;
37 | border: none;
38 | color: #777;
39 | cursor:pointer;
40 | line-height: 50px;
41 | outline: none;
42 | padding:0 15px;
43 | }
44 | nav span {
45 | display: inline-block;
46 | float: left;
47 | padding: 0;
48 | font-size: 1.2em;
49 | }
50 | .hide{
51 | display: none;
52 | }
53 |
54 | /* CONSOLE */
55 | #console {
56 | bottom:0;
57 | list-style: none;
58 | margin:0;
59 | padding:0;
60 | }
61 | #console li {
62 | position: relative;
63 | opacity: .5;
64 | transition: opacity .25s;
65 | }
66 | #console li:nth-child(even){
67 | background-color: #f5f5f5;
68 | }
69 | #console li:last-child { opacity: 1; }
70 | #console li strong {
71 | position: absolute;
72 | right:1em;
73 | top:1em;
74 | font-size: 1.2em;
75 | font-weight: 600;
76 | color: #888;
77 | }
78 |
79 | /* FORM MESSAGE */
80 | form#message{
81 | position: fixed;
82 | left:50%;
83 | top:50%;
84 | background-color: #f0f0f0;
85 | padding:10px;
86 | border:1px solid #ddd;
87 | box-shadow: 0 0 4px rgba(0,0,0,.5);
88 | transform: translate(-50%,-50%);
89 | }
90 | form#message textarea{
91 | border:1px solid #ddd;
92 | display: block;
93 | height:200px;
94 | width:400px;
95 | margin-bottom: 5px;
96 | padding: 10px;
97 | resize: none;
98 | }
99 | form#message button {
100 | width:100%;
101 | display: block;
102 | text-align: center;
103 | background-color: #ddd;
104 | }
105 | form#message button:active{
106 | background-color: #ccc;
107 | }
--------------------------------------------------------------------------------
/live-demos/assets/js/script.js:
--------------------------------------------------------------------------------
1 | // get our console and see if we are using firefox
2 | var _console = document.getElementById('console'),
3 | _scroll = navigator.userAgent.indexOf('Firefox') !== -1,
4 | // message controls
5 | openMessage = document.getElementById('domessage'),
6 | openMessage = document.getElementById('domessage'),
7 | sendMessage = document.getElementById('sendmessage'),
8 | messageForm = document.getElementById('message'),
9 | messageText = document.getElementById('messagetext');
10 |
11 | // clear our log
12 | document.getElementById('clearlog').addEventListener('click',function(e){
13 | e.preventDefault();
14 | _console.innerHTML = '';
15 | });
16 |
17 | // parse message when received thru websocket
18 | function parseMessage(message){
19 | var _date = new Date(),
20 | // format timestamp to a readable format
21 | _ts = _date.toLocaleDateString() + ' ' + _date.toLocaleTimeString(),
22 | // format message object into multiline readable string
23 | _data = JSON.stringify(message).replace(/,"/g,',\n "').replace('{','{\n ').replace('}','\n}'),
24 | // create new element
25 | _li = document.createElement('li');
26 | // set the text
27 | _li.innerHTML = `${_ts} ${_data} `;
28 | // append element to console
29 | _console.appendChild(_li);
30 |
31 | // extra
32 | if (document.body.scrollIntoView)
33 | document.body.scrollIntoView({block: "end", behavior: "smooth"});
34 | }
35 |
36 | // Event Listeners on Buttons and Textarea
37 | openMessage.addEventListener('click',doToggleMessageForm);
38 | sendMessage.addEventListener('click',doSendMessage);
39 | messageText.addEventListener('keyup',function(event){
40 | if(event.keyCode === 13 && event.shiftKey === true)
41 | doSendMessage();
42 | });
43 |
44 | // Toggle our message modal
45 | function doToggleMessageForm(){
46 | if ( messageForm.classList.contains('hide') === true){
47 | messageForm.classList.remove('hide');
48 | messageForm.message.value = '';
49 | openMessage.innerHTML = '× MESSAGE';
50 | } else {
51 | messageForm.classList.add('hide');
52 | openMessage.innerHTML = 'MESSAGE';
53 | }
54 | }
55 |
56 | // Send a message to the server => publish()
57 | function doSendMessage(){
58 | var theMessage = messageText.value.trim();
59 | if (theMessage !== ''){
60 | ws.publish('demo',theMessage);
61 | doToggleMessageForm();
62 | }
63 | }
--------------------------------------------------------------------------------
/live-demos/assets/css/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin:0;
3 | padding: 51px 0 0;
4 | }
5 | body, button, textarea {
6 | font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
7 | }
8 | pre{
9 | font: 400 1rem/1.42 'JetBrains Mono','Operator Mono Ssm', Menlo, Consolas, monospace;
10 | border-radius: 0;
11 | background-color: transparent;
12 | margin:0;
13 | border:0;
14 | border-bottom:1px solid #ddd;
15 | padding:9.5px;
16 | white-space: pre-wrap;
17 | }
18 | * {
19 | box-sizing: border-box;
20 | }
21 | #sig {
22 | position: fixed;
23 | bottom:0;
24 | width: 100vw;
25 | background-color: #eee;
26 | border-top:1px solid #ddd;
27 | padding: 1em;
28 | margin:0;
29 | text-align: center;
30 | }
31 | body table[class^=cfdump]{
32 | font-family: 'JetBrains Mono','Operator Mono Ssm',Menlo,Consolas, monospace;
33 | font-size: .875rem;
34 | line-height: 1.42;
35 | }
36 | nav:not(.navbar) {
37 | background-color: #f8f8f8;
38 | border-bottom:1px solid #e7e7e7;
39 | padding:0 15px;
40 | position: fixed;
41 | width:100%;
42 | top:0;
43 | text-align: right;
44 | z-index: 1;
45 | }
46 | nav:not(.navbar) span,
47 | nav:not(.navbar) button:not(.btn) {
48 | background-color:transparent;
49 | border: none;
50 | color: #777;
51 | cursor:pointer;
52 | line-height: 50px;
53 | outline: none;
54 | padding:0 15px;
55 | }
56 | .container-fluid button:not(.btn){
57 | border-left:1px solid #ddd;
58 | }
59 | nav:not(.navbar) span {
60 | display: inline-block;
61 | float: left;
62 | padding: 0;
63 | font-size: 1.2em;
64 | }
65 | nav.navbar{
66 | margin-bottom: 20px;
67 | }
68 | .hide{
69 | display: none;
70 | }
71 |
72 | /* CONSOLE */
73 | #console {
74 | bottom:0;
75 | list-style: none;
76 | margin:0;
77 | padding:0;
78 | }
79 | #console li {
80 | position: relative;
81 | opacity: .5;
82 | transition: opacity .25s;
83 | }
84 | #console li:nth-child(even){
85 | background-color: #f5f5f5;
86 | }
87 | #console li:last-child { opacity: 1; }
88 | #console li strong {
89 | position: absolute;
90 | right:1em;
91 | top:1em;
92 | font-size: 1.2em;
93 | font-weight: 600;
94 | color: #888;
95 | }
96 |
97 | /* FORM MESSAGE */
98 | form#message{
99 | position: fixed;
100 | left:50%;
101 | top:50%;
102 | background-color: #f0f0f0;
103 | padding:10px;
104 | border:1px solid #ddd;
105 | box-shadow: 0 0 4px rgba(0,0,0,.5);
106 | transform: translate(-50%,-50%);
107 | }
108 | form#message textarea{
109 | border:1px solid #ddd;
110 | display: block;
111 | height:200px;
112 | width:400px;
113 | margin-bottom: 5px;
114 | padding: 10px;
115 | resize: none;
116 | }
117 | form#message button {
118 | width:100%;
119 | display: block;
120 | text-align: center;
121 | background-color: #ddd;
122 | }
123 | form#message button:active{
124 | background-color: #ccc;
125 | }
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/assets/cfwebsocket.js:
--------------------------------------------------------------------------------
1 | function cfwebsocket (
2 | appName,
3 | channel,
4 | appStartPath,
5 | onMessage,
6 | onSubscribe,
7 | isProxy,
8 | wsPort,
9 | server,
10 | secure
11 | ){
12 | var me = this;
13 |
14 | this.server = server ? server : location.hostname;
15 | this.isSecure = secure ? secure : (location.protocol === 'https:' ? true : false);
16 | this.protocol = this.isSecure ? 'wss' : 'ws';
17 | this.port = wsPort ? wsPort : (isProxy ? '' : (this.isSecure ? 8543 : 8577));
18 | this.path = isProxy ? '/cfws/' : '/cfusion/cfusion';
19 | this.server = `${this.protocol}://${this.server}${this.port ? `:${this.port}` : ''}`;
20 | this.ws = new WebSocket(`${this.server}${this.path}`);
21 |
22 | // cf json properties for sending messages
23 | this.subscribe = {ns: 'coldfusion.websocket.channels', type: 'subscribe', channel: channel, appName: appName},
24 | this.unsubscribe = {ns: 'coldfusion.websocket.channels', type: 'unsubscribe', channel: channel, appName: appName},
25 | this.message = {ns: 'coldfusion.websocket.channels', type: 'publish', channel: channel, data:'', appName: appName};
26 |
27 | // WebSocket API Events
28 | this.ws.onopen = function(e){
29 | me.subscribeMe();
30 | };
31 |
32 | this.ws.onmessage = function(e){
33 | // when we receive a message pass it to the parseMessage function (the data piece of the object received)
34 | // console.log('onmessage',e);
35 | var data = JSON.parse(e.data);
36 | // looks like we are calling before CF has been executed
37 | if (data.code === -1 && data.msg.indexOf('is not running') !== -1){
38 | if (appStartPath){
39 | fetch(appStartPath,{
40 | method : 'GET'
41 | })
42 | .then( response => response.json() )
43 | .then( success => {
44 | if (success)
45 | me.subscribeMe();
46 | });
47 | }
48 | }
49 | else if (data.code === 0 && data.type === 'response' && data.reqType === 'subscribe' && data.msg ==='ok' && onSubscribe && typeof onSubscribe === 'object'){
50 | onSubscribe.subscribed();
51 | }
52 | // pass to our onMessage function we passed in
53 | if (onMessage && typeof onMessage === 'object'){
54 | onMessage.parseMessage(data);
55 | }
56 | };
57 |
58 | this.ws.onerror = function(e){
59 | // console.log('onerror',e);
60 | };
61 |
62 | this.ws.onclose = function(e){
63 | // console.log('close',e);
64 | };
65 |
66 | this.subscribeMe = function(){
67 | if (this.ws.readyState === this.ws.OPEN)
68 | this.ws.send(JSON.stringify(this.subscribe));
69 | }
70 |
71 | this.unsubscribeMe = function(){
72 | if (this.ws.readyState === this.ws.OPEN)
73 | this.ws.send(JSON.stringify(this.unsubscribe));
74 | }
75 |
76 | this.publish = function(value){
77 | var obj = Object.assign({},this.message),
78 | publish = value !== '';
79 | if (publish){
80 | obj.data = value;
81 | this.ws.send(JSON.stringify(obj));
82 | }
83 | return publish;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/swift-to-coldfusion/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // WebSockets
4 | //
5 | // Created by Giancarlo Gomez on 9/29/18.
6 | // Copyright © 2018 Giancarlo Gomez. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Starscream
11 |
12 |
13 | final class ViewController: UIViewController{
14 | var socket: WebSocket!
15 | var _subscribe = "{\"ns\":\"coldfusion.websocket.channels\",\"type\":\"subscribe\",\"channel\":\"demo\",\"appName\":\"websockets_demo1\"}";
16 |
17 | @IBOutlet weak var message: UILabel!
18 | @IBOutlet weak var messageBox: UITextField!
19 | @IBOutlet weak var sendButton: UIButton!
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 | var request = URLRequest(url: URL(string: "ws://localhost:8581/cfusion/cfusion")!)
24 | request.timeoutInterval = 5
25 | socket = WebSocket(request: request)
26 | socket.delegate = self
27 | socket.connect()
28 | }
29 |
30 | // MARK: Write Text Action
31 |
32 | @IBAction func write(_ sender: UIButton) {
33 | if (messageBox.text != ""){
34 | let message = "{\"ns\":\"coldfusion.websocket.channels\",\"type\":\"publish\",\"channel\":\"demo\",\"appName\":\"websockets_demo1\",\"data\":\"\(messageBox.text ?? "")\"}";
35 | sendMessage(message: message)
36 | messageBox.text = ""
37 | }
38 | }
39 |
40 | }
41 |
42 | // MARK: - WebSocketDelegate
43 | extension ViewController : WebSocketDelegate {
44 |
45 | func sendMessage(message: String){
46 | socket.write(string: message)
47 | }
48 |
49 | func websocketDidConnect(socket: WebSocketClient) {
50 | sendMessage(message: _subscribe)
51 | }
52 |
53 | func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
54 | if let e = error as? WSError {
55 | print("websocket is disconnected: \(e.message)")
56 | } else if let e = error {
57 | print("websocket is disconnected: \(e.localizedDescription)")
58 | } else {
59 | print("websocket disconnected")
60 | }
61 | message.text = "WebSocket Server Disconnected"
62 | }
63 |
64 | func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
65 | // let try and parse received JSON
66 | guard let data = text.data(using: .utf16),
67 | let jsonData = try? JSONSerialization.jsonObject(with: data),
68 | let jsonDict = jsonData as? [String: Any],
69 | let messageType = jsonDict["type"] as? String else {
70 | return
71 | }
72 | // If we are receiving a data type then lets update the text message
73 | if messageType == "data",
74 | let messageData = jsonDict["data"] as? String{
75 | message.text = messageData
76 | }
77 | // when we subscribe
78 | else if messageType == "response",
79 | let reqType = jsonDict["reqType"] as? String{
80 | if (reqType == "subscribe"){
81 | message.text = "WebSocket Server Connected and Subscribed"
82 | }
83 | }
84 | // log the full response
85 | print(text);
86 | }
87 |
88 | func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
89 | print("Received data: \(data.count)")
90 | }
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Web Animations `@angular/platform-browser/animations`
51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
53 | **/
54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
55 |
56 | /**
57 | * By default, zone.js will patch all possible macroTask and DomEvents
58 | * user can disable parts of macroTask/DomEvents patch by setting following flags
59 | */
60 |
61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
64 |
65 | /*
66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
68 | */
69 | // (window as any).__Zone_enable_cross_context_check = true;
70 |
71 | /***************************************************************************************************
72 | * Zone JS is required by default for Angular itself.
73 | */
74 | import 'zone.js/dist/zone'; // Included with Angular CLI.
75 |
76 |
77 |
78 | /***************************************************************************************************
79 | * APPLICATION IMPORTS
80 | */
81 |
--------------------------------------------------------------------------------
/angular-to-coldfusion/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-redundant-jsdoc": true,
69 | "no-shadowed-variable": true,
70 | "no-string-literal": false,
71 | "no-string-throw": true,
72 | "no-switch-case-fall-through": true,
73 | "no-trailing-whitespace": true,
74 | "no-unnecessary-initializer": true,
75 | "no-unused-expression": true,
76 | "no-use-before-declare": true,
77 | "no-var-keyword": true,
78 | "object-literal-sort-keys": false,
79 | "one-line": [
80 | true,
81 | "check-open-brace",
82 | "check-catch",
83 | "check-else",
84 | "check-whitespace"
85 | ],
86 | "prefer-const": true,
87 | "quotemark": [
88 | true,
89 | "single"
90 | ],
91 | "radix": true,
92 | "semicolon": [
93 | true,
94 | "always"
95 | ],
96 | "triple-equals": [
97 | true,
98 | "allow-null-check"
99 | ],
100 | "typedef-whitespace": [
101 | true,
102 | {
103 | "call-signature": "nospace",
104 | "index-signature": "nospace",
105 | "parameter": "nospace",
106 | "property-declaration": "nospace",
107 | "variable-declaration": "nospace"
108 | }
109 | ],
110 | "unified-signatures": true,
111 | "variable-name": false,
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ],
120 | "no-output-on-prefix": true,
121 | "use-input-property-decorator": true,
122 | "use-output-property-decorator": true,
123 | "use-host-property-decorator": true,
124 | "no-input-rename": true,
125 | "no-output-rename": true,
126 | "use-life-cycle-interface": true,
127 | "use-pipe-transform-interface": true,
128 | "component-class-suffix": true,
129 | "directive-class-suffix": true
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/chat/index.cfm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | WebSocket Chat
7 |
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
23 | Chat App
24 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
55 |
56 |
57 |
58 | We are good
59 |
60 |
61 |
62 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
92 |
93 |
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/form.cfm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
15 |
16 |
17 | Message Post Form
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Post as JSON
30 |
31 |
32 |
33 |
34 |
35 | PROCESSING
36 |
37 |
38 | SUBMIT
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
82 |
83 |
--------------------------------------------------------------------------------
/html-to-coldfusion/cfwebsocket.js:
--------------------------------------------------------------------------------
1 | function cfwebsocket (
2 | appName,
3 | channel,
4 | appStartPath,
5 | onMessage,
6 | onSubscribe,
7 | isProxy,
8 | wsPort,
9 | server,
10 | secure,
11 | customOptions
12 | ){
13 | var me = this;
14 | var theCustomOptions = customOptions || {};
15 |
16 | this.server = server ? server : location.hostname;
17 | this.isSecure = secure ? secure : (location.protocol === 'https:' ? true : false);
18 | this.protocol = this.isSecure ? 'wss' : 'ws';
19 | this.port = wsPort ? wsPort : (isProxy ? '' : (this.isSecure ? 8543 : 8577));
20 | this.path = isProxy ? '/cfws/' : '/cfusion/cfusion';
21 | this.server = `${this.protocol}://${this.server}${this.port ? `:${this.port}` : ''}`;
22 | this.ws = new WebSocket(`${this.server}${this.path}`);
23 |
24 | // cf json properties for sending messages
25 | this.subscribe = {ns: 'coldfusion.websocket.channels', type: 'subscribe', channel: channel, appName: appName,customOptions:theCustomOptions},
26 | this.unsubscribe = {ns: 'coldfusion.websocket.channels', type: 'unsubscribe', channel: channel, appName: appName},
27 | this.message = {ns: 'coldfusion.websocket.channels', type: 'publish', channel: channel, data:'', appName: appName,customOptions:theCustomOptions};
28 |
29 | // WebSocket API Events
30 | this.ws.onopen = function(e){
31 | me.subscribeMe();
32 | };
33 |
34 | this.ws.onmessage = function(e){
35 | // when we receive a message pass it to the parseMessage function (the data piece of the object received)
36 | var data = JSON.parse(e.data);
37 | // looks like we are calling before CF has been executed
38 | if (data.code === -1 && data.msg.indexOf('is not running') !== -1){
39 | if (appStartPath){
40 | fetch(appStartPath,{
41 | method : 'GET'
42 | })
43 | .then( response => response.json() )
44 | .then( success => {
45 | if (success)
46 | me.subscribeMe();
47 | });
48 | }
49 | }
50 | else if (data.code === 0 && data.type === 'response' && data.reqType === 'subscribe' && data.msg ==='ok' && onSubscribe && typeof onSubscribe === 'function'){
51 | onSubscribe();
52 | }
53 | // pass to our onMessage function we passed in
54 | if (onMessage && typeof onMessage === 'function'){
55 | onMessage(data);
56 | }
57 | };
58 |
59 | this.ws.onerror = function(e){
60 | // console.log('onerror',e);
61 | };
62 |
63 | this.ws.onclose = function(e){
64 | // console.log('close',e);
65 | };
66 |
67 | this.subscribeMe = function(){
68 | if (this.ws.readyState === this.ws.OPEN)
69 | this.ws.send(JSON.stringify(this.subscribe));
70 | }
71 |
72 | this.unsubscribeMe = function(){
73 | if (this.ws.readyState === this.ws.OPEN)
74 | this.ws.send(JSON.stringify(this.unsubscribe));
75 | }
76 |
77 | this.publish = function(value,customOptions){
78 | var theCustomOptions = customOptions || {};
79 | var obj = Object.assign({},this.message),
80 | publish = value !== '';
81 | if (publish){
82 | obj.data = value;
83 | // add custom options
84 | for (let key of Object.keys(theCustomOptions))
85 | obj.customOptions[key] = theCustomOptions[key];
86 | // send message
87 | this.ws.send(JSON.stringify(obj));
88 | }
89 | return publish;
90 | }
91 | }
--------------------------------------------------------------------------------
/other-demo-files/socket.io/server.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { createServer } from 'node:http';
3 | import { fileURLToPath } from 'node:url';
4 | import { dirname, join } from 'node:path';
5 | import { Server } from 'socket.io';
6 |
7 | const app = express();
8 | const server = createServer(app);
9 | const io = new Server(server);
10 |
11 | const __dirname = dirname(fileURLToPath(import.meta.url));
12 |
13 | app.use( express.static('public') );
14 | app.use( '/publish', express.json() );
15 |
16 | // The Simple WebSocket Server
17 | io.on('connection', socket => {
18 |
19 | const channels = socket.handshake.query.channels ?
20 | socket.handshake.query.channels.toString().split(',') :
21 | [];
22 |
23 | console.log( `user connected - ${socket.id}`, channels );
24 |
25 | const subscribeTo = channels => {
26 | channels.forEach( channel => {
27 | socket.join( channel );
28 | console.log( `subscribing ${socket.id} to channel ${channel}`);
29 | });
30 | }
31 |
32 | const unsubscribeFrom = channels => {
33 | channels.forEach( channel => {
34 | socket.leave( channel );
35 | console.log( `unsubscribing ${socket.id} from channel ${channel}`);
36 | });
37 | }
38 |
39 | // join each channel passed (room)
40 | subscribeTo( channels );
41 |
42 | socket.on('disconnect', () => {
43 | console.log( `user disconnected - ${socket.id}` );
44 | });
45 |
46 | socket.on('subscribe', channels => {
47 | channels = channels ? channels.toString().split(',') : [];
48 | subscribeTo( channels );
49 | });
50 |
51 | socket.on('unsubscribe', channels => {
52 | channels = channels ? channels.toString().split(',') : [];
53 | unsubscribeFrom( channels );
54 | });
55 |
56 | socket.on('publish', data => {
57 | console.log( 'publish ', data );
58 |
59 | if ( data.constructor !== Object ) {
60 | data = {
61 | message : data
62 | };
63 | }
64 |
65 | // send back the publisherId
66 | data.publisherId = socket.id;
67 |
68 | // emit the message to the other subscribers
69 | // use socket.broadcast to send to all except the sender
70 | if ( data.channel )
71 | io.to( data.channel ).emit( 'message', data );
72 | else
73 | io.emit( 'message', data );
74 | });
75 | });
76 |
77 | // publish via post
78 | app.post('/publish', ( req, res ) => {
79 | console.log( `[200] ${req.method} ${req.url} via ${req.get('user-agent')} ${req.get('host')} ${req.ip}`);
80 |
81 | try {
82 | if ( !req.body.message ) {
83 | return res.status(400).json({'error':'message is required'});
84 | }
85 |
86 | req.body.publisherId = 0;
87 |
88 | // if post to channel or global
89 | if ( req.body.channel )
90 | io.to( req.body.channel ).emit( 'message', req.body );
91 | else
92 | io.emit( 'message',req.body );
93 |
94 | res.json( { 'success':true } );
95 | }
96 | catch(e){
97 | res.json( { 'success':false } );
98 | }
99 | });
100 |
101 | // get channel subscriber count and meta
102 | app.get('/subscribers/:channel', async ( req, res ) => {
103 | const sockets = ( await io.in( req.params.channel ).fetchSockets() ).map( socket => {
104 | return {
105 | id : socket.id ,
106 | date : socket.handshake.time
107 | };
108 | });
109 | res.json( { 'success':true, 'count':sockets.length, sockets } );
110 | });
111 |
112 | server.listen( 3000, () => {
113 | console.log('server running at http://localhost:3000');
114 | });
--------------------------------------------------------------------------------
/angular-to-coldfusion/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "websockets": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {},
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/websockets",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": "src/polyfills.ts",
20 | "tsConfig": "src/tsconfig.app.json",
21 | "assets": [
22 | "src/favicon.ico",
23 | "src/assets"
24 | ],
25 | "styles": [
26 | "src/styles.css"
27 | ],
28 | "scripts": []
29 | },
30 | "configurations": {
31 | "production": {
32 | "fileReplacements": [
33 | {
34 | "replace": "src/environments/environment.ts",
35 | "with": "src/environments/environment.prod.ts"
36 | }
37 | ],
38 | "optimization": true,
39 | "outputHashing": "all",
40 | "sourceMap": false,
41 | "extractCss": true,
42 | "namedChunks": false,
43 | "aot": true,
44 | "extractLicenses": true,
45 | "vendorChunk": false,
46 | "buildOptimizer": true
47 | }
48 | }
49 | },
50 | "serve": {
51 | "builder": "@angular-devkit/build-angular:dev-server",
52 | "options": {
53 | "browserTarget": "websockets:build"
54 | },
55 | "configurations": {
56 | "production": {
57 | "browserTarget": "websockets:build:production"
58 | }
59 | }
60 | },
61 | "extract-i18n": {
62 | "builder": "@angular-devkit/build-angular:extract-i18n",
63 | "options": {
64 | "browserTarget": "websockets:build"
65 | }
66 | },
67 | "test": {
68 | "builder": "@angular-devkit/build-angular:karma",
69 | "options": {
70 | "main": "src/test.ts",
71 | "polyfills": "src/polyfills.ts",
72 | "tsConfig": "src/tsconfig.spec.json",
73 | "karmaConfig": "src/karma.conf.js",
74 | "styles": [
75 | "src/styles.css"
76 | ],
77 | "scripts": [],
78 | "assets": [
79 | "src/favicon.ico",
80 | "src/assets"
81 | ]
82 | }
83 | },
84 | "lint": {
85 | "builder": "@angular-devkit/build-angular:tslint",
86 | "options": {
87 | "tsConfig": [
88 | "src/tsconfig.app.json",
89 | "src/tsconfig.spec.json"
90 | ],
91 | "exclude": [
92 | "**/node_modules/**"
93 | ]
94 | }
95 | }
96 | }
97 | },
98 | "websockets-e2e": {
99 | "root": "e2e/",
100 | "projectType": "application",
101 | "architect": {
102 | "e2e": {
103 | "builder": "@angular-devkit/build-angular:protractor",
104 | "options": {
105 | "protractorConfig": "e2e/protractor.conf.js",
106 | "devServerTarget": "websockets:serve"
107 | },
108 | "configurations": {
109 | "production": {
110 | "devServerTarget": "websockets:serve:production"
111 | }
112 | }
113 | },
114 | "lint": {
115 | "builder": "@angular-devkit/build-angular:tslint",
116 | "options": {
117 | "tsConfig": "e2e/tsconfig.e2e.json",
118 | "exclude": [
119 | "**/node_modules/**"
120 | ]
121 | }
122 | }
123 | }
124 | }
125 | },
126 | "defaultProject": "websockets"
127 | }
--------------------------------------------------------------------------------
/chat/ChatListener.cfc:
--------------------------------------------------------------------------------
1 | component extends="CFIDE.websocket.ChannelListener" {
2 |
3 | public boolean function allowSubscribe( struct subscriberInfo ) {
4 | //I am crucial. You must use me. Or else.
5 | if( !arguments.subscriberInfo.connectionInfo.authenticated ) return false;
6 | //I'm an optional check just to be extra secure
7 | if( !structKeyExists(arguments.subscriberInfo,"username") ) return false;
8 | // send to all connected clients that this new subscriber has joined
9 | wsPublish( "chat",arguments.subscriberInfo.username & " has joined the chat room, say hello ... " );
10 | // in a thread send to all clients (including just connected the full connected list)
11 | thread name="subscribers_#createUUID()#" action="run"{
12 | returnConnectedUsers();
13 | }
14 | return true;
15 | }
16 |
17 | public boolean function allowPublish( struct publisherInfo ) {
18 | return arguments.publisherInfo.connectionInfo.authenticated;
19 | }
20 |
21 | /**
22 | * Transforms the message into a struct that is used by beforeSendMessage
23 | * The struct containst the publisher's connection info and a to parameter
24 | * sent by the publisher when sending a private message
25 | * ( review /assets/chat.js to see how it is done )
26 | */
27 | public any function beforePublish( any message, struct publisherInfo ){
28 | cflock( scope="application", timeout="10", type="exclusive" ){
29 | application.publishedMessages++;
30 | }
31 | var messageData = {
32 | message : arguments.message,
33 | publisherInfo : arguments.publisherInfo.connectionInfo,
34 | to : arguments.publisherInfo.to ?: {}
35 | };
36 | return messageData;
37 | }
38 |
39 | public any function beforeSendMessage( any message, struct subscriberInfo ){
40 | var className = "message";
41 | var isMe = false;
42 | var isPrivateMessage = false;
43 | var messageText = "";
44 | var privateMessageText = "";
45 |
46 | if ( isStruct(arguments.message.message) ){
47 | messageText = arguments.message.message;
48 | }
49 | else if ( arguments.message.message == "FORCE-RECONNECT" ){
50 | messageText = arguments.message.message;
51 | }
52 | else if ( structKeyExists(arguments.message.publisherInfo,"username") ){
53 | isMe = !compare(arguments.subscriberInfo.connectioninfo.clientid,arguments.message.publisherInfo.clientid);
54 |
55 | isPrivateMessage = !structIsEmpty( arguments.message.to ?: {} ) &&
56 | structKeyExists(arguments.message.to,"id") &&
57 | structKeyExists(arguments.message.to,"username");
58 |
59 | className &= ( isMe ? " me" : "" ) &
60 | ( isPrivateMessage ? " private" : "" );
61 |
62 | if ( isPrivateMessage ){
63 | privateMessageText = "Private
64 | #( isMe ? "to " & arguments.message.to.username : "from " & arguments.message.publisherInfo.username )#
65 | ";
66 | }
67 | messageText = "
68 |
69 | #arguments.message.publisherInfo.username#
70 | #privateMessageText#
71 |
72 |
#arguments.message.message#
73 |
";
74 | }
75 | else {
76 | messageText = "
77 |
SYSTEM
78 |
#arguments.message.message#
79 |
";
80 | }
81 | return messageText;
82 | }
83 |
84 | public any function canSendMessage(any message, struct subscriberInfo, struct publisherInfo) {
85 | var canSend = true;
86 | var privateToData = {};
87 |
88 | // system messages skip this check as they are meant for all
89 | // which is done by making sure that publisherInfo.connectionInfo.clientID
90 | // exists as the system message does not have one
91 | if ( !isNull(arguments.publisherInfo.connectionInfo.clientID) ){
92 | privateToData = !isNull(message.to) && isStruct(message.to) ? message.to : {};
93 | // if not private
94 | // or myself
95 | // or message is for me
96 | canSend = ( structIsEmpty( privateToData ) ) ||
97 | arguments.publisherInfo.connectionInfo.clientID == arguments.subscriberInfo.connectionInfo.clientID ||
98 | privateToData.id == arguments.subscriberInfo.connectionInfo.clientID ;
99 | }
100 | return canSend;
101 | }
102 |
103 | public function afterUnsubscribe( struct subscriberInfo ){
104 | wsPublish("chat",arguments.subscriberInfo.username & " has left the chat room");
105 | returnConnectedUsers();
106 | }
107 |
108 | /**
109 | * Publishes array of connected users for UI processing on client
110 | */
111 | private function returnConnectedUsers(){
112 | var _clients = wsGetSubscribers("chat");
113 | var _client = {};
114 | var clients = [];
115 |
116 | // sort the array
117 | arraySort(_clients,function (e1, e2){
118 | return compare(e1.subscriberInfo.username, e2.subscriberInfo.username);
119 | });
120 |
121 | for( _client in _clients ){
122 | clients.append({
123 | "id" : _client.clientid,
124 | "username" : _client.subscriberInfo.username,
125 | "city" : trim( _client.subscriberInfo.clientInfo.city ?: "" ),
126 | "region" : trim( _client.subscriberInfo.clientInfo.region ?: "" )
127 | });
128 | }
129 |
130 | wsPublish( "chat",{"type":"clients","data":clients} );
131 | }
132 | }
--------------------------------------------------------------------------------
/other-demo-files/socket.io_site/socketio.client.js:
--------------------------------------------------------------------------------
1 | var channels = ['demo','global'],
2 | socket = {},
3 | server = {},
4 | ws = {CONNECTED:0,NOTIFY:0},
5 | _console = document.getElementById('console'),
6 | _clearlog = document.getElementById('clearlog'),
7 | _scroll = navigator.userAgent.indexOf('Firefox') !== -1,
8 | // message controls
9 | openMessage = document.getElementById('domessage'),
10 | openMessage = document.getElementById('domessage'),
11 | sendMessage = document.getElementById('sendmessage'),
12 | messageForm = document.getElementById('message'),
13 | messageText = document.getElementById('messagetext');
14 |
15 | // let's fetch our info
16 | fetch('./info/',{type:'GET'})
17 | .then( response => response.json() )
18 | .then( response => {
19 | if(!response.success)
20 | window.alert(response.message);
21 | else
22 | callSocketIO(response.data.socketio_server,response.data.socketio_port);
23 | });
24 |
25 |
26 | function callSocketIO(server,port){
27 | // go an connect
28 | try{
29 | socket = io(`${server}:${port}/`,{
30 | query : 'channels=' + channels.join(',')
31 | });
32 | setupSocketIO();
33 | }
34 | catch(e){
35 | window.alert('We could not connect to the socket IO instance, make sure you have started it.');
36 | }
37 | }
38 |
39 | /* ==========================================================================
40 | SOCKET EVENTS
41 | ========================================================================== */
42 | function setupSocketIO(){
43 | socket.on('connect',function(){
44 | ws.CONNECTED = 1;
45 | parseMessage('connected to ' + channels.join(','));
46 | });
47 |
48 | socket.on('disconnect',function(data){
49 | ws.CONNECTED = 0;
50 | // set timeout to avoid opening alert if we refresh
51 | window.setTimeout(notifyDisconnect, 500);
52 | });
53 |
54 | socket.on('reconnect',function(data){
55 | ws.CONNECTED = 1;
56 | ws.NOTIFY = 0;
57 | parseMessage('We are back on');
58 | });
59 |
60 | socket.on('message',function(data){
61 | parseMessage(data);
62 | // global reload
63 | if (data.message && data.message === 'RELOAD'){
64 | parseMessage('I will reload in 3 seconds');
65 | window.setTimeout(function(){
66 | window.location.reload()
67 | },3000);
68 | }
69 | });
70 | }
71 |
72 | /* ==========================================================================
73 | GLOBAL LISTENERS AND FUNCTIONS
74 | ========================================================================== */
75 | _clearlog.addEventListener("click",function(e){
76 | e.preventDefault();
77 | _console.innerHTML = '';
78 | });
79 |
80 | function send(channel,message){
81 | parseMessage(message);
82 | socket.emit('message',{channel : channel, message :message});
83 | }
84 |
85 | function notifyDisconnect(){
86 | if (ws.CONNECTED === 0 && ws.NOTIFY === 0){
87 | parseMessage('We have been disconnected');
88 | ws.NOTIFY++;
89 | }
90 | }
91 |
92 | function parseMessage(message){
93 | var _date = new Date(),
94 | // format timestamp to a readable format
95 | _ts = _date.toLocaleDateString() + ' ' + _date.toLocaleTimeString(),
96 | // format message object into multiline readable string
97 | _data = JSON.stringify(message).replace(/,"/g,',\n "').replace('{','{\n ').replace('}','\n}'),
98 | // create new element
99 | _li = document.createElement('li');
100 | // set the text
101 | _li.innerHTML = `${_ts} ${_data} `;
102 | // append element to console
103 | _console.appendChild(_li);
104 |
105 | // extra
106 | // if firefox use internal smooth functionality of scrollIntoView - if not animate using function
107 | if (document.body.scrollIntoView)
108 | document.body.scrollIntoView({block: "end", behavior: "smooth"});
109 | }
110 |
111 | // Event Listeners on Buttons and Textarea
112 | openMessage.addEventListener('click',doToggleMessageForm);
113 | sendMessage.addEventListener('click',doSendMessage);
114 | messageText.addEventListener('keyup',function(event){
115 | if(event.keyCode === 13 && event.shiftKey === true)
116 | doSendMessage();
117 | });
118 |
119 | // Toggle our message modal
120 | function doToggleMessageForm(){
121 | if ( messageForm.classList.contains('hide') === true){
122 | messageForm.classList.remove('hide');
123 | messageForm.message.value = '';
124 | } else {
125 | messageForm.classList.add('hide');
126 | }
127 | }
128 |
129 | // Send a message to the server => publish()
130 | function doSendMessage(){
131 | var theMessage = messageText.value.trim();
132 | if (theMessage !== ''){
133 | send('demo',theMessage);
134 | doToggleMessageForm();
135 | }
136 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Realtime with WebSockets - Demo Code
2 | __Presented By [Giancarlo Gomez](https://github.com/GiancarloGomez)__
3 |
4 | This is the demo code used in my WebSockets presentation.
5 |
6 | * __angular-to-coldfusion__ The Angular Example I gave at CFSUMMIT2018. View README in directory on how to run.
7 | * __assets__ The CSS and JavaScript files that are shared by the live demo UIs
8 | * __css__
9 | * __js__
10 | * __chat__ The Chat demo
11 | * __html-to-coldfusion__ The HTML to ColdFusion WebSocket example.
12 | * __live-demos__
13 | * __demo-01__ Minimal requirements
14 | * __demo-02__ AdvancedSocket Example
15 | * __other-demo-files__
16 | * __socket.io__ ( [node.js required](https://nodejs.org/) )
17 | This is the code for the [socket.io](http://socket.io/) server that will run on port 8080.
18 | Remember to do an ``npm install`` after downloading and then run ``node server.js`` to start.
19 | * __socket.io_site__
20 | The example site using the socket.io server to connect, send and receive messages both from the client and the server.
21 | This should be mapped to its own site or ran as a separate server using CommandBox.
22 | * __swift-to-coldfusion__ The native iOS app example shown in CFSUMMIT2018. Getting this running is a bit more cumbersome and requires working with the [Starscream](https://github.com/daltoniam/Starscream) libray for using WebSockets in Swift. The code here includes the Main Storyboard and the main View Controller for reference.
23 |
24 | ## Running Demos ( CommandBox, Socket.IO and )
25 | You can easily run the demos using CommandBox. Simply clone this repo to your local machine
26 | and execute the ``box start`` command at the ``root`` folder for the main app and in the ``other-demo-files\socket.io_site``
27 | folder for the demo of using Socket.IO instead of ColdFusion's WebSockets.
28 |
29 | __The default server.json is set to use ColdFusion 2021. If you use this one make sure
30 | to install the websocket package using cfpm and/or thru the administrator. If you choose to
31 | do it from the CLI simply type the following while in CommandBox and/or the Server's bin folder__
32 |
33 | ```bash
34 | cfpm install websocket
35 | ```
36 |
37 | > Lucee server is not supported for these examples. If using Lucee, I recommend looking at the socket.io example
38 | or using an existing service for WebSocket communication.
39 |
40 | If you want to run other version's other than 2021 you can do so by starting the server as follows:
41 | ```bash
42 | # ColdFusion 2018
43 | box server start serverConfigFile=server.2018.json
44 | # ColdFusion 2016
45 | box server start serverConfigFile=server.2016.json
46 | # ColdFusion 11 ( make sure to point to supported JDK if system version not supported )
47 | box server start serverConfigFile=server.11.json
48 | ```
49 |
50 | __Application.cfc Settings__
51 | To run this code you must set 2 request variables in ``Application.cfc``, these variables are for the site ran from ``other-demo-files\socket.io_site``
52 |
53 | ```javascript
54 | request.socketio_server = "http://127.0.0.1:51095/";
55 | request.socketio_broadcast = "http://127.0.0.1:51095/broadcast/";
56 | ```
57 |
58 | __Socket.IO__
59 | As stated above you will need to run ``node server.js`` command at the ``other-demo-files\socket.io`` folder.
60 |
61 | __HTML To ColdFusion Demo__
62 | Once you have configured both of your servers, make sure to go into the ``html-to-coldfusion\script.js`` and set
63 | the values to the WebSocket Port used by ColdFusion and the Path and Port to your server.
64 |
65 | ```javascript
66 | var cfws = new cfwebsocket(
67 | // appName for the server we are interacting => live-demos\demo-01
68 | 'websockets_demo1',
69 | // the name of the channel we are subscribing to
70 | 'demo',
71 | // the file to run in case a ColdFusion request has not execute yet
72 | 'http://localhost:50320/live-demos/demo-01/runme/',
73 | // the global function to execute to parse a message received
74 | parseMessage,
75 | // the global function to execute when we subscribe
76 | subscribed,
77 | // if we are using the proxy method
78 | false,
79 | // the Port used by ColdFusion for WebSockets ( if not included it uses defaults defined in cfwebsocket.js)
80 | 8581,
81 | // the server we are connecting to
82 | 'localhost',
83 | // if the connection should be secure
84 | false
85 | );
86 | ```
87 |
88 | __Angular To ColdFusion Demo__
89 | Same as above, simply open the `angular-to-coldfusion\src\app\app.component.ts` file and set the attributes based on your ColdFusion instance.
90 | ```javascript
91 | this.cfwebsocket = new window['cfwebsocket'](
92 | // appName for the server we are interacting => live-demos\demo-01
93 | 'websockets_demo1',
94 | // the name of the channel we are subscribing to
95 | 'demo',
96 | // the file to run in case a ColdFusion request has not execute yet
97 | 'http://localhost:50320/live-demos/demo-01/runme/',
98 | // the global function to execute to parse a message received
99 | // we pass entire object and allow defaults to pass back to correct function
100 | this,
101 | // the global function to execute when we subscribe
102 | // we pass entire object and allow defaults to pass back to correct function
103 | this,
104 | // if we are using the proxy method
105 | false,
106 | // the Port used by ColdFusion for WebSockets ( if not included it uses defaults defined in cfwebsocket.js)
107 | 8581,
108 | // the server we are connecting to
109 | 'localhost',
110 | // if the connection should be secure
111 | false
112 | )
113 | ```
114 |
115 | ## Conferences
116 | * NCDevCon 2016
117 | * Dev.Objective 2016
118 | * ColdFusion Summit 2016
119 | * ColdFusion Summit 2017
120 | * ColdFusion Summit 2018
121 |
122 | ## Slides
123 | All slides can be found in the [ColdFusion Realtime With WebSockets Repo](https://github.com/GiancarloGomez/ColdFusion-Realtime-With-WebSockets "ColdFusion Realtime With WebSockets Repo")
124 |
--------------------------------------------------------------------------------
/swift-to-coldfusion/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/index.cfm:
--------------------------------------------------------------------------------
1 |
2 | if( !structKeyExists(request,"socketio_server") )
3 | throw("You must set the full URL to the Socket.IO Console Site to request.socketio_server - review instructions in Application.onRequestStart()");
4 | if( !structKeyExists(request,"socketio_broadcast") )
5 | throw("You must set the full URL to the Socket.IO Broadcast Handler to request.socketio_broadcast - review instructions in Application.onRequestStart()");
6 | links = [
7 | {
8 | title : "DEMOS",
9 | description : "",
10 | nav : [
11 | {link:"/live-demos/demo-00/",title:"Minimal"},
12 | {link:"/live-demos/demo-00/restricted/",title:"Restricted"},
13 | {link:"/live-demos/demo-01/",title:"Simple"},
14 | {link:"/live-demos/demo-01/advanced/",title:"Advanced"},
15 | {link:"/live-demos/broadcast/",title:"Broadcast",ajax:true},
16 | {link:"/live-demos/subscribers/",title:"Subscribers",modal:true},
17 | {link:"/live-demos/console/",title:"Console"}
18 | ]
19 | },
20 | // {
21 | // title : "DEMO 2",
22 | // description : "ADVANCED",
23 | // nav : [
24 | // {link:"/live-demos/demo-02/",title:"Simple Console"},
25 | // {link:"/live-demos/demo-02/advanced/",title:"Advanced Console"},
26 | // {link:"/live-demos/demo-02/broadcast/",title:"Broadcast",ajax:true},
27 | // {link:"/live-demos/demo-02/subscribers/",title:"Subscribers",modal:true}
28 | // ]
29 | // },
30 | {
31 | title : "CHAT",
32 | description : "",
33 | nav : [
34 | {link:"/chat/",title:"Chat App"},
35 | {link:"/chat/broadcast/",title:"Broadcast",ajax:true},
36 | {link:"/chat/subscribers/",title:"Subscribers",modal:true},
37 | {link:"/chat/console/",title:"Console"}
38 | ]
39 | }
40 | // {
41 | // title : "OTHER",
42 | // description : "",
43 | // nav : [
44 | // {link:"/html-to-coldfusion/",title:"HTML to ColdFusion"},
45 | // {link:request.socketio_server,title:"Socket.IO Console"},
46 | // {link:request.socketio_broadcast,title:"Socket.IO Broadcast",ajax:true}
47 | // ]
48 | // }
49 | ];
50 |
51 |
52 |
53 |
54 |
55 |
56 | WebSockets 201
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | WebSockets 201
68 |
69 |
70 |
71 |
72 |
#link.title#
73 |
74 |
75 | #nav.title#
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Giancarlo Gomez
85 | Fuse Developments •
86 | CrossTrackr
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
108 |
109 |
110 |
111 |
112 |
174 |
175 |
--------------------------------------------------------------------------------
/chat/assets/chat.js:
--------------------------------------------------------------------------------
1 | const Chat = {
2 | connected: false,
3 | clients: [],
4 | channel: null,
5 | username: null,
6 | clientID: null,
7 | ui: {
8 | body: document.body,
9 | loginModal: null,
10 | usernameField: null,
11 | usernameErrorText: null,
12 | usernameFormGroup: null,
13 | clearMessages: null,
14 | leaveRoom: null,
15 | messageToField: null,
16 | messageField: null,
17 | messageForm: null,
18 | messages: null,
19 | users: null
20 |
21 | },
22 | init : function() {
23 | const _this = this,
24 | _ui = this.ui;
25 | // setup modal and show
26 | _ui.loginModal = $('#login')
27 | .modal({
28 | backdrop : 'static',
29 | keyboard : false
30 | })
31 | .modal('show');
32 | // setup rest of UI
33 | _ui.usernameField = _ui.loginModal.find('input');
34 | _ui.usernameErrorText = _ui.loginModal.find('.invalid-feedback');
35 | _ui.usernameFormGroup = _ui.usernameField.parents('.input-group');
36 | _ui.messageForm = $('#messageFrm');
37 | _ui.messageToField = $('#message-to');
38 | _ui.messageField = $('#message');
39 | _ui.leaveRoom = $('#leave-room');
40 | _ui.clearMessages = document.getElementById('clear-messages')
41 | _ui.messages = document.getElementById('messages');
42 | _ui.users = document.getElementById('users');
43 |
44 | // listeners
45 |
46 | // focus on login field
47 | _ui.loginModal.on('shown.bs.modal',function(e){
48 | _ui.usernameField.val('').focus();
49 | _this.clearMessages();
50 | _ui.users.innerHTML = '';
51 | });
52 |
53 | // username blur
54 | _ui.usernameField.on('focus',function(){
55 | _ui.usernameField.removeClass('is-invalid');
56 | _ui.usernameErrorText.html('');
57 | });
58 |
59 | // clear our log
60 | _ui.clearMessages.addEventListener('click',_this.clearMessages.bind(_this));
61 |
62 | // join room
63 | _ui.loginModal.find('#loginFrm').submit(_this.login.bind(_this));
64 |
65 | // leave room
66 | _ui.leaveRoom.on('click',_this.logout.bind(_this));
67 |
68 | // send message
69 | _ui.messageForm.submit(_this.sendMessage.bind(_this));
70 | },
71 | clearMessages: function( event ){
72 | if ( event )
73 | event.preventDefault();
74 | this.ui.messages.innerHTML = '';
75 | },
76 | login: function ( event ){
77 | if ( event ){
78 | event.preventDefault();
79 | let username = this.ui.usernameField.val().trim();
80 | if ( username === '' ){
81 | this.ui.usernameField.addClass('is-invalid');
82 | this.ui.usernameErrorText.html('Please enter a username to login');
83 | }
84 | else {
85 | // set the username into our Client Info
86 | AdvancedSocket.clientInfo.username = username;
87 | // Authenticate User
88 | // On Success => AdvancedSocket.connected
89 | // On Fail => AdvancedSocket.onError
90 | ws.authenticate( username, '' );
91 | }
92 | }
93 | },
94 | loginError: function(){
95 | this.ui.usernameField.addClass('is-invalid').val('');
96 | this.ui.usernameErrorText.html('Invalid User, please try again');
97 | },
98 | logout: function( event ){
99 | if ( event ){
100 | event.preventDefault();
101 | // Unsubscribe
102 | ws.unsubscribe( this.channel );
103 | // data updates
104 | this.connected = false;
105 | this.clientID =
106 | this.channel =
107 | this.username = null;
108 | // UI Updates
109 | this.ui.body.classList.add('off');
110 | this.ui.loginModal.modal('show');
111 | // change our AdvancedSocket to not run the Auto Connect feature
112 | AdvancedSocket.autoConnect = false;
113 | AdvancedSocket.checkConnection();
114 | }
115 | },
116 | receiveMessage: function( obj ){
117 | if( typeof obj.data === 'object' ){
118 | // process connected user menu
119 | this.clients = obj.data.data;
120 | this.updateClients();
121 | }
122 | else if ( obj.data !== 'FORCE-RECONNECT' ) {
123 | this.ui.messages.innerHTML += obj.data;
124 | // animate the scroll
125 | if ( typeof document.body.scrollIntoView === 'function' ){
126 | this.ui.messages.querySelector('.message:last-child').scrollIntoView({
127 | behavior : 'smooth'
128 | });
129 | }
130 | else {
131 | $(this.ui.messages).animate({scrollTop : this.ui.messages.scrollHeight + 'px'},500);
132 | }
133 | }
134 | },
135 | sendMessage:function( event ){
136 | if ( event ){
137 | event.preventDefault();
138 |
139 | let value = this.ui.messageField.val().trim(),
140 | toID = this.ui.messageToField.val(),
141 | data = {};
142 |
143 | if ( value !== '' ){
144 | // for private message
145 | if ( toID !== '' )
146 | data.to = this.clients.find(client => client.id === toID );
147 | // publish
148 | ws.publish( this.channel, value, data );
149 | // reset message field
150 | this.ui.messageField.val('').focus();
151 | }
152 | }
153 | },
154 | updateClients: function() {
155 | let userList = '