Knockout JS - foreach: data-bind not get refreshed - asp.net-mvc-4

SPEC : Building a customer support application.
First time, when a user send a message able to save to DB and show in the view without any issues. And from my end, if i send I am able to push the data to the UI as well DB and the user is able to get the message.
But the issue is when the user send back response again, its not showing up refreshing the UI.
Below is my knockout code,
var csendpoints = {
"Active": "/cs/oc/",
"ActiveClientData": "/cs/gnm/",
"Archive": "/cw/archive/",
"Dequeue": function (id) { return "/cw/dequeue/" + id; },
"GetQueue": "/cs/q/",
"GetArchive": "/cs/h/",
"GetActive": "/cs/active/",
"RecentlyQueued": "/cs/rq/",
"Send": "/cs/send",
"SitRep": "/cw/sitrep",
};
var customerSupportViewModel;
function PagedViewModels(model) {
var self = this;
self.Items = ko.observableArray(model.Items || []);
self.MetaData = ko.observable(model.MetaData || "");
}
function PagedListMetaDataViewModel(model) {
var self = this;
self.FirstItemOnPage = ko.observable(model.FirstItemOnPage || "");
self.HasNextPage = ko.observable(model.HasNextPage || "");
self.HasPreviousPage = ko.observable(model.HasPreviousPage || "");
self.IsFirstPage = ko.observable(model.IsFirstPage || "");
self.IsLastPage = ko.observable(model.IsLastPage || "");
self.LastItemOnPage = ko.observable(model.LastItemOnPage || "");
self.PageCount = ko.observable(model.PageCount || "");
self.PageNumber = ko.observable(model.PageNumber || "");
self.PageSize = ko.observable(model.PageSize || "");
self.TotalItemCount = ko.observable(model.TotalItemCount || "");
}
function ConversationMessageViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientContent = ko.observable(model.ClientContent || "");
self.Content = ko.observable(model.Content || "");
self.ConversationMessageId = ko.observable(model.ConversationMessageId || "");
self.dummy = ko.observable();
self.Sender = ko.observable(model.Sender || "");
self.SentOn = ko.observable(model.SentOn || "");
self.SentOnUniversalSortable = ko.observable(model.SentOnUniversalSortable || "");
self.SentTimeFromNow = ko.computed(function () {
self.dummy();
return moment.utc(self.SentOnUniversalSortable()).fromNow();
});
function updateTimeFromNow() {
setTimeout(function () {
self.dummy.notifySubscribers();
updateTimeFromNow();
},
60 * 1000);
}
updateTimeFromNow();
}
function ConversationViewModels(model) {
var self = this;
self.ClientId = ko.observable(model.ClientId || "");
self.ClientName = ko.observable(model.ClientName || "");
self.ClientProfileImage = ko.observable(model.ClientProfileImage || "");
self.ClientSex = ko.observable(model.ClientSex || "");
self.ClientLanguage = ko.observable(model.ClientLanguage || "");
self.ClientProvince = ko.observable(model.ClientProvince || "");
self.ClientCountry = ko.observable(model.ClientCountry || "");
self.ClientCity = ko.observable(model.ClientCity || "");
self.ClientNameInitial = ko.observable(model.ClientName.substring(0, 1).toUpperCase());
self.ClientContent = ko.observable(model.ClientContent || "");
self.ConversationId = ko.observable(model.ConversationId || "");
self.CreatedOn = ko.observable(model.CreatedOn || "");
self.MessagePreview = ko.observable(model.MessagePreview || "");
self.Messages = ko.observableArray([]);
self.ProfileImageUrl = ko.observable(model.ProfileImageUrl || "");
self.Sender = ko.observable(model.Sender || "");
self.SupportName = ko.observable(customerSupportViewModel.CustomerSupportName());
self.TextBoxContent = ko.observable("");
self.TextBoxEnabled = ko.observable(true);
self.MakeActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
customerSupportViewModel.CurrentConversations.push(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
};
self.MakeRemoveMultiActive = function () {
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.ArchivedConversations.remove(self);
customerSupportViewModel.ActiveConversation(self);
//customerSupportViewModel.CurrentConversations.push(self);
//customerSupportViewModel.CurrentConversations.remove(self);
$.ajax(csendpoints.Active + self.ConversationId(),
{
success: console.log("Debug: Now Active")
});
$("#chat-wrap").mCustomScrollbar("destroy");
$("#chat-wrap").height($(window).height() - 314);
$("#chat-wrap")
.mCustomScrollbar({
axis: "y",
scrollEasing: "linear",
scrollButtons: { enable: true },
theme: "dark-thick",
scrollbarPosition: "inside",
mouseWheelPixels: 400
});
initMap()
};
self.Archive = function () {
customerSupportViewModel.ArchivedConversations.push(self);
customerSupportViewModel.ActiveConversation('');
customerSupportViewModel.CurrentConversations.remove(self);
customerSupportViewModel.WaitingList.remove(self);
customerSupportViewModel.RefreshWaiting();
customerSupportViewModel.RefreshActive();
customerSupportViewModel.RefreshArchive();
$.ajax(csendpoints.Archive + self.ConversationId(),
{
success: console.log("Debug: Now Archived")
});
};
self.SendMessage = function (data) {
self.TextBoxEnabled(false);
var msj = {
"SentOn": moment.utc().format(),
"SentOnUniversalSortable": moment().format(),
"Sender": 1,
"SenderName": customerSupportViewModel.CustomerSupportName(),
"Content": self.TextBoxContent(),
"ClientId": self.ClientId(),
"ClientName": self.ClientName()
};
self.Messages.push(new ConversationMessageViewModels(msj));
$.ajax({
type: "POST",
url: csendpoints.Send,
data: $(data).serialize(),
success: function () { },
complete: function () {
self.TextBoxContent("");
self.TextBoxEnabled(true);
}
});
};
_.each(model.Messages, function (item) {
self.Messages.push(new ConversationMessageViewModels(item));
});
var sortedMessages = _.sortBy(self.Messages(),
function (iteratedItem) { return iteratedItem.SentOnUniversalSortable(); });
self.Messages(sortedMessages);
}
function CustomerSupportViewModel(model) {
var self = this;
customerSupportViewModel = self;
function getWaitingList(page) {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data) {
self.WaitingList.removeAll();
_.each(data.Items, function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
self.WaitingListPaging(data.MetaData);
}
});
}
function getActiveList(page) {
$.ajax(csendpoints.GetActive + page,
{
success: function (data) {
self.CurrentConversations.removeAll();
_.each(data.Items, function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
self.CurrentConversationsPaging(data.MetaData);
}
});
}
function getArchiveList(page) {
$.ajax(csendpoints.GetArchive + page,
{
success: function (data) {
self.ArchivedConversations.removeAll();
_.each(data.Items, function (item) {
self.ArchivedConversations.push(new ConversationViewModels(item));
});
self.ArchivedConversations(data.MetaData);
}
});
}
// Conversations in Queue(i.e. waiting), Current(i.e. active) and Archive (i.e. history)
self.WaitingList = ko.observableArray([]);
self.ArchivedConversations = ko.observableArray([]);
self.CurrentConversations = ko.observableArray([]);
// Paging models
self.WaitingListPaging = ko.observable(model.ConversationsInQueue.MetaData);
self.ArchiveListPaging = ko.observable(model.ArchivedConversations.MetaData);
self.CurrentConversationsPaging = ko.observable(model.CurrentConversations.MetaData);
// The conversation currently active.
self.ActiveConversation = ko.observable();
//setInterval(self.ActiveConversation, 500);
// The name of the WeChatAccount serving the client
self.CustomerSupportName = ko.observable(model.CustomerSupportName);
self.RefreshWaiting = function () {
var page = self.WaitingListPaging().PageNumber;
getWaitingList(page);
};
self.RefreshActive = function () {
var page = self.CurrentConversationsPaging().PageNumber;
getActiveList(page);
//getWaitingList(page);
};
self.RefreshArchive = function () {
var page = self.ArchiveListPaging().PageNumber;
//getArchiveList(page);
getWaitingList(page);
};
//self.RefreshChatActive = function () {
// getActiveList()
//};
// ###### PAGING for Waiting / Queue ######
// Forward paging for the queue / waiting list
self.NextPageWaitingList = function () {
if (self.WaitingListPaging().IsLastPage) return;
var page = self.WaitingListPaging().PageNumber + 1;
getWaitingList(page);
};
self.PreviousPageWaitingList = function (data, e) {
//console.log($(data))
//console.log((e.target) ? e.target : e.srcElement)
if (self.WaitingListPaging().IsFirstPage) return;
var page = self.WaitingListPaging().PageNumber - 1;
getWaitingList(page);
};
// ###### PAGING for Active / Current conversations ######
self.NextPageActiveList = function () {
if (self.CurrentConversations().IsLastPage) return;
var page = self.CurrentConversations().PageNumber + 1;
getActiveList(page);
};
self.PreviousPageActiveList = function () {
if (self.CurrentConversations().IsFirstPage) return;
var page = self.CurrentConversations().PageNumber - 1;
getActiveList(page);
};
// ###### PAGING for History / Archived conversations ######
self.NextPageArchiveList = function () {
if (self.ArchivedConversations().IsLastPage) return;
var page = self.ArchivedConversations().PageNumber + 1;
getArchiveList(page);
};
self.PreviousPageArchiveList = function (data, e) {
if (self.ArchivedConversations().IsFirstPage) return;
var page = self.ArchivedConversations().PageNumber - 1;
getArchiveList(page);
};
_.each(model.ConversationsInQueue.Items,
function (item) { self.WaitingList.push(new ConversationViewModels(item)); });
_.each(model.CurrentConversations.Items,
function (item) { self.CurrentConversations.push(new ConversationViewModels(item)); });
_.each(model.ArchivedConversations.Items,
function (item) { self.ArchivedConversations.push(new ConversationViewModels(item)); });
function getNewQueue(page) {
setTimeout(function () {
$.ajax(csendpoints.GetQueue + page,
{
success: function (data, odata) { getNewQueue(); }
});
},
15 * 1000);
}
}
var customerSupportViewModel = new CustomerSupportViewModel(jsonViewModel);
window.setInterval(customerSupportViewModel.RefreshWaiting, 1000);
window.setInterval(customerSupportViewModel.RefreshActive, 200);
window.setInterval(customerSupportViewModel.Messages, 200);
ko.applyBindings(customerSupportViewModel);
var conversationMessageViewModels = new ConversationMessageViewModels(jsonViewModel);
Here MakeRemoveMultiActive is used as a click function, and in the view, it load the data what user sent as well the response sent from my end. Note, this happens only on click event.
I want to update the UI after the click event so the view is open with the data and autoupdates.
Request experts to shed some light.

