Refresh webpage on change in folder - javascript

I want to refresh my web page every time a file is uploaded to the folder.
I have a web service written in flask having the following handler
#app.route('/getlatest/')
def getlatest():
import os
import glob
newset = max(glob.iglob('static/*'),key=os.path.getctime)
Return newest;
This gives me the name of the latest file in the folder.
I have an Ajax call in my JS (client side) to constantly get data from above function.
function GetLatest()
{
$.ajax({
url: "http://localhost:5000/getlatest",
success: function(result)
{
if(previousName != result){
previousName = result;
$("#image").attr("src","/"+previousName);
}
}
});
}
function calling server every second.
(function myLoop (i) {
setTimeout(function () {
GetLatest();
if (--i) myLoop(i);
}, 1000)
})(100);
This Works fine [well almost].
My Question is: Is there any better way to do it[ there must be ]?
i'm open to technology choices what every they may be [node, angualr etc.]

yea you can use websockets(flask-socketio).That will let you to have an open connection between you and server and every time in the folder is a new photo will be displayed on a selected div
http://flask-socketio.readthedocs.io/en/latest/
https://pypi.python.org/pypi/Flask-SocketIO/2.9.1

So here is how i did it.
first of all thanks to
https://blog.miguelgrinberg.com/post/easy-websockets-with-flask-and-gevent
for explaining it perfectly.
What i have learnt reading a couple of blogs.
All communication initiated using http protocol is client server communication, and client is always the initiator. So in this case we have to use a different protocol: Web-sockets which allow you to create a full-duplex (two way) connection.
Here is the server code;
socketio = SocketIO(app, async_mode=async_mode)
thread = None
prevFileName = ""
def background_thread():
prevFileName = ""
while True:
socketio.sleep(0.5)
if(isNewImageFileAdded(prevFileName) == "true"):
prevFileName = getLatestFileName()
socketio.emit('my_response',
{'data': prevFileName, 'count': 0},
namespace='/test');
def getLatestFileName():
return max(glob.iglob('static/*'),key=os.path.getctime)
def isNewImageFileAdded(prevFileName):
latestFileName = max(glob.iglob('static/*'),key=os.path.getctime)
if(prevFileName == latestFileName):
return "false"
else:
return "true"
Creating separate thread to keep the socket open. emit sends the msg to the client from server...
#socketio.on('connect', namespace='/test')
def test_connect():
global thread
if thread is None:
thread = socketio.start_background_task(target=background_thread)
emit('my_response', {'data': 'Connected', 'count': 0})
And here is Client Side.[replaced ajax call with the following code]
var socket = io.connect(location.protocol + '//' + document.domain + ':' +
location.port + namespace);
socket.on('my_response', function(msg) {
setTimeout(function(){ $("#image").attr("src","/"+msg.data)},1000)
});
Please correct me if i am wrong.

Related

create a template url-routing script

