diff --git a/app/assets/javascripts/widget.js b/app/assets/javascripts/widget.js deleted file mode 100644 index 571a37d..0000000 --- a/app/assets/javascripts/widget.js +++ /dev/null @@ -1,90 +0,0 @@ -// asciinema embedded player - -(function() { - function insertAfter(referenceNode, newNode) { - referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); - } - - function params(container, script) { - function format(name) { - var value = script.getAttribute('data-' + name); - if (value) { - return name + '=' + value; - } - } - - var options = ['size', 'speed', 'autoplay', 'loop', 'theme', 't', 'preload', 'cols', 'rows']; - - return '?' + options.map(format).filter(Boolean).join('&'); - } - - function locationFromString(string) { - var parser = document.createElement('a'); - parser.href = string; - return parser; - } - - function apiHostFromScript(script) { - var location = locationFromString(script.src); - return location.protocol + '//' + location.host; - } - - function insertPlayer(script) { - // do not insert player if there's one already associated with this script - if (script.dataset.player) { - return; - } - - var apiHost = apiHostFromScript(script); - - var asciicastId = script.id.split('-')[1]; - - var container = document.createElement('div'); - container.id = "asciicast-container-" + asciicastId; - container.className = 'asciicast'; - container.style.display = 'block'; - container.style.float = 'none'; - container.style.overflow = 'hidden'; - container.style.padding = 0; - container.style.margin = '20px 0'; - - insertAfter(script, container); - - var iframe = document.createElement('iframe'); - iframe.src = apiHost + "/a/" + asciicastId + '/embed' + params(container, script); - iframe.id = "asciicast-iframe-" + asciicastId; - iframe.name = "asciicast-iframe-" + asciicastId; - iframe.scrolling = "no"; - iframe.setAttribute('allowFullScreen', 'true'); - iframe.style.overflow = "hidden"; - iframe.style.margin = 0; - iframe.style.border = 0; - iframe.style.display = "inline-block"; - iframe.style.width = "100%"; - iframe.style.float = "none"; - iframe.style.visibility = "hidden"; - iframe.onload = function() { this.style.visibility = 'visible' }; - - container.appendChild(iframe); - - function receiveSize(e) { - if (e.origin === apiHost) { - var name = e.data[0]; - var data = e.data[1]; - var iframeWindow = iframe.contentWindow || iframe; - - if (e.source == iframeWindow && name == 'asciicast:size') { - iframe.style.width = '' + data.width + 'px'; - iframe.style.height = '' + data.height + 'px'; - } - } - } - - window.addEventListener("message", receiveSize, false); - - script.dataset.player = container; - } - - var scripts = document.querySelectorAll("script[id^='asciicast-']"); - [].forEach.call(scripts, insertPlayer); -})(); diff --git a/app/assets/javascripts/widget.js b/app/assets/javascripts/widget.js new file mode 120000 index 0000000..5f5ebf4 --- /dev/null +++ b/app/assets/javascripts/widget.js @@ -0,0 +1 @@ +../../../web/static/assets/js/embed.js \ No newline at end of file diff --git a/docker/nginx/asciinema.conf b/docker/nginx/asciinema.conf index 33709a3..ef8501c 100644 --- a/docker/nginx/asciinema.conf +++ b/docker/nginx/asciinema.conf @@ -44,7 +44,7 @@ server { proxy_redirect off; } - location ~ ^/a/[^.]+\.(json|gif)$ { + location ~ ^/a/[^.]+\.(js|json|gif)$ { try_files $uri $uri/index.html $uri.html @phoenix; } diff --git a/lib/asciinema/trailing_format.ex b/lib/asciinema/trailing_format.ex index f07103a..e214f1f 100644 --- a/lib/asciinema/trailing_format.ex +++ b/lib/asciinema/trailing_format.ex @@ -1,5 +1,5 @@ defmodule Asciinema.TrailingFormat do - @known_extensions ["json", "png", "gif"] + @known_extensions ["js", "json", "png", "gif"] def init(opts), do: opts diff --git a/test/controllers/asciicast_embed_controller_test.exs b/test/controllers/asciicast_embed_controller_test.exs new file mode 100644 index 0000000..9741b10 --- /dev/null +++ b/test/controllers/asciicast_embed_controller_test.exs @@ -0,0 +1,9 @@ +defmodule Asciinema.AsciicastEmbedControllerTest do + use Asciinema.ConnCase + + test "serves embed js", %{conn: conn} do + conn = get conn, "/a/12345.js" + assert response(conn, 200) + assert response_content_type(conn, :js) + end +end diff --git a/web/controllers/asciicast_embed_controller.ex b/web/controllers/asciicast_embed_controller.ex new file mode 100644 index 0000000..809c17d --- /dev/null +++ b/web/controllers/asciicast_embed_controller.ex @@ -0,0 +1,14 @@ +defmodule Asciinema.AsciicastEmbedController do + use Asciinema.Web, :controller + + @max_age 60 + + def show(conn, _params) do + path = Application.app_dir(:asciinema, "priv/static/js/embed.js") + + conn + |> put_resp_content_type("application/javascript") + |> put_resp_header("cache-control", "public, max-age=#{@max_age}") + |> send_file(200, path) + end +end diff --git a/web/router.ex b/web/router.ex index 6bbdef4..487235b 100644 --- a/web/router.ex +++ b/web/router.ex @@ -10,6 +10,17 @@ defmodule Asciinema.Router do plug Asciinema.Auth end + pipeline :asciicast_embed_script do + plug :accepts, ["js"] + end + + scope "/", Asciinema do + pipe_through :asciicast_embed_script + + # rewritten by TrailingFormat from /a/123.js to /a/123/js + get "/a/:id/js", AsciicastEmbedController, :show + end + pipeline :asciicast_file do plug :accepts, ["json"] end diff --git a/web/static/assets/js/embed.js b/web/static/assets/js/embed.js new file mode 100644 index 0000000..571a37d --- /dev/null +++ b/web/static/assets/js/embed.js @@ -0,0 +1,90 @@ +// asciinema embedded player + +(function() { + function insertAfter(referenceNode, newNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); + } + + function params(container, script) { + function format(name) { + var value = script.getAttribute('data-' + name); + if (value) { + return name + '=' + value; + } + } + + var options = ['size', 'speed', 'autoplay', 'loop', 'theme', 't', 'preload', 'cols', 'rows']; + + return '?' + options.map(format).filter(Boolean).join('&'); + } + + function locationFromString(string) { + var parser = document.createElement('a'); + parser.href = string; + return parser; + } + + function apiHostFromScript(script) { + var location = locationFromString(script.src); + return location.protocol + '//' + location.host; + } + + function insertPlayer(script) { + // do not insert player if there's one already associated with this script + if (script.dataset.player) { + return; + } + + var apiHost = apiHostFromScript(script); + + var asciicastId = script.id.split('-')[1]; + + var container = document.createElement('div'); + container.id = "asciicast-container-" + asciicastId; + container.className = 'asciicast'; + container.style.display = 'block'; + container.style.float = 'none'; + container.style.overflow = 'hidden'; + container.style.padding = 0; + container.style.margin = '20px 0'; + + insertAfter(script, container); + + var iframe = document.createElement('iframe'); + iframe.src = apiHost + "/a/" + asciicastId + '/embed' + params(container, script); + iframe.id = "asciicast-iframe-" + asciicastId; + iframe.name = "asciicast-iframe-" + asciicastId; + iframe.scrolling = "no"; + iframe.setAttribute('allowFullScreen', 'true'); + iframe.style.overflow = "hidden"; + iframe.style.margin = 0; + iframe.style.border = 0; + iframe.style.display = "inline-block"; + iframe.style.width = "100%"; + iframe.style.float = "none"; + iframe.style.visibility = "hidden"; + iframe.onload = function() { this.style.visibility = 'visible' }; + + container.appendChild(iframe); + + function receiveSize(e) { + if (e.origin === apiHost) { + var name = e.data[0]; + var data = e.data[1]; + var iframeWindow = iframe.contentWindow || iframe; + + if (e.source == iframeWindow && name == 'asciicast:size') { + iframe.style.width = '' + data.width + 'px'; + iframe.style.height = '' + data.height + 'px'; + } + } + } + + window.addEventListener("message", receiveSize, false); + + script.dataset.player = container; + } + + var scripts = document.querySelectorAll("script[id^='asciicast-']"); + [].forEach.call(scripts, insertPlayer); +})();