My mobile titanium app is loading data from a remote url, in the form of a xml file, that I have generated. The idea is that the app is downloading that xml and processes it into a SQLlite db. But I was thinking, I could also replace the server side generated xml with a ready sqlite file with all the data in it. That way, I don't need to do any processing client side, which saves some of the user's time.
Is this a good idea? If yes, how would I "fake" generate a sqlite file? There are no headers that I need to pass on, so that's not the issue, but what I noticed when I opened an .sqlite file with coda, that there are weird characters meaning the encoding must be different.
Thanks!
There is no mechanism in any browsers I am aware of which would allow you to transfer a binary image of a sqlite database (even assuming byte ordering and character coding were not an issue).
Certainly, it may improve performance to maintain / transfer the data in a format closer to a set of CREATE and INSERT statements than atomic XML.
You can create a SQLite database in many programs - I use MesaSQLLite. It is just a binary file. Here is an example of downloading and installing on iOS. I have not tested it on Android.
var win = Ti.UI.createWindow({
backgroundColor: '#eee'
});
var button1 = Ti.UI.createButton({
width: '60%',
height: 30,
top: 5,
title: '1) Get Database'
});
var button2 = Ti.UI.createButton({
width: '60%',
height: 30,
top: 40,
title: '2) Install Database'
});
var button3 = Ti.UI.createButton({
width: '60%',
height: 30,
top: 75,
title: '3) Query Database'
});
var msg = Ti.UI.createLabel({
top: 110,
width: '80%',
height: 120,
text: 'click the buttons in order',
font: {
fontFamily: 'Courier',
fontSize: 12
},
textAlign: 'center'
});
win.add(button1);
win.add(button2);
win.add(button3);
win.add(msg);
function localFile() {
var localDbFile = 'testdb.db';
return Ti.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory, localDbFile);
}
button1.addEventListener('click',
function() {
msg.text = "Getting Database from Server";
var xhr = Ti.Network.createHTTPClient();
xhr.open("GET", "http://www.titaniumdevelopment.com.au/testdb.db");
xhr.onload = function() {
if (xhr.status == 200) {
msg.text = "saving database";
var file = localFile();
file.write(this.responseData);
msg.text = "database saved as " + file.nativePath;
} else {
msg.text = "Unable to get database. Response code was " + xhr.status;
}
};
xhr.onerror = function() {
msg.text = "Error fetching database";
};
xhr.send();
});
button2.addEventListener('click',
function() {
var file = localFile();
if (file.exists()) {
msg.text = "installing database";
Ti.Database.install(file.nativePath, 'test');
msg.text = "database installed";
} else {
msg.text = "unable to find database";
}
});
button3.addEventListener('click',
function() {
try {
var db = Titanium.Database.open('test');
var rows = db.execute('SELECT * FROM TIPS');
msg.text = 'ROW COUNT = ' + rows.getRowCount();
rows.close();
db.close();
} catch(e){
msg.text = "unable to open database";
}
});
win.open();
Note: I'm not sure the App Store would approve this as they are not too keen on fetching data remotely.
Related
I am using a custom slash command in Slack to add data to a Google Doc via Google Apps Scripts, however, when the script finishes running (successfully), Google Script sends this to Slack, which users will interpret as an error:
<DOCTYPE html><html><head><link rel="shortcut icon" href="//ssl.gstatic.com/docs/script/images/favicon.ico"><title>Error</title><style type="text/css">body {background-color: #fff; margin: 0; padding: 0;}.errorMessage {font-family: Arial,sans-serif; font-size: 12pt; font-weight: bold; line-height: 150%; padding-top: 25px;}</style></head><body><div><img src="//ssl.gstatic.com/docs/script/images/logo.png"></div><center>The script completed but did not return anything.</center></body></html>
How can I prevent this message from being sent to Slack?
function doPost(request) {
var sheets = SpreadsheetApp.openById('id-would-go-here');
var params = request.parameters;
var nR = getNextRow(sheets) + 1;
if (params.token == "token-would-go-here") {
// FALL BACK TO DEFAULT TEXT IF NO ORDER PROVIDED
var order = params.text || "No Order Entered";
var employee = params.user_name || "Name Error";
// RECORD TIMESTAMP AND USER NAME IN SPREADSHEET
sheets.getRangeByName('date').getCell(nR,1).setValue(new Date());
sheets.getRangeByName('employee').getCell(nR,1).setValue(employee);
// RECORD UPDATE INFORMATION INTO SPREADSHEET
sheets.getRangeByName('order').getCell(nR,1).setValue(order);
var channel = "lunch-orders";
postResponse(channel,order,employee);
var eph_url = params.response_url;
var eph_response = UrlFetchApp.fetch(eph_url,200);
} else {
return;
}
}
function postResponse(channel, order, employee) {
var payload = {
"channel": "#lunch-orders",
"username": "Lunch Order",
"link_names": 1,
"attachments":[
{
"fallback": "A lunch order was placed, but the display here is a little messed up. Check the Google Doc to view the order.",
"mrkdwn_in": ["pretext"],
"color": "good",
"fields":[
{
"title":"" + employee + "'s Lunch Order",
"value": "" + order + "",
"short":false
}
]
}
]
};
var url = 'incoming-webhook-url-goes-here';
var options = {
'method': 'post',
'payload': JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(url,options);
}
Your setup is a little off. You don't POST the data back, you return it using ContentService. Now a small issue is that you have to run the webapp as anonymous authentication. What I do is save the slack token in the script property services and make sure the requesting Slack app's token matches.
Here is an example webhook that simply writes the parameters passed to the switch command to a spreadsheet.
function doPost(e) {
var returnMessage;
var slackToken = PropertiesService.getScriptProperties().getProperty("SlackToken") ;
var postData = parseParams(e.postData.getDataAsString());
if(postData.token === slackToken){
SpreadsheetApp.openById('.....').getSheetByName("Log").appendRow([postData]);
returnMessage = "Your record has been logged."
}else{
returnMessage = "Invalid Token"
}
return ContentService.createTextOutput(JSON.stringify({text:returnMessage})).setMimeType(ContentService.MimeType.JSON);
}
function parseParams(postData){
var postObj = {}
postData.split("&").map(function(param){var thisParam = param.split("="); postObj[thisParam[0]] = thisParam[1]});
return postObj;
}
I'm still rather new to Appcelerator and using JSON files with JavaScript. I know I am parsing in the JSON file correctly, and breaking the data apart correctly, but for some reason the app can't seem to properly fetch the JSON data, and I invariably always get the onerror firing instead of the onload. Here is my code:
// Set the background color with no windows or tabs
Titanium.UI.setBackgroundColor('#000');
// Create the window
var win1 = Titanium.UI.createWindow({
title:'Challenge Window',
backgroundColor:'#fff',
});
// Store the image and its properties
var image = Titanium.UI.createImageView({
image: "https://myavantiservices.files.wordpress.com/2015/02/helloworld.gif",
height: 380,
width: 380,
center: 512,
top: -50
});
var table = Ti.UI.createTableView();
var tableData = [];
var json, line1, city, state, zip, appfile_id, id, body;
// Parse our JSON file using onload
var url = "https://raw.githubusercontent.com/JordanAshton/CodingChallenge/master/JSONtwitterOutput.txt";
var xhr = Ti.Network.createHTTPClient({
onload: function() {
json = JSON.parse(this.responseText);
for (var i = 0; i < json.things.length; i++){
var row = Ti.UI.createTableViewRow({
className: 'row',
objectName: 'row',
rowID: i,
height: 100,
borderColor: accentColor,
borderWidth: 1,
borderRadius: 5,
backgroundImage:'../images/opbg.png',
filter:json.data[i].line1 + "\n" + json.data[i].city + "," + json.data[i].state + " " + json.data[i].zip,
appfile_id: json.data[i].appfile_id,
message_id: json.data[i].id,
messagebody: json.data[i].body
});
tableData.push(row);
}
table.setData(tableData);
},
onerror: function(e) {
Ti.API.debug("STATUS: " + this.status);
Ti.API.debug("TEXT: " + this.responseText);
Ti.API.debug("ERROR: " + e.error);
alert('There was an error retrieving the remote data. Try again.');
},
timeout:5000
});
xhr.open("GET", url);
xhr.send();
// Add the image to the window and open the window
win1.add(image);
win1.add(table);
win1.open();
The JSON file I am parsing:
{"results":[
{"text":"#twitterapi https://code.google.com/archive/p/twitter-api/issues/353",
"to_user_id":396524,
"to_user":"TwitterAPI",
"from_user":"jkoum",
"metadata":
{
"result_type":"popular",
"recent_retweets": 109
},
"id":1478555574,
"from_user_id":1833773,
"iso_language_code":"nl",
"source":"twitter< /a>",
"profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg",
"created_at":"Wed, 08 Apr 2009 19:22:10 +0000"},
... truncated ...],
"since_id":0,
"max_id":1480307926,
"refresh_url":"?since_id=1480307926&q=%40twitterapi",
"results_per_page":15,
"next_page":"?page=2&max_id=1480307926&q=%40twitterapi",
"completed_in":0.031704,
"page":1,
"query":"%40twitterapi"}
}
The root node of the JSON file you are parsing is "results"; but you are iterating up to json.things.length, which at that point should be 0, then no row is created and added to the tableView.
Also those json.data[i].<some_property> look like out of context.
Hth.
I created (I copied) a chat server using Node.JS and the server is on my LocalHost: 127.0.0.1, only I can use the chat, but I want that anyone can use the server too, so I want to know how to put this Chat server in my real server:
http://sistema.agrosys.com.br/sistema/padrao/HTML5/WebSocket/
And : http://calnetaskmanager.herokuapp.com/
But I don't now how to put my chat-server.js into my Heroku Server.
What should I do to make it possible.
Thanks in advance
If you want to see what happens: client side on my server
Client Side:
$(function() {
"use strict";
// for better performance - to avoid searching in DOM
var content = $('#content');
var input = $('#input');
var status = $('#status');
// my color assigned by the server
var myColor = false;
// my name sent to the server
var myName = false;
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
// if browser doesn't support WebSocket, just show some notification and exit
if (!window.WebSocket) {
content.html($('<p>', {
text: 'Sorry, but your browser doesn\'t ' + 'support WebSockets.'
}));
input.hide();
$('span').hide();
return;
}
// open connection
var connection = new WebSocket('ws://127.0.0.1:1337');
connection.onopen = function() {
// first we want users to enter their names
input.removeAttr('disabled');
status.text('Choose name:');
};
connection.onerror = function(error) {
// just in there were some problems with conenction...
content.html($('<p>', {
text: 'Sorry, but there\'s some problem with your ' + 'connection or the server is down.'
}));
};
// most important part - incoming messages
connection.onmessage = function(message) {
// try to parse JSON message. Because we know that the server always returns
// JSON this should work without any problem but we should make sure that
// the massage is not chunked or otherwise damaged.
try {
var json = JSON.parse(message.data);
} catch (e) {
console.log('This doesn\'t look like a valid JSON: ', message.data);
return;
}
// NOTE: if you're not sure about the JSON structure
// check the server source code above
if (json.type === 'color') { // first response from the server with user's color
myColor = json.data;
status.text(myName + ': ').css('color', myColor);
input.removeAttr('disabled').focus();
// from now user can start sending messages
} else if (json.type === 'history') { // entire message history
// insert every single message to the chat window
for (var i = 0; i < json.data.length; i++) {
addMessage(json.data[i].author, json.data[i].text,
json.data[i].color, new Date(json.data[i].time));
}
} else if (json.type === 'message') { // it's a single message
input.removeAttr('disabled'); // let the user write another message
addMessage(json.data.author, json.data.text,
json.data.color, new Date(json.data.time));
} else {
console.log('Hmm..., I\'ve never seen JSON like this: ', json);
}
};
/**
* Send mesage when user presses Enter key
*/
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (!msg) {
return;
}
// send the message as an ordinary text
connection.send(msg);
$(this).val('');
// disable the input field to make the user wait until server
// sends back response
input.attr('disabled', 'disabled');
// we know that the first message sent from a user their name
if (myName === false) {
myName = msg;
}
}
});
/**
* This method is optional. If the server wasn't able to respond to the
* in 3 seconds then show some error message to notify the user that
* something is wrong.
*/
setInterval(function() {
if (connection.readyState !== 1) {
status.text('Error');
input.attr('disabled', 'disabled').val('Unable to comminucate ' + 'with the WebSocket server.');
}
}, 3000);
/**
* Add message to the chat window
*/
function addMessage(author, message, color, dt) {
content.prepend('<p><span style="color:' + color + '">' + author + '</span> # ' +
+(dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':' + (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes()) + ': ' + message + '</p>');
}
});
* {
font-family: tahoma;
font-size: 12px;
padding: 0px;
margin: 0px;
}
p {
line-height: 18px;
}
div {
width: 500px;
margin-left: auto;
margin-right: auto;
}
#content {
padding: 5px;
background: #ddd;
border-radius: 5px;
overflow-y: scroll;
border: 1px solid #CCC;
margin-top: 10px;
height: 160px;
}
#input {
border-radius: 2px;
border: 1px solid #ccc;
margin-top: 10px;
padding: 5px;
width: 400px;
}
#status {
width: 88px;
display: block;
float: left;
margin-top: 15px;
}
<!DOCTYPE html>
<html>
<head>
<style type="text/css"></style>
<meta charset="utf-8">
<title>WebSockets - Simple chat</title>
</head>
<body>
<div id="content"></div>
<div>
<span id="status">Choose name:</span>
<input type="text" id="input">
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="./frontend.js"></script>
</body>
Server Side
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
"use strict";
// Optional. You will see this name in eg. 'ps' or 'top' command
process.title = 'node-chat';
// Port where we'll run the websocket server
var webSocketsServerPort = 1337;
// websocket and http servers
var webSocketServer = require('websocket').server;
var http = require('http');
/**
* Global variables
*/
// latest 100 messages
var history = [];
// list of currently connected clients (users)
var clients = [];
/**
* Helper function for escaping input strings
*/
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/</g, '<')
.replace(/>/g, '>').replace(/"/g, '"');
}
// Array with some colors
var colors = ['red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange'];
// ... in random order
colors.sort(function(a, b) {
return Math.random() > 0.5;
});
/**
* HTTP server
*/
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
});
/**
* WebSocket server
*/
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket request is just
// an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6
httpServer: server
});
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
// accept connection - you should check 'request.origin' to make sure that
// client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
var userName = false;
var userColor = false;
console.log((new Date()) + ' Connection accepted.');
// send back chat history
if (history.length > 0) {
connection.sendUTF(JSON.stringify({
type: 'history',
data: history
}));
}
// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
if (userName === false) { // first message sent by user is their name
// remember user name
userName = htmlEntities(message.utf8Data);
// get random color and send it back to the user
userColor = colors.shift();
connection.sendUTF(JSON.stringify({
type: 'color',
data: userColor
}));
console.log((new Date()) + ' User is known as: ' + userName + ' with ' + userColor + ' color.');
} else { // log and broadcast the message
console.log((new Date()) + ' Received Message from ' + userName + ': ' + message.utf8Data);
// we want to keep history of all sent messages
var obj = {
time: (new Date()).getTime(),
text: htmlEntities(message.utf8Data),
author: userName,
color: userColor
};
history.push(obj);
history = history.slice(-100);
// broadcast message to all connected clients
var json = JSON.stringify({
type: 'message',
data: obj
});
for (var i = 0; i < clients.length; i++) {
clients[i].sendUTF(json);
}
}
}
});
// user disconnected
connection.on('close', function(connection) {
if (userName !== false && userColor !== false) {
console.log((new Date()) + " Peer " + connection.remoteAddress + " disconnected.");
// remove user from the list of connected clients
clients.splice(index, 1);
// push back user's color to be reused by another user
colors.push(userColor);
}
});
});
you need to host your Node.js code somewhere, for example on Heroku, they have free plans for Node.js, you will then be given a public url, which you can distribute to others
Make it available to everyone... This is easy.
Deploy your code to a publicly available host -- say Heroku as
mentioned by #dark_ruby in his answer.
Provide a computing device with a browser (a nexus 7 will do) to
every person in the entire world
Configure it to have your service as the default home page
Done.
I have a web application that performs a Jquery .post request for database queries. I also have three different web socket connections that are used to push status updates from the server to the client (CPU and Memory stats in a live chart, database status, and query queue). While a query is not running, everything works smoothly, but once a query is started (post request), then the three web socket connections seem to hang/block while waiting for the query to return. I was reading about this and have not found any relevant answers...I suspect that it is probably something really dumb on my part...but this has had me scratching my head for the better part of a day now. I thought I might try moving the web socket connections to web workers...but in theory, the POST should not be blocking to begin with...So, here are the relevant snippets of code...The full source is a couple of thousand lines of code...so I didn't want to inundate anyone with it...but could show it if it is useful. So, the big question is what am I doing wrong here? Or perhaps, am I misunderstanding how AJAX calls work as far as blocking goes?
// query execution button that grabs the query for the most recently focused query source (SPARQL editor, history, or canned)
$("#querySubmitButton").on("click", function(e) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Let's make sure we are clearing out the work area and the popup contents
$("#viz").empty();
// Get YASQE to tell us what type of query we are running
var queryType = editor.getQueryType();
// refactored so that we can clean up the on-click function and also make other query types in a more modular way
switch(queryType) {
case 'SELECT':
sparqlSelect();
break;
case 'CONSTRUCT':
sparqlConstruct();
break;
case 'ASK':
sparqlAsk();
break;
case 'DESCRIBE':
sparqlDescribe();
break;
case 'INSERT':
sparqlInsert();
break;
default:
popup.show("Unrecognized query type.","error");
break;
}
});
// Functions to do each of the query types (SELECT, CONSTRUCT, ASK, DESCRIBE, INSERT)
// SELECT
function sparqlSelect() {
$.post("sparqlSelect", { database: $("#DB_label").html(),'query': editor.getValue() }).done(function(data, textStatus, xhr) {
// Enable the query button
$("#querySubmitButton").removeAttr('disabled');
// If the query worked, store it
storeQueryHistory(query);
// if the previous query was a CONSTRUCT, then lets hide the graph metrics button
$("#nav-trigger-graphStatistics").fadeOut(800);
// Need to slide the query menu back
sliders("in",$("#nav-trigger-query").attr("id"));
var columns = [];
var fields = [];
var comboboxFields = [];
// Hide the graph search panel
$("#graphSearch").fadeOut(1400);
// Show the results and visualization button/tab
$("#nav-trigger-results").fadeIn(1400);
$("#nav-trigger-visualization").fadeIn(1400);
$.each(data.results.head.vars, function(index, value) {
columns.push({'field': value, 'title': value});
var to = {};
to[value] = {type: "string"};
fields.push(to);
// Let's also populate the two Comboboxes for the Visualization while we are at it
comboboxFields.push({'text': value, 'value': value});
});
// Now, set the two combobox datasources for visualizations
var categoriesDS = new kendo.data.DataSource({
data: comboboxFields
});
vizCategoryAxis.setDataSource(categoriesDS);
var valuesDS = new kendo.data.DataSource({
data: comboboxFields
});
vizValueAxis.setDataSource(valuesDS);
var dataBindings = [];
$.each(data.results.results.bindings, function(index1, value) {
var tempobj = {};
$.each(value, function(k1,v1) {
tempobj[k1] = v1.value;
});
tempobj.id=index1;
dataBindings.push(tempobj);
});
var configuration = {
dataSource: {
data: dataBindings,
pageSize: 25
},
height: 400,
scrollable: true,
sortable: true,
filterable: true,
reorderable: true,
resizable: true,
toolbar: ["excel"],
excel: {
allPages: true,
filterable: true,
proxyURL: "/saveExcel"
},
pageable: {
input: true,
numeric: false,
pageSizes: true
},
'columns': columns,
dataBound: function(e) {
$(e.sender.element).find('td').each(function() {
var temp = $(this).html();
if (isUrl(temp)) {
$(this).html('' + temp + '');
}
});
}
};
// Create the popup window
var gridWindow = $("#resultsPopup").kendoWindow({
width: "70%",
title: "Query Results",
actions: [
"Minimize",
"Maximize",
"Close"
]
}).data('kendoWindow');
// Center and show the popup window
gridWindow.center().open();
// Create/update/refresh the grid
resultsGrid.setOptions(configuration);
resultsGrid.dataSource.page(1);
$("#nav-trigger-results").on('click',function() {
// Center and show the popup window
gridWindow.center().open();
});
}).fail(function(xhr) {
// If we are timed-out
if (xhr.status === 401) {
// First, clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
// Next, disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
popup.show("Session for " + host + " has timed out, please log back in.","error");
}
else {
// Enable the query button
$("#querySubmitButton").removeAttr('disabled');
popup.show("Error, no results (" + xhr.status + " " + xhr.statusText + ")","error");
}
});
}
// Function to connect to the query queue websocket
function queueWebsocketConnect() {
var qws = new WebSocket('wss://endeavour:3000/queue');
// Let's disconnect our Websocket connections when we leave the app
$(window).on('unload', function() {
console.log('Websocket connection closed');
qws.close();
});
// Status websocket onopen
qws.onopen = function () {
console.log('Websocket connection opened');
popup.show("Websocket connection opened","success");
};
qws.onclose = function (event) {
console.log('Websocket connection closed');
popup.show("Websocket connection closed","info");
};
qws.onmessage = function (msg) {
var res = JSON.parse(msg.data);
var tableRows = '<thead><tr><td>Query Position</td><td>Query ID</td><td>Kill/Cancel Query</td></tr></thead><tbody>';
if (res.executing != null && res.entry.length > 0) {
$("#queryQueue").empty();
tableRows += '<tr><td>1</td><td>' + res.executing.id + '</td><td><input type="button" class="k-button" value="Kill"></td></tr>';
$.each(res.entry, function(index,object) {
tableRows += '<tr><td>' + (object.pos + 1) + '</td><td>' + object.query.id + '</td><td><input type="button" class="k-button" value="Cancel"></td></tr>';
});
tableRows += '</tbody>';
$("#queryQueue").html(tableRows);
}
else if (res.executing != null) {
$("#queryQueue").empty();
tableRows += '<tr><td>1</td><td>' + res.executing.id + '</td><td><input type="button" class="k-button" value="Kill"></td></tr>';
tableRows += '</tbody>';
$("#queryQueue").html(tableRows);
}
else {
console.log(res);
$("#queryQueue").empty();
}
};
}
// Function to connect to the stats websocket
function websocketConnect () {
// Set up websocket connection for system stats
var ws = new WebSocket('wss://endeavour:3000/stats');
// Let's disconnect our Websocket connections when we leave the app
$(window).on('unload', function() {
console.log('Websocket connection closed');
ws.close();
});
// Status websocket onopen
ws.onopen = function () {
console.log('Websocket connection opened');
popup.show("Websocket connection opened","success");
};
// Status websocket onclose
ws.onclose = function (event) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
// Clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
console.log('Websocket connection closed');
popup.show("Websocket connection closed","error");
$("#websocketReconnectButtonYes").on('click', function() {
websocketConnect();
queueWebsocketConnect();
websocketReconnect.close();
});
$("#websocketReconnectButtonNo").on('click', function() {
websocketReconnect.close();
});
websocketReconnect.center().open();
};
// When updates are received, push them out to update the details
var logoutCount = 0;
ws.onmessage = function (msg) {
if (msg.data === 'loggedOut') {
// Ensure we only emit this one time instead of a stream of them
if (logoutCount == 0) {
// Disable the query button
$("#querySubmitButton").attr('disabled',true);
// Change "login" tab text color to red so we know we are no longer logged in
var styles = { 'color': "#FFCCD2" };
$("#nav-trigger-login").css(styles);
// Clear the host, database, and status text
$("#host_label").html('');
$("#DB_label").html('');
$("#status_label").html('');
console.log("Session for " + $("#host_label").html() + " has timed out, please log back in.");
popup.show("Session for " + $("#host_label").html() + " has timed out, please log back in.","error");
}
logoutCount = 1;
}
else {
logoutCount = 0;
var res = JSON.parse(msg.data);
var host = $("#host_label").html();
var pdatabase = $("#DB_label").html();
var pstatus = $("#status_label").html();
// Disable the query button unless the database is "CONNECTED"
if ($("#status_label").html() !== res.current.databaseStatus) {
if (res.current.databaseStatus !== "CONNECTED") {
$("#querySubmitButton").attr('disabled',true);
}
else {
$("#querySubmitButton").removeAttr('disabled');
}
if (res.current.databaseStatus == 'CONNECTED' || res.current.databaseStatus == 'STOPPED') {
$("#startDB").removeAttr('disabled');
}
else {
$("#startDB").attr('disabled',true);
}
}
// Maybe a more intelligent way to do this, but need to make sure that if the cookie is still valid, then populate the database login stuff
if ($("#dbConfigHost").val() == "" && $("#dbConfigUser").val() == "") {
$("#dbConfigHost").val(res.host);
$("#dbConfigUser").val(res.user);
// Change "login" tab text color to green so we know we are logged in
var styles = { 'color': "#C5E6CC" };
$("#nav-trigger-login").css(styles);
var databasesDS = new kendo.data.DataSource({
data: res.databases.database
});
databasePicker.setDataSource(databasesDS);
}
// Update the labels when values change
if (res.host != $("#host_label").html()) {
$("#host_label").html(res.host);
popup.show("Host changed to " + res.host,"info");
}
if (pdatabase != res.current.name) {
$("#DB_label").html(res.current.name);
popup.show("Database changed to " + res.current.name ,"info");
}
if (pstatus != res.current.databaseStatus) {
$("#status_label").html(res.current.databaseStatus);
}
// Update the sparklines
cpulog.options.series[0].data = res.system.cpu;
cpulog.refresh();
memlog.options.series[0].data = res.system.mem;
memlog.refresh();
}
};
// Open the websocket connection to listen for changes to the query list
var queryWS = new WebSocket('wss://endeavour:3000/queryList');
queryWS.onmessage = function(msg) {
var res = JSON.parse(msg.data);
var queriesDS = new kendo.data.DataSource({
data: res
});
cannedQuery.setDataSource(queriesDS);
};
}
Well, I guess when one has been heading down one road for a while, one assumes that it is in the right direction. After further head-scratching, I found the issue and it was related to the blocking/non-blocking nature of my backend web-framework. As it turns out, in Mojolicious (Perl web-framework), http calls can be either synchronous or asynchronous depending on how one writes the call.
my $tx = $ua->get('http://foo.bar?query=getSomeFoo');
if($tx->success) {
$self->render($tx->res->content);
}
else {
$self->rendered($tx->res->code);
}
This is a blocking/synchronous request. Nothing happens until after the GET finishes. On the other hand, if one writes the request like so, it is an asynchronous request:
$ua->get('http://foo.bar?query=getSomeFoo' => sub {
my ($ua,$tx) = #_;
if($tx->success) {
$self->render($tx->res->content);
}
else {
$self->rendered($tx->res->code);
}
});
So, if anyone else has encountered this issue...here is the answer. If I am the only idiot on the planet that has committed this blunder...then I guess I have put my shame out there for all to have a good chuckle at.
Bottom line was that this was well documented in the Mojolicious docs...but I had been doing it one way for so long that I completely forgot about it.
Cheers!
I'm trying to show a notification using an image I receive over a socket as an arrayBuffer. The notification shows, but without the image supplied. The standard firefox logo/icon is used instead. Any help would be appreciated. The code seems to run without any errors, or rather, no errors are are thrown/stack trace printed when the notification is created.
Here is the code to create the notification:
ps_worker.port.on("notification", function(notification){
//DISPLAY LINK TO USER
var arrayBuffer_icon = notification.icon;
var arrayBuffer_largeicon = notification.largeicon;
var str = String.fromCharCode.apply(null,arrayBuffer_icon);
var base64String = utils.btoa(str).replace(/.{76}(?=.)/g,'$&\n');
var dataUri = "data:image/png;base64,"+ base64String;
notifications.notify({
title: notification.app + ": " + notification.title,
text: notification["subject"] + "\n" + notification.content,
data: "did gyre and gimble in the wabe", // data is a string passed through to the on click listener
iconURL: dataUri,
onClick: function (data) {
console.log(data);
}
});
});
the utils.btoa call is implemented as described here: https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Unit_testing
The relevant server code is (node.js):
function send_notification_to_socket(user, notification, target){
fs.readFile(notification.iconpath, function(err, buf){
if(socketstore.get_socket_by_id(user)){
socket = socketstore.get_socket_by_id(user);
notification["icon"] = buf;
socket.emit('notification', notification);
}else{
console.log("No socket for user " + user);
}
});
}
Any ideas on what I might be doing wrong?