Related

angular 8- webrtc-adapter in Firefox not working

I try to run poc which created in angular 8 using webrtc-adpater. Problem is that it is not working in firefox and safari, but working in chrome.
What I observe connection is never connecting or connected in firefox.
I import webrtc in code so anything else we need to handle for firefox and safari.
Basically from Wowza I trying to consume streaming.
Below code sample which working in chrome but not in Firefox and safari:
const ICE_SERVERS: RTCIceServer[] = [
{ urls: ['stun:stun.example.com', 'stun:stun-1.example.com'] },
{ urls: 'stun:stun.l.google.com:19302' }
];
setupSignalingServer(ele) {
const self = this;
window.RTCPeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection;
let signalingConnection = new WebSocket('wss://local.geofabricdev.com/webrtc-session.json');
signalingConnection.binaryType = 'arraybuffer';
signalingConnection.onopen = function (res) {
console.log('connection open');
signalingConnection.onerror = self.errorHandler;
// window.RTCPeerConnection = self.getRTCPeerConnection();
const peerConnection = new RTCPeerConnection(PEER_CONNECTION_CONFIG);
peerConnection.addEventListener('connectionstatechange', event => {
console.log(event);
if (peerConnection.connectionState === 'connected') {
console.log('connected');
}
});
peerConnection.addEventListener('icecandidate', event => {
console.log('peerconnection state5:' + peerConnection.connectionState);
if (event.candidate) {
// signalingChannel.send({'new-ice-candidate': event.candidate});
}
});
peerConnection.addEventListener('track', async event => {
console.log('gotRemoteTrack: kind:' + event.track.kind + ' stream:' + event.streams[0]);
const video = self.createVideo();
// const remoteVideo1 = document.querySelector('#remoteVideo') as HTMLVideoElement;
try {
video.srcObject = event.streams[0];
// remoteVideo1.srcObject = event.streams[0];
} catch (error) {
video.src = window.URL.createObjectURL(event.streams[0]);
}
});
signalingConnection.addEventListener('message', async message => {
console.dir(message);
// self.getSignalMessageCallback(message);
console.log('wsConnection.onmessage: ' + message.data);
const signal = JSON.parse(message.data);
if(signal.status !== 200) {
const video = self.createVideo();
video.poster = '../assets/streaming-error.png';
}
const streamInfoResponse = signal['streamInfo'];
let g = [];
// self.socketStream.some((f) => {
g = self.streams.filter((str) => signal['streamInfo'].streamName === str.streamName);
// if(g.length > 0) {
// return true;
// }
// });
// });
if (streamInfoResponse !== undefined) {
g[0]['sessionId'] = streamInfoResponse.sessionId;
}
console.log('Received signal');
const msgCommand = signal['command'];
console.log(msgCommand);
if (signal.sdp) {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp));
if (signal.sdp) {
const description = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(description);
signalingConnection.send('{"direction":"play", "command":"sendResponse", "streamInfo":' +
JSON.stringify(g[0]) + ', "sdp":' + JSON.stringify(description) + ',"userData":' + JSON.stringify(self.userData) + '}');
}
// })
// .catch(self.errorHandler);
} else if (signal.iceCandidates) {
console.log('ice: ' + JSON.stringify(signal.iceCandidates));
peerConnection.addIceCandidate(new RTCIceCandidate(signal.iceCandidates[0])).catch(self.errorHandler);
}
if ('sendResponse'.localeCompare(msgCommand) === 0) {
if (signalingConnection != null) {
signalingConnection.close();
}
signalingConnection = null;
}
// };
});
signalingConnection.send('{"direction":"play", "command":"getOffer", "streamInfo":' +
JSON.stringify(ele) + ', "userData":' + JSON.stringify(self.userData) + '}');
};
signalingConnection.onclose = function (r) {
console.log('close');
};
}
private createVideo() {
const video = document.createElement('video');
video.classList.add('cameraPanel');
video.autoplay = true;
video.preload = 'true';
video.muted = true;
video.width = 300;
const remoteVideo = document.querySelector('#videoContainer') as HTMLElement;
remoteVideo.appendChild(video);
return video;
}

