I need your help guys, I'm building my own web chat for my online radio site. I already have a AJAX PHP Web chat from Tutorialzine. I want to modify it. But i don't know where to start. I want it to integrate with Facebook. I want it instead of asking for username and email, there will be a button that says 'Connect to Facebook'. and the Profile Picture and Name of the user will automatically saved to the database. I really need it. And i want it to be moderated. Thank You! and God bless everyone! :)
ajax.php
<?php
/* Database Configuration. Add your details below */
$dbOptions = array(
'db_host' => 'localhost',
'db_user' => 'root',
'db_pass' => '',
'db_name' => 'chat'
);
/* Database Config End */
error_reporting(E_ALL ^ E_NOTICE);
require "classes/DB.class.php";
require "classes/Chat.class.php";
require "classes/ChatBase.class.php";
require "classes/ChatLine.class.php";
require "classes/ChatUser.class.php";
session_name('webchat');
session_start();
if(get_magic_quotes_gpc()){
// If magic quotes is enabled, strip the extra slashes
array_walk_recursive($_GET,create_function('&$v,$k','$v = stripslashes($v);'));
array_walk_recursive($_POST,create_function('&$v,$k','$v = stripslashes($v);'));
}
try{
// Connecting to the database
DB::init($dbOptions);
$response = array();
// Handling the supported actions:
switch($_GET['action']){
case 'login':
$response = Chat::login($_POST['name'],$_POST['email']);
break;
case 'checkLogged':
$response = Chat::checkLogged();
break;
case 'logout':
$response = Chat::logout();
break;
case 'submitChat':
$response = Chat::submitChat($_POST['chatText']);
break;
case 'getUsers':
$response = Chat::getUsers();
break;
case 'getChats':
$response = Chat::getChats($_GET['lastID']);
break;
default:
throw new Exception('Wrong action');
}
echo json_encode($response);
}
catch(Exception $e){
die(json_encode(array('error' => $e->getMessage())));
}
?>
script.js
$(document).ready(function(){
// Run the init method on document ready:
chat.init();
});
var chat = {
// data holds variables for use in the class:
data : {
lastID : 0,
noActivity : 0
},
// Init binds event listeners and sets up timers:
init : function(){
// Using the defaultText jQuery plugin, included at the bottom:
$('#name').defaultText('Nickname');
$('#email').defaultText('Email (Gravatars are Enabled)');
// Converting the #chatLineHolder div into a jScrollPane,
// and saving the plugin's API in chat.data:
chat.data.jspAPI = $('#chatLineHolder').jScrollPane({
verticalDragMinHeight: 12,
verticalDragMaxHeight: 12
}).data('jsp');
// We use the working variable to prevent
// multiple form submissions:
var working = false;
// Logging a person in the chat:
$('#loginForm').submit(function(){
if(working) return false;
working = true;
// Using our tzPOST wrapper function
// (defined in the bottom):
$.tzPOST('login',$(this).serialize(),function(r){
working = false;
if(r.error){
chat.displayError(r.error);
}
else chat.login(r.name,r.gravatar);
});
return false;
});
// Submitting a new chat entry:
$('#submitForm').submit(function(){
var text = $('#chatText').val();
if(text.length == 0){
return false;
}
if(working) return false;
working = true;
// Assigning a temporary ID to the chat:
var tempID = 't'+Math.round(Math.random()*1000000),
params = {
id : tempID,
author : chat.data.name,
gravatar : chat.data.gravatar,
text : text.replace(/</g,'<').replace(/>/g,'>')
};
// Using our addChatLine method to add the chat
// to the screen immediately, without waiting for
// the AJAX request to complete:
chat.addChatLine($.extend({},params));
// Using our tzPOST wrapper method to send the chat
// via a POST AJAX request:
$.tzPOST('submitChat',$(this).serialize(),function(r){
working = false;
$('#chatText').val('');
$('div.chat-'+tempID).remove();
params['id'] = r.insertID;
chat.addChatLine($.extend({},params));
});
return false;
});
// Logging the user out:
$('a.logoutButton').live('click',function(){
$('#chatTopBar > span').fadeOut(function(){
$(this).remove();
});
$('#submitForm').fadeOut(function(){
$('#loginForm').fadeIn();
});
$.tzPOST('logout');
return false;
});
// Checking whether the user is already logged (browser refresh)
$.tzGET('checkLogged',function(r){
if(r.logged){
chat.login(r.loggedAs.name,r.loggedAs.gravatar);
}
});
// Self executing timeout functions
(function getChatsTimeoutFunction(){
chat.getChats(getChatsTimeoutFunction);
})();
(function getUsersTimeoutFunction(){
chat.getUsers(getUsersTimeoutFunction);
})();
},
// The login method hides displays the
// user's login data and shows the submit form
login : function(name,gravatar){
chat.data.name = name;
chat.data.gravatar = gravatar;
$('#chatTopBar').html(chat.render('loginTopBar',chat.data));
$('#loginForm').fadeOut(function(){
$('#submitForm').fadeIn();
$('#chatText').focus();
});
},
// The render method generates the HTML markup
// that is needed by the other methods:
render : function(template,params){
var arr = [];
switch(template){
case 'loginTopBar':
arr = [
'<span><img src="',params.gravatar,'" width="23" height="23" />',
'<span class="name">',params.name,
'</span>Logout</span>'];
break;
case 'chatLine':
arr = [
'<div class="chat chat-',params.id,' rounded"><span class="gravatar"><img src="',params.gravatar,
'" width="23" height="23" onload="this.style.visibility=\'visible\'" />','</span><span class="author">',params.author,
':</span><span class="text">',params.text,'</span><span class="time">',params.time,'</span></div>'];
break;
case 'user':
arr = [
'<div class="user" title="',params.name,'"><img src="',
params.gravatar,'" width="30" height="30" onload="this.style.visibility=\'visible\'" /></div>'
];
break;
}
// A single array join is faster than
// multiple concatenations
return arr.join('');
},
// The addChatLine method ads a chat entry to the page
addChatLine : function(params){
// All times are displayed in the user's timezone
var d = new Date();
if(params.time) {
// PHP returns the time in UTC (GMT). We use it to feed the date
// object and later output it in the user's timezone. JavaScript
// internally converts it for us.
d.setUTCHours(params.time.hours,params.time.minutes);
}
params.time = (d.getHours() < 10 ? '0' : '' ) + d.getHours()+':'+
(d.getMinutes() < 10 ? '0':'') + d.getMinutes();
var markup = chat.render('chatLine',params),
exists = $('#chatLineHolder .chat-'+params.id);
if(exists.length){
exists.remove();
}
if(!chat.data.lastID){
// If this is the first chat, remove the
// paragraph saying there aren't any:
$('#chatLineHolder p').remove();
}
// If this isn't a temporary chat:
if(params.id.toString().charAt(0) != 't'){
var previous = $('#chatLineHolder .chat-'+(+params.id - 1));
if(previous.length){
previous.after(markup);
}
else chat.data.jspAPI.getContentPane().append(markup);
}
else chat.data.jspAPI.getContentPane().append(markup);
// As we added new content, we need to
// reinitialise the jScrollPane plugin:
chat.data.jspAPI.reinitialise();
chat.data.jspAPI.scrollToBottom(true);
},
// This method requests the latest chats
// (since lastID), and adds them to the page.
getChats : function(callback){
$.tzGET('getChats',{lastID: chat.data.lastID},function(r){
for(var i=0;i<r.chats.length;i++){
chat.addChatLine(r.chats[i]);
}
if(r.chats.length){
chat.data.noActivity = 0;
chat.data.lastID = r.chats[i-1].id;
}
else{
// If no chats were received, increment
// the noActivity counter.
chat.data.noActivity++;
}
if(!chat.data.lastID){
chat.data.jspAPI.getContentPane().html('<p class="noChats">No chats yet</p>');
}
// Setting a timeout for the next request,
// depending on the chat activity:
var nextRequest = 1000;
// 2 seconds
if(chat.data.noActivity > 3){
nextRequest = 2000;
}
if(chat.data.noActivity > 10){
nextRequest = 5000;
}
// 15 seconds
if(chat.data.noActivity > 20){
nextRequest = 15000;
}
setTimeout(callback,nextRequest);
});
},
// Requesting a list with all the users.
getUsers : function(callback){
$.tzGET('getUsers',function(r){
var users = [];
for(var i=0; i< r.users.length;i++){
if(r.users[i]){
users.push(chat.render('user',r.users[i]));
}
}
var message = '';
if(r.total<1){
message = 'No one is online';
}
else {
message = r.total+' '+(r.total == 1 ? 'person':'people')+' online';
}
users.push('<p class="count">'+message+'</p>');
$('#chatUsers').html(users.join(''));
setTimeout(callback,15000);
});
},
// This method displays an error message on the top of the page:
displayError : function(msg){
var elem = $('<div>',{
id : 'chatErrorMessage',
html : msg
});
elem.click(function(){
$(this).fadeOut(function(){
$(this).remove();
});
});
setTimeout(function(){
elem.click();
},5000);
elem.hide().appendTo('body').slideDown();
}
};
// Custom GET & POST wrappers:
$.tzPOST = function(action,data,callback){
$.post('php/ajax.php?action='+action,data,callback,'json');
}
$.tzGET = function(action,data,callback){
$.get('php/ajax.php?action='+action,data,callback,'json');
}
// A custom jQuery method for placeholder text:
$.fn.defaultText = function(value){
var element = this.eq(0);
element.data('defaultText',value);
element.focus(function(){
if(element.val() == value){
element.val('').removeClass('defaultText');
}
}).blur(function(){
if(element.val() == '' || element.val() == value){
element.addClass('defaultText').val(value);
}
});
return element.blur();
}
If you only want to connect with facebook for the user name and picture then all you need to do is include the Facebook Javascript SDK, and then either use the Login Button plugin or use the Client-Side authentication.
If you want to connect with the Facebook internal chat, then you can use the Chat API which has two authentication methods: Facebook Platform and Username/Password.
If you want the first method (sounds like what you want) then you'll need to authenticate the user, either with the client side flow or the server side flow and ask for the "xmpp_login" permission.
There are php examples in the chat API documentation.
Related
I have a WordPress site where I run guitar courses and I currently have various notifications triggered by various events that I am sending via email or OneSignal. But I want users to see notifications appear in near real time on the site when they are logged in, regardless of the means by which I sent the message.
Lately I have been revisiting a notification system that I implemented on my previous site that I built with Laravel and trying to figure out how to adapt for WordPress. Here is what I do on the Laravel site (a lot of it based on this great tutorial: https://www.sitepoint.com/add-real-time-notifications-laravel-pusher/).
var notificationsWrapper = $('.dropdown-notifications');
var notificationsToggle = notificationsWrapper.find('a[data-toggle]');
var notificationsCountElem = notificationsToggle.find('i[data-count]');
var notificationsCount = parseInt(notificationsCountElem.data('count'));
window.Pusher = require('pusher-js');
import Echolibrary from "laravel-echo";
var notifications = [];
const NOTIFICATION_TYPES = {
follow: 'App\\Notifications\\UserFollowed',
newEx: 'App\\Notifications\\NewExercisePosted'
};
window.Echo = new Echolibrary({
broadcaster: 'pusher',
key: 'xxxxxxxxxxx',
cluster: 'mt1',
encrypted: true
});
$(document).ready(function () {
if(Laravel.userId) {
// load notifications from database
$.get(`/notifications`, function (data) {
addNotifications(data, "#notifications", 'db');
});
// load new ones from pusher
Echo.private(`App.User.${Laravel.userId}`)
.notification((notification) => {
addNotifications([notification], '#notifications', 'pusher');
});
}
});
function addNotifications(newNotifications, target, source) {
notifications = _.concat(notifications, newNotifications);
// show only last 5 notifications
notifications.slice(0, 5);
showNotifications(notifications, target);
}
function showNotifications(notifications, target) {
notificationsCount = notifications.length;
notificationsCountElem.attr('data-count', notificationsCount);
if(notifications.length) {
var htmlElements = notifications.map(function (notification) {
return makeNotification(notification);
});
$('#markall').show();
$(target + 'Header').after(htmlElements.join(''));
$(target).addClass('has-notifications');
$('#nomsgs').hide();
} else {
$('#markall').hide();
$(target + 'Header').after('<li class="dropdown-header" id="nomsgs">No messages</li>');
$(target).removeClass('has-notifications');
}
}
// Make a single notification string
function makeNotification(notification) {
var to = routeNotification(notification);
var notificationText = makeNotificationText(notification);
return '<li class = "msgitems" style="background-color:white; border-style:solid; border-width: 1px; border-color:#2073a2; margin: 2px; padding:0px"><a style="color:blue;" href="' + to + '">' + notificationText + '</a></li>';
}
function routeNotification(notification) {
var to = `?read=${notification.id}`;
if(notification.type === NOTIFICATION_TYPES.follow) {
to = 'users' + to;
} else if(notification.type === NOTIFICATION_TYPES.newEx) {
const exId = notification.data.ex_id;
to = `guitar-lesson-ex/${exId}` + to;
}
return '/' + to;
}
function makeNotificationText(notification) {
var text = '';
if(notification.type === NOTIFICATION_TYPES.follow) {
const name = notification.data.follower_name;
text += `<strong>${name}</strong> followed you`;
} else if(notification.type === NOTIFICATION_TYPES.newEx) {
text += `<img src = "/img/guitarpick.png" alt = "guitar pick"> New exercise: ` + notification.data.title;
}
return text;
}
$("#markall").click(function () {
var targetHref = $(this).data('href');
$.post('/markallnotificationsread', function (data) {
data.success ? (window.location.href = targetHref) : false;
}, 'json');
notificationsCountElem.attr('data-count', 0);
$('#notificationsHeader').nextAll().remove();
$('#notificationsHeader').after('<li class="dropdown-header" id ="nomsgs">No messages</li>');
$('#notifications').removeClass('has-notifications');
$('#markall').hide();
return false;
});
To do this in WordPress, should I take same approach, meaning import the Laravel Echo library or should I be thinking in different direction?
Also, in my current case, I have certain notifications that
go to a user (like 'your course access expires soon'), and
go to a group (like 'your course is now open')
For case 1, is pusher even a good use case, since this is not really a channel sort of message, but a one-off to a single user?
Anyway, curious if maybe there is a completely different, better way you would go about this.
I'm new to WebRTC and I don't understand how the signaling works on the server side. I know the request must be two-way. I'm able to send an offer to a PHP page but from there I'm stuck.. How does the other peer pick that offer so that it can generate an answer? I don't want a socket solution because I don't have my own server and I don't want to have a third-party dependency.
I have created an offer and sent that offer to a PHP page. See my code:
var myPeerConnection;
var caller_video=document.getElementById('caller_video');
var receiver_video=document.getElementById('receiver_video');
var mediaConstraints = {
video:true,
audio:false
};
function sendToServer(msg){
var msgJSON = JSON.stringify(msg);
$.ajax({
type:'get',
url:'offer.php',
data:'object='+msgJSON,
beforeSend:function(){
console.log('Sending...');
},
success:function(data){
console.log(data);
}
});
}
function reportError(error){
console.log(error.name);
}
function handleNegotiationNeededEvent(){
myPeerConnection.createOffer().then(function(offer) {
return myPeerConnection.setLocalDescription(offer);
})
.then(function(){ // so here I'm supposed to send an offer
sendToServer({
name: myUsername,
target: targetUsername,
type: "video-offer",
sdp: myPeerConnection.localDescription
});
})
.catch(reportError);
}
function handleICECandidateEvent(event){
if(event.candidate){//send the ICECandidates
sendToServer({
name: myUsername,
target: targetUsername,
type: "new-ice-candidate",
candidate: event.candidate
});
}
}
function handleTrackEvent(event){
console.log(event);
document.getElementById("received_video").srcObject = event.streams[0];
}
function handleRemoveTrackEvent(event){
var stream = document.getElementById("received_video").srcObject;
var trackList = stream.getTracks();
if (trackList.length == 0){
closeVideoCall();
}
}
function handleICEConnectionStateChangeEvent(event){
console.log('ICE connection changed!');
switch(myPeerConnection.iceConnectionState) {
case "closed":
case "failed":
case "disconnected":
closeVideoCall();
break;
}
}
function handleICEGatheringStateChangeEvent(event){
console.log('Is gathering');
console.log(event);
}
function handleSignalingStateChangeEvent(event) {
console.log('Signaling state changed');
switch(myPeerConnection.signalingState) {
case "closed":
closeVideoCall();
break;
}
};
function createPeerConnection() {
var STUN = {
'url': 'stun:stun.l.google.com:19302',
};
var iceServers =
{
iceServers: [STUN]
};
myPeerConnection = new RTCPeerConnection(iceServers);
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.ontrack = handleTrackEvent;
myPeerConnection.onremovetrack = handleRemoveTrackEvent;
myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
}
function handleGetUserMediaError(e) {
switch(e.name) {
case "NotFoundError":
alert("Unable to open your call because no camera and/or microphone" +
"were found.");
break;
case "SecurityError":
case "PermissionDeniedError":
// Do nothing; this is the same as the user canceling the call.
break;
default:
alert("Error opening your camera and/or microphone: " + e.message);
break;
}
closeVideoCall();
}
function closeVideoCall(){
//do something to exit the video call
}
//invite the other peer...we want to send our SDP
function invite(evt){
if (myPeerConnection){
console.log('Call already started');
}
else{
//myPeerConnection=new MediaStream();
targetUsername ='Nevil';//Unique other peer username
myUsername='Philip';
createPeerConnection();//this function creates a peer connection//uses the STUN/TURNS servers...updates myPeerConnection variable so it's not null
navigator.mediaDevices.getUserMedia(mediaConstraints)//grab our media constraints
.then(function(localStream) {
caller_video.srcObject =localStream;
caller_video.play();
localStream.getTracks().forEach(track => myPeerConnection.addTrack(track, localStream));
})
.catch(handleGetUserMediaError);
}
}
//we click the call button
document.querySelector('#callBt').addEventListener('click',function(){
invite();
});
I know there must be a way to send back the answer but I would like to just send the offer; that way I will understand how both peers exchange the SDP on the backend. Please try using PHP. If anyone can create a signaling XHR request, I will appreciate it.
I'm using this function to try and send my objects coordinates and id to my php script. I'm not sure how to setup how to get the card_id and it's top and left coordinates into the post so I can retrieve them in my php script as one array. I have the all the values alerting properly but how do I pass them make sure I'm getting the proper ones on the other end and that the coordinates go with the card_id?
document.getElementById('rasterize-scoutlog2').onclick = function() {
if (!fabric.Canvas.supports('toDataURL')) {
alert('This browser doesn\'t provide means to serialize canvas to an image');
}
else {
canvas.deactivateAllWithDispatch().renderAll();
//window.open(canvas.toDataURL('png'));
//var strDataURI = (canvas.toDataURL('png'));
//strDataURI = strDataURI.substr(22, strDataURI.length);
var objsInCanvas = canvas.getObjects();
objsInCanvas.forEach(function(object) {
var stickycard_ids = [object.card_id];
var stickycard_top = [object.top];
var stickycard_left = [object.left];
if(object.card_id != null){
stickycard_ids.forEach(function(stickycard_idarr) {
alert(stickycard_idarr+stickycard_top+stickycard_left);
});
}
});
var scoutlogname = $('#scoutmapselectcard').val();
$.post("maps/savescout_log.php",
{
//str: strDataURI,
// queryStr: queryStr,
scoutlogname: scoutlogname,
//**** stickycard_idarr: stickycard_idarr
},
function(data){
if(data == "OK"){
$("#msg").html("Scout Log saved to account!").css({'color':"green",'font-weight':"bold"});
}
if(data=="EMPTY"){
$("#msg").html("Please Enter a name for your Scout Log!").css({color:"red"});
}
if(data=="WRONGCH"){
$("#msg").html("Only A_Z,a-z,0-9-_ allowed in Scout Log name!").css({color:"red"});
}
if(data=="EXIST"){
$("#msg").html("Scout Log name all ready exists!<br> Delete the existing one before saving.").css({color:"red"});
}
if(data=="ERROR"){
$("#msg").html("Scout Log not saved!").css({color:"red"});
}
window.setTimeout(function() {
$('#msg').empty();
}, 5000);
});
}
};
I got the objects and their details all going to arrays. I used the .push method to populate the arrays with the objects values in the foreach loop. Now on to the php end to insert them into the mysql database table.
document.getElementById('rasterize-scoutlog').onclick = function() {
if (!fabric.Canvas.supports('toDataURL')) {
alert('This browser doesn\'t provide means to serialize canvas to an image');
}else {
canvas.deactivateAllWithDispatch().renderAll();
//window.open(canvas.toDataURL('png'));
//var strDataURI = (canvas.toDataURL('png'));
//strDataURI = strDataURI.substr(22, strDataURI.length);
var objsInCanvas = canvas.getObjects();
var stickycard_ids = [];
var stickycard_top = [];
var stickycard_left = [];
var stickycard_type = [];
objsInCanvas.forEach(function(object) {
if(object.card_id != null){
stickycard_ids.push(object.card_id);
stickycard_top.push(object.top);
stickycard_left.push(object.left);
stickycard_type.push(object.cardtype);
}
});
var scoutmapname = $('#scoutmapselectcard').val()
var scoutlogname = $('#scoutlogname').val();
$.post("maps/savescout_log.php",
{
//str: strDataURI,
//queryStr: queryStr,
scoutlogname: scoutlogname,
scoutmapname: scoutmapname,
stickycard_ids: stickycard_ids,
stickycard_top: stickycard_top,
stickycard_left: stickycard_left,
stickycard_type:stickycard_type
},
function(data){
if(data == "OK"){
$("#msg").html("Scout Log saved to account!").css({'color':"green",'font-weight':"bold"});
}
if(data=="EMPTY"){
$("#msg").html("Please Enter a name for your Scout Log!").css({color:"red"});
}
if(data=="WRONGCH"){
$("#msg").html("Only A_Z,a-z,0-9-_ allowed in Scout Log name!").css({color:"red"});
}
if(data=="EXIST"){
$("#msg").html("Scout Log name all ready exists!<br> Delete the existing one before saving or enter a new name.").css({color:"red"});
}
if(data=="ERROR"){
$("#msg").html("Scout Log not saved!").css({color:"red"});
}
window.setTimeout(function() {
$('#msg').empty();}, 5000);
});
}
};
Here's the php that will insert those mutiple arrays into into mysql using PDO. I'm using the stickycard_ids as the indexing value then posting that and the other assiociated array values to a new row every time the stickycard_ids value changes.
for ($i=0; $i < count($_POST['stickycard_ids']); $i++ ) {
$card_id = $_POST['stickycard_ids'][$i];
$cardtype = $_POST['stickycard_type'][$i];
$top_y = $_POST['stickycard_top'][$i];
$left_x = $_POST['stickycard_left'][$i];
$sql ="INSERT INTO tablename (scoutmapname,scoutlogname,card_id,left_x,top_y,cardtype)
VALUES (:scoutlogname,:card_id,:left_x,:top_y,:cardtype)";
$q = $pdo->prepare($sql);
$q->execute(array(':scoutmapname'=>$scoutmapname,':scoutlogname'=>$scoutlogname,':card_id'=>$card_id,':left_x'=>$left_x,':top_y'=>$top_y,':cardtype'=>$cardtype));
}
So, I have this HTML5 chatting script which requires users to input their name and email to start chatting. The script grabs the users display picture from gavatar. I'm trying to change that to make it take a webcam snapshot from the users computer and set that as a display picture.
I've seen a few examples of the usage of HTML5 to access the users webcam but I can't seem to figure out how to automatically take the snapshot and set it as the users display picture.
Here's my current code.
$(document).ready(function(){
// Run the init method on document ready:
chat.init();
});
var chat = {
// data holds variables for use in the class:
data : {
lastID : 0,
noActivity : 0
},
// Init binds event listeners and sets up timers:
init : function(){
// Using the defaultText jQuery plugin, included at the bottom:
$('#name').defaultText('Nickname');
$('#email').defaultText('Email');
// Converting the #chatLineHolder div into a jScrollPane,
// and saving the plugin's API in chat.data:
chat.data.jspAPI = $('#chatLineHolder').jScrollPane({
verticalDragMinHeight: 12,
verticalDragMaxHeight: 12
}).data('jsp');
// We use the working variable to prevent
// multiple form submissions:
var working = false;
// Logging a person in the chat:
$('#loginForm').submit(function(){
if(working) return false;
working = true;
// Using our tzPOST wrapper function
// (defined in the bottom):
$.tzPOST('login',$(this).serialize(),function(r){
working = false;
if(r.error){
chat.displayError(r.error);
}
else chat.login(r.name,r.gravatar);
});
return false;
});
// Submitting a new chat entry:
$('#submitForm').submit(function(){
var text = $('#chatText').val();
if(text.length == 0){
return false;
}
if(working) return false;
working = true;
// Assigning a temporary ID to the chat:
var tempID = 't'+Math.round(Math.random()*1000000),
params = {
id : tempID,
author : chat.data.name,
gravatar : chat.data.gravatar,
text : text.replace(/</g,'<').replace(/>/g,'>')
};
// Using our addChatLine method to add the chat
// to the screen immediately, without waiting for
// the AJAX request to complete:
chat.addChatLine($.extend({},params));
// Using our tzPOST wrapper method to send the chat
// via a POST AJAX request:
$.tzPOST('submitChat',$(this).serialize(),function(r){
working = false;
$('#chatText').val('');
$('div.chat-'+tempID).remove();
params['id'] = r.insertID;
chat.addChatLine($.extend({},params));
});
return false;
});
// Logging the user out:
$('a.logoutButton').live('click',function(){
$('#chatTopBar > span').fadeOut(function(){
$(this).remove();
});
$('#submitForm').fadeOut(function(){
$('#loginForm').fadeIn();
});
$.tzPOST('logout');
return false;
});
// Checking whether the user is already logged (browser refresh)
$.tzGET('checkLogged',function(r){
if(r.logged){
chat.login(r.loggedAs.name,r.loggedAs.gravatar);
}
});
// Self executing timeout functions
(function getChatsTimeoutFunction(){
chat.getChats(getChatsTimeoutFunction);
})();
(function getUsersTimeoutFunction(){
chat.getUsers(getUsersTimeoutFunction);
})();
},
// The login method hides displays the
// user's login data and shows the submit form
login : function(name,gravatar){
chat.data.name = name;
chat.data.gravatar = gravatar;
$('#chatTopBar').html(chat.render('loginTopBar',chat.data));
$('#loginForm').fadeOut(function(){
$('#submitForm').fadeIn();
$('#chatText').focus();
});
},
// The render method generates the HTML markup
// that is needed by the other methods:
render : function(template,params){
var arr = [];
switch(template){
case 'loginTopBar':
arr = [
'<span><img src="',params.gravatar,'" width="23" height="23" />',
'<span class="name">',params.name,
'</span>Logout</span>'];
break;
case 'chatLine':
arr = [
'<div class="chat chat-',params.id,' rounded"><span class="gravatar"><img src="',params.gravatar,
'" width="23" height="23" onload="this.style.visibility=\'visible\'" />','</span><span class="author">',params.author,
':</span><span class="text">',params.text,'</span><span class="time">',params.time,'</span></div>'];
break;
case 'user':
arr = [
'<div class="user" title="',params.name,'"><img src="',
params.gravatar,'" width="30" height="30" onload="this.style.visibility=\'visible\'" /></div>'
];
break;
}
// A single array join is faster than
// multiple concatenations
return arr.join('');
},
// The addChatLine method ads a chat entry to the page
addChatLine : function(params){
// All times are displayed in the user's timezone
var d = new Date();
if(params.time) {
// PHP returns the time in UTC (GMT). We use it to feed the date
// object and later output it in the user's timezone. JavaScript
// internally converts it for us.
d.setUTCHours(params.time.hours,params.time.minutes);
}
params.time = (d.getHours() < 10 ? '0' : '' ) + d.getHours()+':'+
(d.getMinutes() < 10 ? '0':'') + d.getMinutes();
var markup = chat.render('chatLine',params),
exists = $('#chatLineHolder .chat-'+params.id);
if(exists.length){
exists.remove();
}
if(!chat.data.lastID){
// If this is the first chat, remove the
// paragraph saying there aren't any:
$('#chatLineHolder p').remove();
}
// If this isn't a temporary chat:
if(params.id.toString().charAt(0) != 't'){
var previous = $('#chatLineHolder .chat-'+(+params.id - 1));
if(previous.length){
previous.after(markup);
}
else chat.data.jspAPI.getContentPane().append(markup);
}
else chat.data.jspAPI.getContentPane().append(markup);
// As we added new content, we need to
// reinitialise the jScrollPane plugin:
chat.data.jspAPI.reinitialise();
chat.data.jspAPI.scrollToBottom(true);
},
// This method requests the latest chats
// (since lastID), and adds them to the page.
getChats : function(callback){
$.tzGET('getChats',{lastID: chat.data.lastID},function(r){
for(var i=0;i<r.chats.length;i++){
chat.addChatLine(r.chats[i]);
}
if(r.chats.length){
chat.data.noActivity = 0;
chat.data.lastID = r.chats[i-1].id;
}
else{
// If no chats were received, increment
// the noActivity counter.
chat.data.noActivity++;
}
if(!chat.data.lastID){
chat.data.jspAPI.getContentPane().html('<p class="noChats">No chats yet</p>');
}
// Setting a timeout for the next request,
// depending on the chat activity:
var nextRequest = 1000;
// 2 seconds
if(chat.data.noActivity > 3){
nextRequest = 2000;
}
if(chat.data.noActivity > 10){
nextRequest = 5000;
}
// 15 seconds
if(chat.data.noActivity > 20){
nextRequest = 15000;
}
setTimeout(callback,nextRequest);
});
},
// Requesting a list with all the users.
getUsers : function(callback){
$.tzGET('getUsers',function(r){
var users = [];
for(var i=0; i< r.users.length;i++){
if(r.users[i]){
users.push(chat.render('user',r.users[i]));
}
}
var message = '';
if(r.total<1){
message = 'No one is online';
}
else {
message = r.total+' '+(r.total == 1 ? 'person':'people')+' online';
}
users.push('<p class="count">'+message+'</p>');
$('#chatUsers').html(users.join(''));
setTimeout(callback,15000);
});
},
// This method displays an error message on the top of the page:
displayError : function(msg){
var elem = $('<div>',{
id : 'chatErrorMessage',
html : msg
});
elem.click(function(){
$(this).fadeOut(function(){
$(this).remove();
});
});
setTimeout(function(){
elem.click();
},5000);
elem.hide().appendTo('body').slideDown();
}
};
// Custom GET & POST wrappers:
$.tzPOST = function(action,data,callback){
$.post('php/ajax.php?action='+action,data,callback,'json');
}
$.tzGET = function(action,data,callback){
$.get('php/ajax.php?action='+action,data,callback,'json');
}
// A custom jQuery method for placeholder text:
$.fn.defaultText = function(value){
var element = this.eq(0);
element.data('defaultText',value);
element.focus(function(){
if(element.val() == value){
element.val('').removeClass('defaultText');
}
}).blur(function(){
if(element.val() == '' || element.val() == value){
element.addClass('defaultText').val(value);
}
});
return element.blur();
}
And this is the PHP file for grabbing the users DP from gravatar.
<?php
class ChatUser extends ChatBase{
protected $name = '', $gravatar = '';
public function save(){
DB::query("
INSERT INTO webchat_users (name, gravatar)
VALUES (
'".DB::esc($this->name)."',
'".DB::esc($this->gravatar)."'
)");
return DB::getMySQLiObject();
}
public function update(){
DB::query("
INSERT INTO webchat_users (name, gravatar)
VALUES (
'".DB::esc($this->name)."',
'".DB::esc($this->gravatar)."'
) ON DUPLICATE KEY UPDATE last_activity = NOW()");
}
}
?>
Also another bit of code.
<?php
/* The Chat class exploses public static methods, used by ajax.php */
class Chat{
public static function login($name,$email){
if(!$name || !$email){
throw new Exception('Fill in all the required fields.');
}
if(!filter_input(INPUT_POST,'email',FILTER_VALIDATE_EMAIL)){
throw new Exception('Your email is invalid.');
}
// Preparing the gravatar hash:
$gravatar = md5(strtolower(trim($email)));
$user = new ChatUser(array(
'name' => $name,
'gravatar' => $gravatar
));
// The save method returns a MySQLi object
if($user->save()->affected_rows != 1){
throw new Exception('This nick is in use.');
}
$_SESSION['user'] = array(
'name' => $name,
'gravatar' => $gravatar
);
return array(
'status' => 1,
'name' => $name,
'gravatar' => Chat::gravatarFromHash($gravatar)
);
}
public static function checkLogged(){
$response = array('logged' => false);
if($_SESSION['user']['name']){
$response['logged'] = true;
$response['loggedAs'] = array(
'name' => $_SESSION['user']['name'],
'gravatar' => Chat::gravatarFromHash($_SESSION['user']['gravatar'])
);
}
return $response;
}
public static function logout(){
DB::query("DELETE FROM webchat_users WHERE name = '".DB::esc($_SESSION['user']['name'])."'");
$_SESSION = array();
unset($_SESSION);
return array('status' => 1);
}
public static function submitChat($chatText){
if(!$_SESSION['user']){
throw new Exception('You are not logged in');
}
if(!$chatText){
throw new Exception('You haven\' entered a chat message.');
}
$chat = new ChatLine(array(
'author' => $_SESSION['user']['name'],
'gravatar' => $_SESSION['user']['gravatar'],
'text' => $chatText
));
// The save method returns a MySQLi object
$insertID = $chat->save()->insert_id;
return array(
'status' => 1,
'insertID' => $insertID
);
}
public static function getUsers(){
if($_SESSION['user']['name']){
$user = new ChatUser(array('name' => $_SESSION['user']['name']));
$user->update();
}
// Deleting chats older than 5 minutes and users inactive for 30 seconds
DB::query("DELETE FROM webchat_lines WHERE ts < SUBTIME(NOW(),'0:5:0')");
DB::query("DELETE FROM webchat_users WHERE last_activity < SUBTIME(NOW(),'0:0:30')");
$result = DB::query('SELECT * FROM webchat_users ORDER BY name ASC LIMIT 18');
$users = array();
while($user = $result->fetch_object()){
$user->gravatar = Chat::gravatarFromHash($user->gravatar,30);
$users[] = $user;
}
return array(
'users' => $users,
'total' => DB::query('SELECT COUNT(*) as cnt FROM webchat_users')->fetch_object()->cnt
);
}
public static function getChats($lastID){
$lastID = (int)$lastID;
$result = DB::query('SELECT * FROM webchat_lines WHERE id > '.$lastID.' ORDER BY id ASC');
$chats = array();
while($chat = $result->fetch_object()){
// Returning the GMT (UTC) time of the chat creation:
$chat->time = array(
'hours' => gmdate('H',strtotime($chat->ts)),
'minutes' => gmdate('i',strtotime($chat->ts))
);
$chat->gravatar = Chat::gravatarFromHash($chat->gravatar);
$chats[] = $chat;
}
return array('chats' => $chats);
}
public static function gravatarFromHash($hash, $size=23){
return 'http://www.gravatar.com/avatar/'.$hash.'?size='.$size.'&default='.
urlencode('http://www.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?size='.$size);
}
}
?>
You can copy the media stream into a canvas and later manipulate as you wish. Then you can assign an event listener to canvas and on mouse click to create an image. You can do that with canvas toDataUrl method.
So the workflow would be something like this:
accessing the camera with getUserMedia
create a copy of the life media stream and move into the canvas
attach an event listener to canvas and on click...
export the canvas to an image with toDataUrl method
I hope you got the idea.
EDIT:
I just found the same explanation done by me with real code example: https://developer.mozilla.org/en-US/docs/WebRTC/Taking_webcam_photos
I have a situation where I call Facebook.showPermissionDialog('offline_access'...) then later I make an ajax.post call. The ajax.post call fails if the call to the permission dialog was made prior. But it succeeds when the permission dialog was not called prior. Is anyone aware of some relationship between this dialog and ajax.post?
If you want to check out the problem firsthand, visit my app at http://apps.facebook.com/rails_dev (THIS IS A FACEBOOK APP SO YOU MUST GRANT ACCESS TO YOUR PROFILE).
Here's the code that calls Facebook.showPermissionDialog():
<?php
echo $this->jsInit($config);
if(!$userNamespace->newGame) {
$log->debug('NOT new game, calling turnResume()');
echo 'setVarBalance(' . $this->gamePlayerData['funds'] . ');'."\n";
echo 'turnResume();'."\n";
}
echo $this->drawTrack($this->routeData, $this->trainData);
echo $this->drawCityGoods($this->cityGoodsData);
//$link = 'startSetCity()'; //$config->url->absolute->fb->canvas . '/turn/start-set-city';
echo $this->drawCitiesAjax($this->cityDescData);
$log->debug('view: end start-select-city');
if(!$facebook->api_client->users_hasAppPermission('offline_access', $this->fbUserId)):
?>
var dialog = new Dialog().showMessage('Constant Authorization', 'Rails Across Europe is about to request \'Constant Authorization\' to your account. If you don\'t give us constant authorization, Facebook will eventually cause your game to timeout, thereby losing all game information. By granting this authorization, Facebook will not cause your game to timeout. This is the only reason we need this authorization.');
dialog.onconfirm = function() {
Facebook.showPermissionDialog('offline_access', null, false, null);
}
<?php
endif;
?>[
Here's the FBJS code that calls ajax.post:
switch(state) {
case START_SET_CITY:
//new Dialog().showMessage('test', 'START_SET_CITY');
//console.time('start_set_city');
ajax.responseType = Ajax.JSON;
ajax.ondone = function(data) {
//console.time('ondone');
//new Dialog().showMessage('in ajaxSetCity.ondone');
//new Dialog().showMessage('test', 'city=' + dump(data.city, 3) + '::: train=' + dump(data.train, 3));
drawCityAjax(data.city, data.train);
setVarBalance(data.funds);
ajax.responseType = Ajax.JSON;
ajax.post(baseURL + '/turn/start');
//console.timeEnd('ondone');
};
ajax.post(baseURL + '/turn/start-set-city', param); // <=== THIS IS THE AJAX CALL THAT FAILS
var actionPrompt = document.getElementById('action-prompt');
var innerHtml = '<span><div id="action-text">Build Track: Select a city where track building should begin</div>'+
'<div id="action-end">'+
'<input type="button" value="End Track Building" id="next-phase" onClick="moveTrainAuto();" />'+
'</div></span>';
actionPrompt.setInnerXHTML(innerHtml);
var btn = document.getElementById('next-phase');
btn.addEventListener('click', moveTrainAutoEvent);
state = TRACK_CITY_START;
//console.timeEnd('start_set_city');
// get funds balance from backend and call setVarBalance()
break;
I had the same problem if ajax.requireLogin parameter was true. Since you are already asking for extended permissions you can set it to false. Code below works for me:
Facebook.showPermissionDialog("publish_stream", function(permissions) {
var form_data = form.serialize();
var ajax = new Ajax();
ajax.responseType = Ajax.RAW;
ajax.requireLogin = false;
ajax.ondone = function(data) {
console.log("onerror")
};
ajax.onerror = function() {
console.log("onerror")
};
ajax.post("http://foo.example.com/submit", form_data);
return false;
});