Current section
Files
Jump to
Current section
Files
lib/provider/local/livereload/client_handler.ex
defmodule Reblog.Provider.Local.Livereload.ClientHandler do
@moduledoc """
Cowboy handler to serve up livereload client.
"""
require Logger
def init(req, state) do
handle(req, state)
end
defp handle(request, %{from: from} = state) do
Logger.debug(fn -> ["Live"] end)
Logger.debug(fn -> [" request: ", inspect(request, pretty: true)] end)
req = :cowboy_req.reply(200,
%{
<<"content-type">> => <<"text/html">>
}, [build_body(from, request.path_info, request)], request)
{:ok, req, state}
end
defp build_body(from, path_info, %{host: host, port: port}) do
_live_file_path = Path.join([from] ++ path_info)
[path_ending | path_start] = Enum.reverse(path_info)
path_ending_ext = Path.extname(path_ending)
path_info = (if (path_ending_ext != ".html" && path_ending_ext != ".htm") do
path_info ++ ["index.html" <> "?cacheBust=#{Integer.to_string(System.os_time(:seconds))}"]
else
Enum.reverse([path_ending <> "?cacheBust=#{Integer.to_string(System.os_time(:seconds))}"] ++ path_start)
end)
live_url_path = Path.join(path_info)
Logger.debug(fn -> ["Serving: ", live_url_path] end)
# File.read!(
["""
<html style="width: 100%; height: 100%;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Live</title>
<base href="http://#{host}:#{port}/">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous">
</script>
<script type="text/javascript">
(function (window, $) {
const connect = () => {
var ws = new WebSocket(`ws://${window.location.host}/livereload`);
ws.addEventListener('open', (evt) => {
console.log("Connected to server.");
ws.send('ping');
console.log('Initiated ping-pong.')
});
var handlers = [];
ws.addEventListener('message', (evt) => {
if (evt.data === 'pong') {
setTimeout(() => ws.send('ping'), 1500)
return;
}
if (evt.data === 'connected') {
console.log('Confirmed connection.')
return;
}
var data = JSON.parse(evt.data);
console.log('Received:', data);
handlers.forEach((h) => { h(data); });
});
ws.addEventListener('close', (evt) => {
console.log("Disconnected from server.");
});
ws.addEventListener('error', (evt) => {
console.error(evt.data);
});
return {
send: function () { return this.ws.apply(this, arguments); },
handle: function (callback) { handlers.push(callback); }
};
};
var connection = connect();
var connection = connect();
$(() => {
// commence the iframe hacks. TODO: cleanup :(
$liveFrame = $('<iframe sandbox="allow-same-origin allow-scripts allow-top-navigation" frameborder="0" allowfullscreen id="live-frame" src="#{live_url_path}" name="' + Date.now() + '" style="width: 100%; height: 100%; border: 0; padding: 0; margin: 0;" />');
$('body').append($liveFrame);
$liveFrame.ready(() => {
// make sure we stay attached to our "live" session.
$liveFrame[0].onload = function () {
var lc = $liveFrame[0].contentWindow.location
history.replaceState(null, '', lc.origin + "/live" + lc.pathname);
};
// websocket on message => handle file changes.
connection.handle((data) => {
if (data.change === 'post' || data.change === 'page') {
if (window.location.pathname.indexOf(data.transformed) === -1) { return; }
$('#live-frame').contents().find('#content').html(data.contents);
return;
}
window.location.reload();
});
});
});
}.call(null, window, window.$));
</script>
</head>
<body style="width: 100%; height: 100%; padding: 0; margin: 0;">
<!-- Dynamic Content -->
</body>
</html>
"""]
end
end