Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add client-to-client example using Pusher client-events.

  • Loading branch information...
commit 11aa4d2c67f5794435b964214972b0e45f67d51f 1 parent de4d739
@bennadel authored
View
58 examples/client_to_client/Application.cfc
@@ -0,0 +1,58 @@
+<cfscript>
+
+component
+ output = "false"
+ hint = "I define the application settings and event handlers."
+ {
+
+
+ // Define the application settings.
+ this.name = hash( getCurrentTemplatePath() );
+ this.applicationTimeout = createTimeSpan( 0, 0, 10, 0 );
+ this.sessionManagement = false;
+
+ // Get the current directory and the root directory so that we can
+ // set up the mappings to our components.
+ this.appDirectory = getDirectoryFromPath( getCurrentTemplatePath() );
+ this.projectDirectory = ( this.appDirectory & "../../" );
+
+ // Map to our Lib folder so we can access our project components.
+ this.mappings[ "/lib" ] = ( this.projectDirectory & "lib/" );
+
+ // Map to our Vendor folder so we can access 3rd-party components.
+ this.mappings[ "/vendor" ] = ( this.projectDirectory & "vendor/" );
+
+
+ // I initialize the request.
+ public boolean function onRequestStart(){
+
+ // Store the credentials for the Pusher App API.
+ // *********************************************************
+ // THESE ARE DEMO CREDENTIALS AND SHOULD NOT BE USED IN YOUR
+ // PRODUCTION APP; THEY ARE FOR SANDBOX USE AND HAVE HARD
+ // LIMITS ON CONNECTIONS AND MESSAGES. SWAP THESE OUT WHEN
+ // YOU IMPLEMENT THIS LIBRARY.
+ // *********************************************************
+ request.pusherAppID = "1577";
+ request.pusherKey = "967025141727846f5a79";
+ request.pusherSecret = "5a7fd901cdf3e73c18b5";
+ // *********************************************************
+
+ // Create an instance of our pusher component using our demo
+ // credentials and the Crypto library.
+ request.pusher = new lib.Pusher(
+ appID = request.pusherAppID,
+ appKey = request.pusherKey,
+ appSecret = request.pusherSecret,
+ crypto = new vendor.crypto.Crypto()
+ );
+
+ // Return true so the page can load.
+ return( true );
+
+ }
+
+
+}
+
+</cfscript>
View
70 examples/client_to_client/channel_auth.cfm
@@ -0,0 +1,70 @@
+
+<!---
+ I provide the authentication for connecting to both private
+ channels and presence channels. Both channels use the same
+ authentication approach; however, the presence channel requires
+ additional return data.
+--->
+
+<!--- Param the form fields. --->
+<cfparam name="form.channel_name" type="string" />
+<cfparam name="form.socket_id" type="string" />
+
+<!---
+ We are also expecting the userID to come through with all
+ authentication-based requests (due to our connection configuration
+ in the JavaScript).
+--->
+<cfparam name="form.userID" type="string" />
+
+
+<!--- ----------------------------------------------------- --->
+<!--- ----------------------------------------------------- --->
+
+
+<!---
+ For this demo, we don't really have any true security. So, I'm
+ simply going to validate that the userID is a valid UUID.
+--->
+<cfif ! isValid( "uuid", form.userID )>
+
+ <!--- Do not allow the chellen subscription. --->
+ <cfheader
+ statuscode="403"
+ statustext="Forbidden"
+ />
+
+ <!--- Halt any further processing. --->
+ <cfabort />
+
+</cfif>
+
+
+<!--- ----------------------------------------------------- --->
+<!--- ----------------------------------------------------- --->
+
+
+<!--- For this demo, we only care about private channels. --->
+<cfif reFind( "^private-", form.channel_name )>
+
+ <cfset authentication = request.pusher.getPrivateChannelAuthentication(
+ form.socket_id,
+ form.channel_name
+ ) />
+
+<cfelse>
+
+ <cfthrow type="PresenseNotSupported" />
+
+</cfif>
+
+
+<!--- The authentication response is expected in JSON format. --->
+<cfset serializedResponse = serializeJSON( authentication ) />
+
+<!--- Stream respones back to the client. --->
+<cfcontent
+ type="application/json"
+ variable="#charsetDecode( serializedResponse, 'utf-8' )#"
+ />
+
View
254 examples/client_to_client/index.cfm
@@ -0,0 +1,254 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8" />
+
+ <title>
+ Sending Client-To-Client Realtime Messages With Pusher App And ColdFusion
+ </title>
+
+ <link rel="stylesheet" type="text/css" href="./styles.css"></link>
+</head>
+<body>
+
+ <h1>
+ Sending Client-To-Client Realtime Messages With Pusher App And ColdFusion
+ </h1>
+
+ <p>
+ <em>Note</em>: You should see the Mouse cursor for each user viewing this page.
+ </p>
+
+
+ <!-- Load jQuery and Pusher from the CDN. -->
+ <script
+ type="text/javascript"
+ src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
+ </script>
+ <script
+ type="text/javascript"
+ src="//js.pusher.com/2.1/pusher.min.js">
+ </script>
+ <script type="text/javascript">
+
+
+ // Define the pusher key here so we can limit our use of
+ // CFOutput within the JavaScript.
+ var pusherAppKey = "<cfoutput>#request.pusherKey#</cfoutput>";
+
+
+ // -------------------------------------------------- //
+ // -------------------------------------------------- //
+
+
+ // Generate a unique ID for each user.
+ var currentUserID = "<cfoutput>#createUUID()#</cfoutput>";
+
+
+ // -------------------------------------------------- //
+ // -------------------------------------------------- //
+
+
+ // Create a new instance of the Pusher client. The second
+ // argument - the connection options - is an optional set
+ // of data that can be passed through with Authentication
+ // requests. In this case, we need to define the authorization
+ // end-point since client-events need to be on authorized
+ // channel subscriptions.
+ var pusher = new Pusher(
+ pusherAppKey,
+ {
+ authEndpoint: "./channel_auth.cfm",
+ auth: {
+ params: {
+ userID: currentUserID
+ }
+ }
+ }
+ );
+
+
+ // All the users will listen for and announce mouse-move
+ // events over this private channel.
+ // --
+ // NOTE: client-events need to use an authenciated channel.
+ var channel = pusher.subscribe( "private-mouse" );
+
+
+ // Bind to the move event (other user's publishing).
+ // --
+ // NOTE: ALl client-events must be transmitted on events that
+ // are prefixed with "client-".
+ channel.bind(
+ "client-moved",
+ function( event ) {
+
+ applyMouseMove( event.userID, event.x, event.y );
+
+ }
+ );
+
+
+ // Client-events can only be triggered at a maximum of 10 events
+ // per second. As such, we'll have to debounce the events that
+ // are triggered by the local user.
+ var rateLimitEvent = null;
+
+
+ // I push the current user's mouse-move event out on the
+ // private channel to all the other users.
+ // --
+ // NOTE: Client-events do NOT bounce back to the originating
+ // user. As such, we can publish events without having to have
+ // and logic about who sent what.
+ function pushMouseMove( userID, x, y ) {
+
+ if ( rateLimitEvent ) {
+
+ rateLimitEvent.x = x;
+ rateLimitEvent.y = y;
+ return;
+
+ }
+
+ rateLimitEvent = {
+ userID: userID,
+ x: x,
+ y: y
+ };
+
+ // Debounce for 100ms (we can only send a max of 10 events
+ // per second ~ every 100ms).
+ setTimeout(
+ function() {
+
+ channel.trigger( "client-moved", rateLimitEvent );
+
+ rateLimitEvent = null;
+
+ },
+ 100
+ );
+
+ }
+
+
+ // -------------------------------------------------- //
+ // -------------------------------------------------- //
+
+
+ // Keep a collection of the users we know about.
+ var users = [];
+
+ // Start watching the document for mouse movements.
+ $( document ).mousemove( handleMouseMove );
+
+
+ // I apply a mouse move events that has been received over the
+ // pusher channel (from another user).
+ function applyMouseMove( userID, x, y ) {
+
+ var user = getUserByID( userID );
+
+ // If the user has not yet been defined locally, then
+ // create it and append it to the BODY.
+ if ( ! user ) {
+
+ user = createNewUser( userID );
+
+ users.push( user );
+
+ $( "body" ).append( user.mouse );
+
+ }
+
+ // Update the data and location for the user.
+ user.mouse.css({
+ left: ( ( user.x = x ) + "px" ),
+ top: ( ( user.y = y ) + "px" )
+ });
+
+ }
+
+
+ // I create a user object with the given userID.
+ function createNewUser( userID ) {
+
+ var mouse = $( "<div class='mouse'></div>" )
+ .append( "<div class='pointer'></div>" )
+ .append( "<div class='label'></div>")
+ ;
+
+ var label = mouse.find( "div.label" );
+
+ // Apply a special label to the current user.
+ if ( userID === currentUserID ) {
+
+ label.text( "Me!" );
+
+ } else {
+
+ label.text( userID );
+
+ // Also, let's add a CSS class that animates the
+ // transition in CSS position. This way, the local
+ // user will update immediately and the external users
+ // will update with an animation in alignement with
+ // the rate-limiting.
+ mouse.addClass( "external" );
+
+ }
+
+ var user = {
+ id: userID,
+ x: -100,
+ y: -100,
+ mouse: mouse
+ };
+
+ user.mouse.css({
+ left: ( user.x + "px" ),
+ top: ( user.y + "px" )
+ });
+
+ return( user );
+
+ }
+
+
+ // I get the user object using the given userID. If the user
+ // has not yet been recorded locally, null is returned.
+ function getUserByID( userID ) {
+
+ for ( var i = 0 ; i < users.length ; i++ ) {
+
+ if ( users[ i ].id === userID ) {
+
+ return( users[ i ] );
+
+ }
+
+ }
+
+ return( null );
+
+ }
+
+
+ // I handle the local mouse-move event triggered by the current
+ // user.
+ function handleMouseMove( event ) {
+
+ // Update the local mouse indicator for the current user.
+ applyMouseMove( currentUserID, event.pageX, event.pageY );
+
+ // Push the mouse move event to all users.
+ pushMouseMove( currentUserID, event.pageX, event.pageY );
+
+ }
+
+
+ </script>
+
+</body>
+</html>
View
34 examples/client_to_client/styles.css
@@ -0,0 +1,34 @@
+
+body {
+ font-family: helvetica, arial, sans-serif ;
+ position: relative ;
+}
+
+div.mouse {
+ position: fixed ;
+}
+
+div.mouse.external {
+ transition: left 100ms ease, top 100ms ease ;
+}
+
+div.mouse div.pointer {
+ background-color: #FF33CC ;
+ border-radius: 50% 50% 50% 50% ;
+ height: 6px ;
+ left: -3px ;
+ position: absolute ;
+ top: -11px ;
+ width: 6px ;
+}
+
+div.mouse div.label {
+ border-bottom: 1px dotted #FF33CC ;
+ color: #333333 ;
+ font-size: 12px ;
+ left: 8px ;
+ line-height: 14px ;
+ position: absolute ;
+ top: -15px ;
+ white-space: nowrap ;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.