Webrtc video chat not working in different networks

I am trying to implement video chat with webrtc using code from google code labs . It works fine on same network but does not work on different network. i have installed coturn on my server. I am not sure if it is working correctly.
Any one used google codelabs code and made it working?
This is the modified main.js file
'use strict';
var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var pcConfig = {
"iceServers":[
{'urls': 'stun:stun.l.google.com:19302'},
{"urls":["turn:78.129.167.90"],"username":"wyzturner1","credential":"wyzturnp2ss"}]
};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
/////////////////////////////////////////////
var room = 'foo';
// Could prompt for room name:
// room = prompt('Enter room name:');
var socket = io.connect('https://www.samplesite.com:8080');
if (room !== '') {
socket.emit('create or join', room);
console.log('Attempted to create or join room', room);
}
socket.on('created', function(room) {
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function(room) {
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
isChannelReady = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message) {
console.log('Client sending message: ', message);
socket.emit('message', message);
}
// This client receives a message
socket.on('message', function(message) {
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message));
doAnswer();
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
navigator.mediaDevices.getUserMedia({
audio: false,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
function gotStream(stream) {
console.log('Adding local stream.');
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
var constraints = {
video: true
};
console.log('Getting user media with constraints', constraints);
//if (location.hostname !== 'localhost') {
// requestTurn(
// 'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
// );
//}
function maybeStart() {
console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
console.log('>>>>>> creating peer connection');
createPeerConnection();
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function() {
sendMessage('bye');
};
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(pcConfig);
pc.addStream(localStream);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.oniceconnectionstatechange = function(){
console.log('ICE state: ',pc.iceConnectionState);
}
pc.onremovestream = handleRemoteStreamRemoved;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleIceCandidate(event) {
console.log('icecandidate event: ', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate
});
} else {
console.log('End of candidates.');
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleCreateOfferError(event) {
console.log('createOffer() error: ', event);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer().then(
setLocalAndSendMessage,
onCreateSessionDescriptionError
);
}
function setLocalAndSendMessage(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
// sessionDescription.sdp = preferOpus(sessionDescription.sdp);
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message', sessionDescription);
sendMessage(sessionDescription);
}
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function requestTurn(turnURL) {
var turnExists = false;
for (var i in pcConfig.iceServers) {
if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
turnExists = true;
turnReady = true;
break;
}
}
// if (!turnExists) {
// console.log('Getting TURN server from ', turnURL);
// // No TURN server. Get one from computeengineondemand.appspot.com:
// var xhr = new XMLHttpRequest();
// xhr.onreadystatechange = function() {
// if (xhr.readyState === 4 && xhr.status === 200) {
// var turnServer = JSON.parse(xhr.responseText);
// console.log('Got TURN server: ', turnServer);
// pcConfig.iceServers.push({
// 'url': 'turn:' + turnServer.username + '#' + turnServer.turn,
// 'credential': turnServer.password
// });
// turnReady = true;
// }
// };
// xhr.open('GET', turnURL, true);
// xhr.send();
// }
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
console.log('Session terminated.');
stop();
isInitiator = false;
}
function stop() {
isStarted = false;
// isAudioMuted = false;
// isVideoMuted = false;
pc.close();
pc = null;
}
///////////////////////////////////////////
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex;
// Search for m line.
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
}
}
if (mLineIndex === null) {
return sdp;
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex],
opusPayload);
}
break;
}
}
// Remove CN in m line and sdp.
sdpLines = removeCN(sdpLines, mLineIndex);
sdp = sdpLines.join('\r\n');
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
var elements = mLine.split(' ');
var newLine = [];
var index = 0;
for (var i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
if (elements[i] !== payload) {
newLine[index++] = elements[i];
}
}
return newLine.join(' ');
}
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
var mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (var i = sdpLines.length - 1; i >= 0; i--) {
var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
var cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
}
// Remove CN line in sdp
sdpLines.splice(i, 1);
}
}
sdpLines[mLineIndex] = mLineElements.join(' ');
return sdpLines;
}