I want to create an url-routing script using javascript as much as possible, but also accepting jQuery in the code. The js file has to change the url path (although I used location.hash instead of location.pathname) and the content of a div with the view id (from external files) accordingly.
Example configuration:
root/index.html
root/tpl/home.html
root/tpl/about.html
home.html content:
<p>This is content of home page</p>
about.html content:
<p>This is the content of the about page </p>
What I have done so far:
'use strict';
var Router = {
root: '/',
routes: [],
urls: [],
titles: [],
navigate: function() {
location.hash = this.root;
return this;
},
add: function(thePath, theUrl, theTitle) {
this.routes.push(thePath);
this.urls.push(theUrl);
this.titles.push(theTitle);
},
loading: function() {
this.navigate();
var r = this.routes;
var u = this.urls;
window.onload = function() {
$("#view").load("tpl/home.html");
};
window.onhashchange = function() {
for (var i = 0; i < r.length; i++) {
if (location.hash == r[i]) {
$("#view").load(u[i]);
}
}
};
}
};
Router.add("#/home", "tpl/home.html", "Home Page");
Router.add("#/about", "tpl/about.html", "About Page");
Router.loading();
Desired type of url:
http://mywebsite.com/
http://mywebsite.com/about
I know there are more than enough libraries that make the routing, like AngularJS and Crossroad, I want to know how this could be done.
To make this URL work - http://mywebsite.com/about - you need a server that knows how to route this request. Since the actual file name is about.html your server must know what to do with extensionless URLs.
Usually, the server uses the file extension as a clue for how to serve up content. For example, if it sees file.php it knows to use the PHP component, for .aspx it knows to use the ASP.NET component, and for .htm or .html it knows to respond with plain HTML (and usually serves the file instead of processing it). Your server must have some rules for what to do with any request, whether it has an extension or not, but without an extension you need to provide an explicit routing rule for that request..
The capabilities for JavaScript to do routing are limited because it requires the user to already be on your site. You can do some interesting things if you parse the URL parameters or use hashes and use them for routing, but that still requires requesting a page from your site as the first step.
For example: the server is already doing some level of "extensionless routing" when you give it this request:
http://mywebsite.com/
The parts of the URL are:
http - protocol
(port 80 is implied because it is default HTTP port)
mywebsite.com - domain AKA host
/ the path
The server sees / and uses what IIS calls a "default document" (I think apache calls it "default index" or "default page"). The server has been configured to return a file such as "index.html" or "default.htm" in this case. So when you request http://mywebsite.com/ you actually may get back the equivalent of http://mywebsite.com/index.html
When the server sees http://mywebsite.com/about it may first look for a folder named about and next for a file named about, but since your file is actually named about.html and is in a different folder (/tpl) the server needs some help to know how to translate http://mywebsite.com/about into the appropriate request - which for you would be http://mywebsite.com/#/about so that it requests the routing page (assuming it is the default document in the web app root folder) so that the browser can parse and execute the JavaScript that does the routing. Capisce?
You might be interested by frontexpress.
My library fix your case like below:
// Front-end application
const app = frontexpress();
const homeMiddleware = (req, res) => {
document.querySelector('#view').innerHTML = '<p>This is content of home page</p>';
}
app.get('/', homeMiddleware);
app.get('/home', homeMiddleware);
app.get('/about', (req, res) => {
document.querySelector('#view').innerHTML = '<p>This is the content of the about page </p>';
});
Obviously, you can get the template files from the server.
The #view will be feeded as below:
document.querySelector('#view').innerHTML = res.responseText;
More detailed sample in this gist
I have worked with what your answers and I have build the following Router. The only issue remains that it still uses location.hash
(function() {
var Router = {
root: '#/',
routes: [],
urls: [],
titles: [],
add: function(thePath, theUrl, theTitle) {
this.routes.push(thePath);
this.urls.push(theUrl);
this.titles.push(theTitle);
},
navigate: function() {
var routes = this.routes,
urls = this.urls,
root = this.root;
function loading() {
var a = $.inArray(location.hash, routes),
template = urls[a];
if (a === -1) {
location.hash = root;
$("#view").load(urls[0]);
}
else {
$("#view").load(template);
if (a === 0) {
window.scrollTo(0, 0);
}
else {
window.scrollTo(0, 90);
}
}
}
window.onload = loading;
window.onhashchange = loading;
}
};
Router.add("#/", "tpl/home.html", "Home Page");
Router.add("#/about", "tpl/about.html", "About Page");
Router.add("#/licence", "tpl/licence.html", "MIIT Licence");
Router.add("#/cabala", "tpl/cabala.html", "Cabala Checker");
Router.add("#/articles/esp", "tpl/article1.html", "ESP");
Router.add("#/fanfics/the-chupacabra-case", "tpl/article2.html", "The Chupacabra Case");
Router.navigate();
})();
Your request reads like what you're attempting to do is to add a "path+file" ("/tpl/about.html") to a base url ("www.myhost.com"). If that's the case, then you need to dynamically extract the host name from your current document and then append the new URL to the existing base. You can get the existing host name by executing the following commands in javascript:
var _location = document.location.toString();
var serverNameIndex = _location.indexOf('/', _location.indexOf('://') + 3);
var serverName = _location.substring(0, serverNameIndex) + '/';
This will return a string like: "http://www.myhost.com/" to which you can now append your new URL. All of this can be done in javascript prior to sending to the server.
If you only want the server name, without the leading http or https, then change the last line to be:
var serverName = _location.substring(_location.indexOf('://') + 3, serverNameIndex) + '/';
Lets break your code down a little bit:
function loading() {
"location.hash" is set based on the URL most recently clicked (www.myhost.home/#about). One essential question is, is this the action that you want, or do you want to pass in a value from the html for the onClick operation? It seems like that would be a more effective approach for you.
var a = $.inArray(location.hash, routes),
template = urls[a];
var a will be set to either -1 or the location of "location.hash" in the "routes array. If location.hash does not exist in routes, then a==-1 and the script will fail, because you're setting template = urls[-1]. You may want to move setting template to inside the "else" statement.
if (a === -1) {
location.hash = root;
$("#view").load(urls[0]);
}
else {yada yada yada
}
You could use a sequence in your html analogous to:
<a onclick="loading('#about')">Go to About Page</a>

Server-Sent Events with Ruby Grape

I am trying to create Server-Sent events on my Ruby Grape API.
The problem is that the connection seems to be closed really fast all the time, as I get Connection closed event all the time on test webpage.
The client connects to the server as I can see the method being called, but I would like to know why is the connection not constant and why I don't receive the data I send using the Thread.
Here is my Ruby code:
$connections = []
class EventsAPI < Sinantra::Base
def connections
$connections
end
get "/" do
content_type "text/event-stream"
stream(:keep_open) { |out|
puts "New connection"
out << "data: {}\n\n"
connections << out
}
end
post "/" do
data = "data\n\n"
connections.each { |out| out << data }
puts "sent\n"
end
end
Here is my Javascript:
var source = new EventSource('http://localhost:9292/events');
source.onmessage = function(e) {
console.log("New message: ", e.data);
showMessage(e.data);
};
source.onopen = function(e) {
// Connection was opened.
};
source.onerror = function(e) {
console.log("Source Error", e)
if (e.eventPhase == EventSource.CLOSED) {
console.log("Connection was closed");
// Connection was closed.
}
};
var showMessage = function(msg) {
var out = document.getElementById('stream');
var d = document.createElement('div')
var b = document.createElement('strong')
var now = new Date;
b.innerHTML = msg;
d.innerHTML = now.getHours() + ":" + now.getMinutes() + ":" +now.getSeconds() + " ";
d.appendChild(b);
out.appendChild(d);
};
EDIT: I got it working with the GET method (I changed the Grape::API to Sinatra::Base as Grape does not implement stream). I now receive data, but the connection is not kept alive and when I use the post method the data never reaches the browser.
Thank you in advance for your answers.
The JS code looks correct. My guess is that you should not start a new thread for your infinite loop. What will be happening is that the main thread will carry on executing, reach the end of its block, and close the http request. Your detached thread is then left writing to a non-existent out stream.
UPDATE in response to your EDIT: POST is not supported in SSE. Data can only be passed to an SSE process by using GET data or cookies.

Cannot communicate with websocket. Autobahn: received HELLO message, and session is not yet established

I am trying to build a WebSocket session using Python 3.4, Django, Autobahn and JS. I have successfully run the websocket server on the python side, but i cannot subscribe or receive any data published by the server
My code is fairly simple:
class TestAppWS(ApplicationSession):
"""
An application component that publishes an event every second.
"""
def onConnect(self):
self.join(u"realm1")
#asyncio.coroutine
def onJoin(self, details):
counter = 0
while True:
self.publish('com.myapp.topic1', counter)
counter += 1
yield from asyncio.sleep(1)
def start_ws():
print("Running")
session_factory = ApplicationSessionFactory()
session_factory.session = TestAppWS
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# factory = WebSocketServerFactory("ws://localhost:8090", debug=False)
# factory.protocol = MyServerProtocol
server = None
try:
transport_factory = WampWebSocketServerFactory(session_factory, debug_wamp=True)
loop = asyncio.get_event_loop()
coro = loop.create_server(transport_factory, 'localhost', 8090)
server = loop.run_until_complete(coro)
loop.run_forever()
except OSError:
print("WS server already running")
except KeyboardInterrupt:
pass
finally:
if server:
server.close()
loop.close()
start_ws() is run inside a separate Thread object. If I access localhost:8090 on my browser I can see the Autobahn welcome message.
On the frontend I have
var connection = new autobahn.Connection({
url: 'ws://localhost:8090/',
realm: 'realm1'}
);
connection.onopen = function (session) {
var received = 0;
function onevent1(args) {
console.log("Got event:", args[0]);
received += 1;
if (received > 5) {
console.log("Closing ..");
connection.close();
}
}
session.subscribe('com.myapp.topic1', onevent1);
};
connection.open();
It does not seem to work, when I try to connect the frontend I get the following error on the backend side:
Failing WAMP-over-WebSocket transport: code = 1002, reason = 'WAMP Protocol Error (Received <class 'autobahn.wamp.message.Hello'> message, and session is not yet established)'
WAMP-over-WebSocket transport lost: wasClean = False, code = 1006, reason = 'connection was closed uncleanly (I failed the WebSocket connection by dropping the TCP connection)'
TX WAMP HELLO Message (realm = realm1, roles = [<autobahn.wamp.role.RolePublisherFeatures object at 0x04710270>, <autobahn.wamp.role.RoleSubscriberFeatures object at 0x047102B0>, <autobahn.wamp.role.RoleCallerFeatures object at 0x047102D0>, <autobahn.wamp.role.RoleCalleeFeatures object at 0x047102F0>], authmethods = None, authid = None)
RX WAMP HELLO Message (realm = realm1, roles = [<autobahn.wamp.role.RoleSubscriberFeatures object at 0x04710350>, <autobahn.wamp.role.RoleCallerFeatures object at 0x04710330>, <autobahn.wamp.role.RoleCalleeFeatures object at 0x04710390>, <autobahn.wamp.role.RolePublisherFeatures object at 0x04710370>], authmethods = None, authid = None)
Traceback (most recent call last):
File "C:\Python34\lib\site-packages\autobahn\wamp\websocket.py", line 91, in onMessage
self._session.onMessage(msg)
File "C:\Python34\lib\site-packages\autobahn\wamp\protocol.py", line 429, in onMessage
raise ProtocolError("Received {0} message, and session is not yet established".format(msg.__class__))
autobahn.wamp.exception.ProtocolError: Received <class 'autobahn.wamp.message.Hello'> message, and session is not yet established
on the javascript console I see:
Uncaught InvalidAccessError: Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999. 1002 is neither.
Any idea? It looks like the session is not started, honestly it is not clear how this session work. Should not the session be initialized once a connection from the client is made?
Your TestAppWs and your browser code are both WAMP application components. Both of these need to connect to a WAMP router. Then they can talk freely to each other (as if there were no router in between .. transparently).
Here is how to run.
Run a WAMP Router.
Using Crossbar.io (but you can use other WAMP routers as well), that's trivial. First install Crossbar.io:
pip install crossbar
Crossbar.io (currently) runs on Python 2, but that's irrelevant as your app components can run on Python 3 or any other WAMP supported language/run-time. Think of Crossbar.io like a black-box, an external infrastructure, like a database system.
Then create and start a Crossbar.io default router:
cd $HOME
mkdir mynode
cd mynode
crossbar init
crossbar start
Run your Python 3 / asyncio component
import asyncio
from autobahn.asyncio.wamp import ApplicationSession
class MyComponent(ApplicationSession):
#asyncio.coroutine
def onJoin(self, details):
print("session ready")
counter = 0
while True:
self.publish('com.myapp.topic1', counter)
counter += 1
yield from asyncio.sleep(1)
if __name__ == '__main__':
from autobahn.asyncio.wamp import ApplicationRunner
runner = ApplicationRunner(url = "ws://localhost:8080/ws", realm = "realm1")
runner.run(MyComponent)
Run your browser component
var connection = new autobahn.Connection({
url: 'ws://localhost:8080/ws',
realm: 'realm1'}
);
connection.onopen = function (session) {
var received = 0;
function onevent1(args) {
console.log("Got event:", args[0]);
received += 1;
if (received > 5) {
console.log("Closing ..");
connection.close();
}
}
session.subscribe('com.myapp.topic1', onevent1);
};
connection.open();

Learning Node - Book - Pages 16-18 - Two Examples

There are two examples in between these pages 16 and 18.
Example 1.3 is a server app.
Example 1.4 is a client app doing GET requests to the server.
When I run the two examples (at the same time) I notice some quite weird behavior
in the client. All requests are executed (i.e. the for loop in the client completes)
but the callbacks of only 5 of them get called. The client doesn't exit and also
doesn't error out. And just no more callbacks are called.
Any ideas what might be happening or how I can troubleshoot this further?
Note: I am running Node.js v0.10.20 on Windows 7.
Server:
var http = require('http');
var fs = require('fs');
// write out numbers
function writeNumbers(res) {
var counter = 0;
// increment, write to client
for (var i = 0; i<100; i++) {
counter++;
res.write(counter.toString() + '\n');
}
}
// create http server
http.createServer(function (req, res) {
var query = require('url').parse(req.url).query;
var app = require('querystring').parse(query).file;
// content header
res.writeHead(200, {'Content-Type': 'text/plain'});
if (!app){
res.end();
console.log('No file argument found in query string.');
return;
}else{
app = app + ".txt";
}
// write out numbers
writeNumbers(res);
// timer/timeout to open file and read contents
setTimeout(function() {
console.log('Opening file: ' + app + '.');
// open and read in file contents
fs.readFile(app, 'utf8', function(err, data) {
res.write('\r\n');
if (err)
res.write('Could not find or open file ' + app + ' for reading.\r\n');
else {
res.write(data);
}
// response is done
res.end();
});
},2000);
}).listen(8124);
console.log('Server running at 8124');
Client:
var http = require('http');
var N = 200;
// The URL we want, plus the path and options we need
var options = {
host: 'localhost',
port: 8124,
path: '/?file=automatic',
method: 'GET'
};
var callback_function = function(response) {
// finished? ok, write the data to a file
console.log('got response back');
};
for (var i = 1; i <= N; i++) {
// make the request, and then end it, to close the connection
http.request(options, callback_function).end();
console.log('done with call # ' + i);
}
--- Experiment Done ---
If I lower N to 10 and also if I do a
global "var i = 1" and then do this thing:
function schedule(){
http.request(options, callback_function).end();
console.log('done with call ' + i);
i++;
if (i<=N){
setTimeout(function(){
schedule();
}, 1000);
}
}
schedule();
instead of the loop in the client, I get similar behavior.
I guess that's what Milimetric meant by "sleep" i.e. just
to make sure I don't hit the server too quickly with too
many simultaneous requests.
But the behavior is not fully identical, it takes several mins
to print 'got response back' on the second set of 5 requests
and then another maybe 5-6 mins for the client to exit.
Still, all that does look weird to me.
C:\PERSONAL\NODE_TEST>node test004.js
done with call 1
got response back
done with call 2
got response back
done with call 3
got response back
done with call 4
got response back
done with call 5
got response back
done with call 6
done with call 7
done with call 8
done with call 9
done with call 10
got response back
got response back
got response back
got response back
got response back
C:\PERSONAL\NODE_TEST>
The problem is that the client doesn't consume the response body sent by the server, so the connection remains (half) open and the http agent only allows 5 concurrent requests per client by default, causing it to hang after 5 requests. The connection will eventually timeout, causing the next 5 requests to be processed.
node.js http.get hangs after 5 requests to remote site
Change your callback function to consume any data sent down the response.
var callback_function = function(response) {
// finished? ok, write the data to a file
console.log('got response back');
response.on('data', function () {});
};

Winsock.SendData equivalent with Javascript?

Can the following VB Script to open an IP cash drawer be done in Javascript instead?
Private Sub CashDrawerConnect_Click()
Winsock1.Close
ipaddr = "192.168.2.5"
Winsock1.RemoteHost = ipaddr
Winsock1.RemotePort = 30998
Winsock1.Connect
Sleep 250
TxtOpStatus = "Connection to the cash drawer at " & ipaddr & " is established..."
TxtOpStatus.Refresh
End Sub
Private Sub CashDrawerOpen_Click()
If Winsock1.State = sckConnected Then
Winsock1.SendData "opendrawer\0a"
Else
TxtOpStatus = "Not connected to the device"
TxtOpStatus.Refresh
End If
End Sub
You could do it on javascript, but not while running on a browser.
You would need to install nodejs and run your js file directly from the console.
This is a small example that would connect you the the drawer and send the "opendrawer" command on your example:
var net = require('net');
var client = net.connect({port: 30998, host: "yourip"}, function() {
client.write("opendrawer\0a");
});
If however the server has access to the drawer the javascript code could just make a request to the server which would be on charge of opening the connection to the drawer and sending the payload (opendrawer).
If you use php you can take a look at the sockets documentation.
Using VB and JavaScript the calls are mostly the same, you just jhave to adapt it to the language. http://www.ostrosoft.com/oswinsck/oswinsck_javascript.asp
The following is a snippet that uses WinSock from JScript
var oWinsock;
var sServer = "192.168.2.5";
var nPort = 3098;
var bClose = false;
oWinsock = new ActiveXObject("OSWINSCK.Winsock");
// Hooking up handlers
WScript.ConnectObject(oWinsock, "oWinsock_");
oWinsock.Connect(sServer, nPort);
WScript.Echo("Invalid URL");
bClose = true;
function oWinsock_OnConnect() {
oWinsock.SendData('Your data');
}
function oWinsock_OnDataArrival(bytesTotal) {
var sBuffer = oWinsock.GetDataBuffer();
sSource = sSource + sBuffer;
}
function oWinsock_OnError(Number, Description, Scode, Source,
HelpFile, HelpContext, CancelDisplay) {
WScript.Echo(Number + ': ' + Description);
}
function oWinsock_OnClose() {
oWinsock.CloseWinsock();
WScript.Echo(sSource);
oWinsock = null;
bClose = true;
}
while (!bClose) {
WScript.Sleep(1);
}
In the browser? Not really, but you can use WebSockets http://en.wikipedia.org/wiki/WebSocket
You'll need to implement a WebSocket server, so if you need to talk directly to a socket, you can't do it from a browser. But you could implement a proxy server that relays information between the socket server and the WebSocket server.
If you don't need two way communication, the best thing would be for your server to provide a webservice that wraps that socket request. Then your client can just make an AJAX call.

Categories