Trello API add base64 data image as an attachement

I'd like to send base64 image as an attachment to a trello card through the API
POST /1/cards/[card id or shortlink]/attachments
There's a file field but it does not specify how should look the data there.
Refs: https://developers.trello.com/advanced-reference/card#post-1-cards-card-id-or-shortlink-attachments
Any idea?
Short answer: Trello's API only works to attach binary data via multipart/form-data. Examples below.
Long answer:
The Trello API to add attachments and images is frustratingly under-documented. They do have a coffeescript Client.js for those of us using Javascript to simplify all the basic operations: https://trello.com/1/client.coffee
Using the vanilla Client.js file I have been able to attach links and text files. While the CURL example shows pulling a binary file in from a hard drive, that doesn't work for those of us completely on a server or client where we don't have permissions to create a file.
From a LOT of trial and error, I've determined all binary data (images, documents, etc.) must be attached using multipart/form-data. Here is a jQuery snippet that will take a URL to an item, get it into memory, and then send it to Trello.
var opts = {'key': 'YOUR TRELLO KEY', 'token': 'YOUR TRELLO TOKEN'};
var xhr = new XMLHttpRequest();
xhr.open('get', params); // params is a URL to a file to grab
xhr.responseType = 'blob'; // Use blob to get the file's mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData(); // this is the formdata Trello needs
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
I have submitted a new coffeescript for Trello developer support to consider uploading to replace Client.js. It adds a "Trello.upload(url)" that does this work.
I've also attached here for convenience in JS form.
// Generated by CoffeeScript 1.12.4
(function() {
var opts={"version":1,"apiEndpoint":"https://api.trello.com","authEndpoint":"https://trello.com"};
var deferred, isFunction, isReady, ready, waitUntil, wrapper,
slice = [].slice;
wrapper = function(window, jQuery, opts) {
var $, Trello, apiEndpoint, authEndpoint, authorizeURL, baseURL, collection, fn, fn1, i, intentEndpoint, j, key, len, len1, localStorage, location, parseRestArgs, readStorage, ref, ref1, storagePrefix, token, type, version, writeStorage;
$ = jQuery;
key = opts.key, token = opts.token, apiEndpoint = opts.apiEndpoint, authEndpoint = opts.authEndpoint, intentEndpoint = opts.intentEndpoint, version = opts.version;
baseURL = apiEndpoint + "/" + version + "/";
location = window.location;
Trello = {
version: function() {
return version;
},
key: function() {
return key;
},
setKey: function(newKey) {
key = newKey;
},
token: function() {
return token;
},
setToken: function(newToken) {
token = newToken;
},
rest: function() {
var args, error, method, params, path, ref, success;
method = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
ref = parseRestArgs(args), path = ref[0], params = ref[1], success = ref[2], error = ref[3];
opts = {
url: "" + baseURL + path,
type: method,
data: {},
dataType: "json",
success: success,
error: error
};
if (!$.support.cors) {
opts.dataType = "jsonp";
if (method !== "GET") {
opts.type = "GET";
$.extend(opts.data, {
_method: method
});
}
}
if (key) {
opts.data.key = key;
}
if (token) {
opts.data.token = token;
}
if (params != null) {
$.extend(opts.data, params);
}
if (method === 'UPLOAD' && typeof (params) === "string" && params.length > 5) {
var xhr = new XMLHttpRequest();
xhr.open('get', params);
xhr.responseType = 'blob'; // Use blob to get the mimetype
xhr.onload = function() {
var fileReader = new FileReader();
fileReader.onload = function() {
var filename = (params.split('/').pop().split('#')[0].split('?')[0]) || params || '?'; // Removes # or ? after filename
var file = new File([this.result], filename);
var form = new FormData();
form.append("file", file);
$.each(['key', 'token'], function(iter, item) {
form.append(item, opts.data[item] || 'ERROR! Missing "' + item + '"');
});
$.extend(opts, {
method: "POST",
data: form,
cache: false,
contentType: false,
processData: false
});
return $.ajax(opts);
};
fileReader.readAsArrayBuffer(xhr.response); // Use filereader on blob to get content
};
xhr.send();
} else {
return $.ajax(opts);
}
},
authorized: function() {
return token != null;
},
deauthorize: function() {
token = null;
writeStorage("token", token);
},
authorize: function(userOpts) {
var k, persistToken, ref, regexToken, scope, v;
opts = $.extend(true, {
type: "redirect",
persist: true,
interactive: true,
scope: {
read: true,
write: false,
account: false
},
expiration: "30days"
}, userOpts);
regexToken = /[&#]?token=([0-9a-f]{64})/;
persistToken = function() {
if (opts.persist && (token != null)) {
return writeStorage("token", token);
}
};
if (opts.persist) {
if (token == null) {
token = readStorage("token");
}
}
if (token == null) {
token = (ref = regexToken.exec(location.hash)) != null ? ref[1] : void 0;
}
if (this.authorized()) {
persistToken();
location.hash = location.hash.replace(regexToken, "");
return typeof opts.success === "function" ? opts.success() : void 0;
}
if (!opts.interactive) {
return typeof opts.error === "function" ? opts.error() : void 0;
}
scope = ((function() {
var ref1, results;
ref1 = opts.scope;
results = [];
for (k in ref1) {
v = ref1[k];
if (v) {
results.push(k);
}
}
return results;
})()).join(",");
switch (opts.type) {
case "popup":
(function() {
var authWindow, height, left, origin, receiveMessage, ref1, top, width;
waitUntil("authorized", (function(_this) {
return function(isAuthorized) {
if (isAuthorized) {
persistToken();
return typeof opts.success === "function" ? opts.success() : void 0;
} else {
return typeof opts.error === "function" ? opts.error() : void 0;
}
};
})(this));
width = 420;
height = 470;
left = window.screenX + (window.innerWidth - width) / 2;
top = window.screenY + (window.innerHeight - height) / 2;
origin = (ref1 = /^[a-z]+:\/\/[^\/]*/.exec(location)) != null ? ref1[0] : void 0;
authWindow = window.open(authorizeURL({
return_url: origin,
callback_method: "postMessage",
scope: scope,
expiration: opts.expiration,
name: opts.name
}), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
receiveMessage = function(event) {
var ref2;
if (event.origin !== authEndpoint || event.source !== authWindow) {
return;
}
if ((ref2 = event.source) != null) {
ref2.close();
}
if ((event.data != null) && /[0-9a-f]{64}/.test(event.data)) {
token = event.data;
} else {
token = null;
}
if (typeof window.removeEventListener === "function") {
window.removeEventListener("message", receiveMessage, false);
}
isReady("authorized", Trello.authorized());
};
return typeof window.addEventListener === "function" ? window.addEventListener("message", receiveMessage, false) : void 0;
})();
break;
default:
window.location = authorizeURL({
redirect_uri: location.href,
callback_method: "fragment",
scope: scope,
expiration: opts.expiration,
name: opts.name
});
}
},
addCard: function(options, next) {
var baseArgs, getCard;
baseArgs = {
mode: 'popup',
source: key || window.location.host
};
getCard = function(callback) {
var height, left, returnUrl, top, width;
returnUrl = function(e) {
var data;
window.removeEventListener('message', returnUrl);
try {
data = JSON.parse(e.data);
if (data.success) {
return callback(null, data.card);
} else {
return callback(new Error(data.error));
}
} catch (error1) {}
};
if (typeof window.addEventListener === "function") {
window.addEventListener('message', returnUrl, false);
}
width = 500;
height = 600;
left = window.screenX + (window.outerWidth - width) / 2;
top = window.screenY + (window.outerHeight - height) / 2;
return window.open(intentEndpoint + "/add-card?" + $.param($.extend(baseArgs, options)), "trello", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
};
if (next != null) {
return getCard(next);
} else if (window.Promise) {
return new Promise(function(resolve, reject) {
return getCard(function(err, card) {
if (err) {
return reject(err);
} else {
return resolve(card);
}
});
});
} else {
return getCard(function() {});
}
}
};
ref = ["GET", "PUT", "POST", "DELETE", "UPLOAD"];
fn = function(type) {
return Trello[type.toLowerCase()] = function() {
return this.rest.apply(this, [type].concat(slice.call(arguments)));
};
};
for (i = 0, len = ref.length; i < len; i++) {
type = ref[i];
fn(type);
}
Trello.del = Trello["delete"];
ref1 = ["actions", "cards", "checklists", "boards", "lists", "members", "organizations", "lists"];
fn1 = function(collection) {
return Trello[collection] = {
get: function(id, params, success, error) {
return Trello.get(collection + "/" + id, params, success, error);
}
};
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
collection = ref1[j];
fn1(collection);
}
window.Trello = Trello;
authorizeURL = function(args) {
var baseArgs;
baseArgs = {
response_type: "token",
key: key
};
return authEndpoint + "/" + version + "/authorize?" + $.param($.extend(baseArgs, args));
};
parseRestArgs = function(arg) {
var error, params, path, success;
path = arg[0], params = arg[1], success = arg[2], error = arg[3];
if (isFunction(params)) {
error = success;
success = params;
params = {};
}
path = path.replace(/^\/*/, "");
return [path, params, success, error];
};
localStorage = window.localStorage;
if (localStorage != null) {
storagePrefix = "trello_";
readStorage = function(key) {
return localStorage[storagePrefix + key];
};
writeStorage = function(key, value) {
if (value === null) {
return delete localStorage[storagePrefix + key];
} else {
return localStorage[storagePrefix + key] = value;
}
};
} else {
readStorage = writeStorage = function() {};
}
};
deferred = {};
ready = {};
waitUntil = function(name, fx) {
if (ready[name] != null) {
return fx(ready[name]);
} else {
return (deferred[name] != null ? deferred[name] : deferred[name] = []).push(fx);
}
};
isReady = function(name, value) {
var fx, fxs, i, len;
ready[name] = value;
if (deferred[name]) {
fxs = deferred[name];
delete deferred[name];
for (i = 0, len = fxs.length; i < len; i++) {
fx = fxs[i];
fx(value);
}
}
};
isFunction = function(val) {
return typeof val === "function";
};
wrapper(window, jQuery, opts);
}).call(this);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>

Ripple exchanges websocket equivalent for ripple data apiv2

I'm trying to get the exchanges in ripple and I found this data API and its working. But I want to use the ripple websocket tool for some reasons. Is there any websocket equivalent for this data API?
I think there is equivalent if you use "tx_history" command in the socket but Sorry to tell you that the json result are not equal to your specific data result.
ripple data apiv2 is being played by ajax. see the result json formatter in ripple for exchange:
} else if (resp.rows.length) {
resp.rows[0] = {
base_currency: resp.rows[0].base_currency,
base_issuer: resp.rows[0].base_issuer,
base_amount: resp.rows[0].base_amount,
counter_amount: resp.rows[0].counter_amount,
counter_currency: resp.rows[0].counter_currency,
counter_issuer: resp.rows[0].counter_issuer,
rate: resp.rows[0].rate,
executed_time: resp.rows[0].executed_time,
ledger_index: resp.rows[0].ledger_index,
buyer: resp.rows[0].buyer,
seller: resp.rows[0].seller,
taker: resp.rows[0].taker,
provider: resp.rows[0].provider,
autobridged_currency: resp.rows[0].autobridged_currency,
autobridged_issuer: resp.rows[0].autobridged_issuer,
offer_sequence: resp.rows[0].offer_sequence,
tx_type: resp.rows[0].tx_type,
tx_index: resp.rows[0].tx_index,
node_index: resp.rows[0].node_index,
tx_hash: resp.rows[0].tx_hash
};
}
res.csv(resp.rows, filename);
} else {
res.json({
result: 'success',
count: resp.rows.length,
marker: resp.marker,
exchanges: resp.rows
});
} }
and it can be only access by get url :
route: '/v2/exchanges/{:base}/{:counter}'
that is bind in there server.js:
app.get('/v2/exchanges/:base/:counter', routes.getExchanges);
and last hint this is their database query using hbase:
HbaseClient.getExchanges = function (options, callback) {
var base = options.base.currency + '|' + (options.base.issuer || '');
var counter = options.counter.currency + '|' + (options.counter.issuer
||''); var table;
var keyBase;
var startRow;
var endRow;
var descending;
var columns;
if (counter.toLowerCase() > base.toLowerCase()) {
keyBase = base + '|' + counter;
} else {
keyBase = counter + '|' + base;
options.invert = true; }
if (!options.interval) {
table = 'exchanges';
descending = options.descending ? true : false;
options.unreduced = true;
//only need certain columns
if (options.reduce) {
columns = [
'd:base_amount',
'd:counter_amount',
'd:rate',
'f:executed_time',
'f:buyer',
'f:seller',
'f:taker'
];
}
} else if (exchangeIntervals.indexOf(options.interval) !== -1) {
keyBase = options.interval + '|' + keyBase;
descending = options.descending ? true : false;
table = 'agg_exchanges';
} else {
callback('invalid interval: ' + options.interval);
return; }
startRow = keyBase + '|' + options.start.hbaseFormatStartRow();
endRow = keyBase + '|' + options.end.hbaseFormatStopRow();
if (options.autobridged) {
options.filterstring = "DependentColumnFilter('f', 'autobridged_currency')";
if (columns) {
columns.push('f:autobridged_currency');
} }
this.getScanWithMarker(this, {
table: table,
startRow: startRow,
stopRow: endRow,
marker: options.marker,
limit: options.limit,
descending: descending,
columns: columns,
filterString: options.filterstring }, function (err, resp) {
if (!resp) {
resp = {rows: []};
}
if (!resp.rows) {
resp.rows = [];
}
if (options.reduce && options.unreduced) {
if (descending) {
resp.rows.reverse();
}
resp.reduced = reduce(resp.rows);
} else if (table === 'exchanges') {
resp.rows = formatExchanges(resp.rows);
} else {
resp.rows = formatAggregates(resp.rows);
}
callback(err, resp); });
/** * formatExchanges */
function formatExchanges (rows) {
rows.forEach(function(row) {
var key = row.rowkey.split('|');
delete row.base_issuer;
delete row.base_currency;
delete row.counter_issuer;
delete row.counter_currency;
row.base_amount = parseFloat(row.base_amount);
row.counter_amount = parseFloat(row.counter_amount);
row.rate = parseFloat(row.rate);
row.offer_sequence = Number(row.offer_sequence || 0);
row.ledger_index = Number(row.ledger_index);
row.tx_index = Number(key[6]);
row.node_index = Number(key[7]);
row.time = utils.unformatTime(key[4]).unix();
});
if (options.invert) {
rows = invertPair(rows);
}
return rows; }
/** * formatAggregates */
function formatAggregates (rows) {
rows.forEach(function(row) {
var key = row.rowkey.split('|');
row.base_volume = parseFloat(row.base_volume),
row.counter_volume = parseFloat(row.counter_volume),
row.buy_volume = parseFloat(row.buy_volume),
row.count = Number(row.count);
row.open = parseFloat(row.open);
row.high = parseFloat(row.high);
row.low = parseFloat(row.low);
row.close = parseFloat(row.close);
row.vwap = parseFloat(row.vwap);
row.close_time = Number(row.close_time);
row.open_time = Number(row.open_time);
});
if (options.invert) {
rows = invertPair(rows);
}
return rows; }
/** * if the base/counter key was inverted, we need to swap * some of the values in the results */
function invertPair (rows) {
var swap;
var i;
if (options.unreduced) {
for (i=0; i<rows.length; i++) {
rows[i].rate = 1/rows[i].rate;
//swap base and counter vol
swap = rows[i].base_amount;
rows[i].base_amount = rows[i].counter_amount;
rows[i].counter_amount = swap;
//swap buyer and seller
swap = rows[i].buyer;
rows[i].buyer = rows[i].seller;
rows[i].seller = swap;
}
} else {
for (i=0; i<rows.length; i++) {
//swap base and counter vol
swap = rows[i].base_volume;
rows[i].base_volume = rows[i].counter_volume;
rows[i].counter_volume = swap;
//swap high and low
swap = 1/rows[i].high;
rows[i].high = 1/rows[i].low;
rows[i].low = swap;
//invert open, close, vwap
rows[i].open = 1/rows[i].open;
rows[i].close = 1/rows[i].close;
rows[i].vwap = 1/rows[i].vwap;
//invert buy_volume
rows[i].buy_volume /= rows[i].vwap;
}
}
return rows; }
/** * reduce * reduce all rows */
function reduce (rows) {
var buyVolume = 0;
var reduced = {
open: 0,
high: 0,
low: Infinity,
close: 0,
base_volume: 0,
counter_volume: 0,
buy_volume: 0,
count: 0,
open_time: 0,
close_time: 0
};
rows = formatExchanges(rows);
// filter out small XRP amounts
rows = rows.filter(function(row) {
if (options.base.currency === 'XRP' && row.base_amount < 0.0005) {
return false;
} else if (options.counter.currency === 'XRP' && row.counter_amount < 0.0005) {
return false;
} else {
return true;
}
});
if (rows.length) {
reduced.open_time = moment.unix(rows[0].time).utc().format();
reduced.close_time = moment.unix(rows[rows.length-1].time).utc().format();
reduced.open = rows[0].rate;
reduced.close = rows[rows.length -1].rate;
reduced.count = rows.length;
} else {
reduced.low = 0;
return reduced;
}
rows.forEach(function(row) {
reduced.base_volume += row.base_amount;
reduced.counter_volume += row.counter_amount;
if (row.rate < reduced.low) reduced.low = row.rate;
if (row.rate > reduced.high) reduced.high = row.rate;
if (row.buyer === row.taker) {
reduced.buy_volume += row.base_amount;
}
});
reduced.vwap = reduced.counter_volume / reduced.base_volume;
return reduced; } };
Maybe you should make a custom websocket that make your RPC call upgrade to 1.1 http protocol (ws).
In nodejs you can simply
// for http
var http = require('http');
// for websocket
var ws = require("nodejs-websocket")
var options = {
host: 'URL-RPC-HERE',
port: '80',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
}
};
var req = http.request(options, function(res) {
// after getting the response wich is the <res>
// we can upgrade it to ws
upToWebsocket(res);
});
//Upgrade to websocket
var upToWebsocket = function(json) {
var server = ws.createServer(function (conn) {
conn.on("json", function (str) {
conn.sendText(str.toUpperCase()+"!!!")
})
conn.on("close", function (code, reason) {
console.log("Connection closed")
})
}).listen(8001)
}
And also if you have Rippled running on a server this does not help because there's no RPC or WS that supports exchange API.

Parse multiple pages with phantomjs

I have made a code that parses all URL-s from a page. Next, I would like to get a href from every parsed URL <div class="holder"></div> and output it to a file and sepparate with a comma.
So far I have made this code. It is able to find all the URL-s need to be parsed and collects them to a comma sepparated file called output2.txt.
var resourceWait = 300,
maxRenderWait = 10000,
url = 'URL TO PARSE HREF-s FROM';
var page = require('webpage').create(),
count = 0,
forcedRenderTimeout,
renderTimeout;
page.viewportSize = { width: 1280, height : 1024 };
function doRender() {
var fs = require('fs');
var path = 'output2.txt';
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
fs.write(path,page.evaluate(function() {
return $('.urlDIV').find('a')
.map(function() {
return this.href;})
.get()
.join(',');
}), 'w');
phantom.exit()
});
}
page.onResourceRequested = function (req) {
count += 1;
clearTimeout(renderTimeout);
};
page.onResourceReceived = function (res) {
if (!res.stage || res.stage === 'end') {
count -= 1;
if (count === 0) {
renderTimeout = setTimeout(doRender, resourceWait);
}
}
};
page.open(url, function (status) {
if (status !== "success") {
phantom.exit();
} else {
forcedRenderTimeout = setTimeout(function () {
console.log(count);
doRender();
}, maxRenderWait);
}
});
Thanks in advance,
Martti