diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 95c305e..476c88d 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,4 +15,8 @@ $(function() { $('abbr.timeago').timeago(); + + $("input[data-behavior=auto-select]").click(function() { + this.select(); + }); }); diff --git a/app/assets/javascripts/embed.js b/app/assets/javascripts/embed.js new file mode 100644 index 0000000..35e2f9d --- /dev/null +++ b/app/assets/javascripts/embed.js @@ -0,0 +1,2 @@ +//= require application +//= require player diff --git a/app/assets/javascripts/player/abstract_player.js.coffee b/app/assets/javascripts/player/abstract_player.js.coffee index 48776e6..864b85b 100644 --- a/app/assets/javascripts/player/abstract_player.js.coffee +++ b/app/assets/javascripts/player/abstract_player.js.coffee @@ -14,6 +14,7 @@ class AsciiIo.AbstractPlayer hud: @options.hud rendererClass: @options.rendererClass snapshot: @options.snapshot + containerWidth: @options.containerWidth createVT: -> throw 'not implemented' diff --git a/app/assets/javascripts/player/views/player_view.js.coffee b/app/assets/javascripts/player/views/player_view.js.coffee index b4d4065..c96205e 100644 --- a/app/assets/javascripts/player/views/player_view.js.coffee +++ b/app/assets/javascripts/player/views/player_view.js.coffee @@ -4,6 +4,7 @@ class AsciiIo.PlayerView extends Backbone.View initialize: (options) -> @createRendererView() + @setupClipping() @createHudView() if options.hud @showLoadingOverlay() @@ -17,6 +18,14 @@ class AsciiIo.PlayerView extends Backbone.View @rendererView.afterInsertedToDom() @rendererView.renderSnapshot @options.snapshot + setupClipping: -> + if @options.containerWidth + rendererWidth = @rendererView.elementWidth() + min = Math.min(@options.containerWidth, rendererWidth) + @rightClipWidth = rendererWidth - min + else + @rightClipWidth = 0 + createHudView: -> @hudView = new AsciiIo.HudView(cols: @options.cols) @@ -39,14 +48,19 @@ class AsciiIo.PlayerView extends Backbone.View onSeekClicked: (percent) -> @trigger 'seek-clicked', percent + showOverlay: (html) -> + element = $(html) + element.css('margin-right': "#{@rightClipWidth}px") if @rightClipWidth + @$el.append(element) + showLoadingOverlay: -> - @$el.append('
') + @showOverlay('
') hideLoadingOverlay: -> @$('.loading').remove() showPlayOverlay: -> - @$el.append('
') + @showOverlay('
') hidePlayOverlay: -> @$('.start-prompt').remove() diff --git a/app/assets/javascripts/player/views/renderers/base.js.coffee b/app/assets/javascripts/player/views/renderers/base.js.coffee index 878cae0..59c8b58 100644 --- a/app/assets/javascripts/player/views/renderers/base.js.coffee +++ b/app/assets/javascripts/player/views/renderers/base.js.coffee @@ -10,6 +10,15 @@ class AsciiIo.Renderer.Base extends Backbone.View @clearState() requestAnimationFrame @render + width: -> + @cols * @cellWidth + + height: -> + @lines * @cellHeight + + elementWidth: -> + @$el.outerWidth() + clearState: -> @state = changes: {} diff --git a/app/assets/javascripts/player/views/renderers/canvas.js.coffee b/app/assets/javascripts/player/views/renderers/canvas.js.coffee index ff5c7bb..bc3cf88 100644 --- a/app/assets/javascripts/player/views/renderers/canvas.js.coffee +++ b/app/assets/javascripts/player/views/renderers/canvas.js.coffee @@ -12,11 +12,8 @@ class AsciiIo.Renderer.Canvas extends AsciiIo.Renderer.Base @cursorVisible = true fixTerminalElementSize: -> - width = @cols * @cellWidth - height = @lines * @cellHeight - - @$el.attr('width', width) - @$el.attr('height', height) + @$el.attr('width', @width()) + @$el.attr('height', @height()) @setFont() diff --git a/app/assets/javascripts/player/views/renderers/pre.js.coffee b/app/assets/javascripts/player/views/renderers/pre.js.coffee index 33aa448..03da5b8 100644 --- a/app/assets/javascripts/player/views/renderers/pre.js.coffee +++ b/app/assets/javascripts/player/views/renderers/pre.js.coffee @@ -18,10 +18,7 @@ class AsciiIo.Renderer.Pre extends AsciiIo.Renderer.Base i++ fixTerminalElementSize: -> - width = @cols * @cellWidth - height = @lines * @cellHeight - - @$el.css(width: width + 'px', height: height + 'px') + @$el.css(width: @width() + 'px', height: @height() + 'px') render: -> if @state.dirty diff --git a/app/assets/stylesheets/asciicasts.css.sass b/app/assets/stylesheets/asciicasts.css.sass index b8a780f..cb5be4e 100644 --- a/app/assets/stylesheets/asciicasts.css.sass +++ b/app/assets/stylesheets/asciicasts.css.sass @@ -96,3 +96,8 @@ div.presentation .other-asciicasts margin-top: 70px + +.extras + .embed-script + width: 100% + margin: 5px 0 diff --git a/app/assets/stylesheets/embed.sass b/app/assets/stylesheets/embed.sass new file mode 100644 index 0000000..65e7b5a --- /dev/null +++ b/app/assets/stylesheets/embed.sass @@ -0,0 +1,4 @@ +//= require player + +body.iframe + background-color: transparent diff --git a/app/controllers/asciicasts_controller.rb b/app/controllers/asciicasts_controller.rb index 99fd004..57dc22e 100644 --- a/app/controllers/asciicasts_controller.rb +++ b/app/controllers/asciicasts_controller.rb @@ -5,7 +5,7 @@ class AsciicastsController < ApplicationController before_filter :ensure_authenticated!, :only => [:edit, :update, :destroy] before_filter :ensure_owner!, :only => [:edit, :update, :destroy] - respond_to :html, :json + respond_to :html, :json, :js def index @asciicasts = PaginatingDecorator.new( @@ -41,6 +41,10 @@ class AsciicastsController < ApplicationController respond_with AsciicastJSONDecorator.new(@asciicast) end end + + format.js do + respond_with @asciicast + end end end diff --git a/app/decorators/asciicast_decorator.rb b/app/decorators/asciicast_decorator.rb index df7caba..aa1bffb 100644 --- a/app/decorators/asciicast_decorator.rb +++ b/app/decorators/asciicast_decorator.rb @@ -99,6 +99,12 @@ class AsciicastDecorator < ApplicationDecorator end end + def embed_script + src = h.asciicast_url(model, :format => :js) + id = "asciicast-#{id}" + %() + end + private def prepare_lines(lines, width, height) diff --git a/app/helpers/asciicasts_helper.rb b/app/helpers/asciicasts_helper.rb index 24b98a2..66dbfeb 100644 --- a/app/helpers/asciicasts_helper.rb +++ b/app/helpers/asciicasts_helper.rb @@ -20,6 +20,7 @@ module AsciicastsHelper speed: (options[:speed] || params[:speed] || 1).to_f, benchmark: !!params[:bm], asciicast_id: asciicast.id, + container_width: params[:container_width], renderer_class: renderer_class, auto_play: options.key?(:auto_play) ? !!options[:auto_play] : false, hud: options.key?(:hud) ? !!options[:hud] : true, diff --git a/app/views/asciicasts/_player.html.erb b/app/views/asciicasts/_player.html.erb index 74e3360..9ee9d43 100644 --- a/app/views/asciicasts/_player.html.erb +++ b/app/views/asciicasts/_player.html.erb @@ -9,6 +9,7 @@ speed: <%= speed %>, benchmark: <%= benchmark %>, model: new AsciiIo.Asciicast({ id: <%= asciicast_id %> }), + containerWidth: <%= container_width || 'null' %>, rendererClass: <%= renderer_class.html_safe %>, autoPlay: <%= auto_play %>, hud: <%= hud %>, diff --git a/app/views/asciicasts/_share.html.erb b/app/views/asciicasts/_share.html.erb deleted file mode 100644 index 1b24740..0000000 --- a/app/views/asciicasts/_share.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Share

- - diff --git a/app/views/asciicasts/_share.html.slim b/app/views/asciicasts/_share.html.slim new file mode 100644 index 0000000..9e4f26a --- /dev/null +++ b/app/views/asciicasts/_share.html.slim @@ -0,0 +1,6 @@ +h1 Share + +ul.delimited + li + ' Embed on your page + input[type="text" class="embed-script" value=@asciicast.embed_script data-behavior="auto-select" readonly] diff --git a/app/views/asciicasts/raw.html.erb b/app/views/asciicasts/raw.html.erb deleted file mode 100644 index c2ff8c7..0000000 --- a/app/views/asciicasts/raw.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= player @asciicast %> diff --git a/app/views/asciicasts/raw.html.slim b/app/views/asciicasts/raw.html.slim new file mode 100644 index 0000000..c37b094 --- /dev/null +++ b/app/views/asciicasts/raw.html.slim @@ -0,0 +1,11 @@ += player @asciicast + +javascript: + $(function() { + var target = parent.postMessage ? parent : (parent.document.postMessage ? parent.document : undefined); + if (typeof target != "undefined" && window !== window.parent) { + var w = $('.player').css('width'); + var h = $('.player').css('height'); + target.postMessage(['asciicast:size', { id: #{@asciicast.id}, width: w, height: h }], '*'); + } + }); diff --git a/app/views/asciicasts/show.html.erb b/app/views/asciicasts/show.html.erb deleted file mode 100644 index df6a073..0000000 --- a/app/views/asciicasts/show.html.erb +++ /dev/null @@ -1,25 +0,0 @@ -
-
- <%= player @asciicast %> -
-
- -
-
-
-

<%= @asciicast.title %>

- -
- <%= @asciicast.description %> -
- - <%= render :partial => 'other_by_user' %> -
- -
- <%= render :partial => 'author' %> - <%= render :partial => 'actions' %> - <%= render :partial => 'info' %> -
-
-
diff --git a/app/views/asciicasts/show.html.slim b/app/views/asciicasts/show.html.slim new file mode 100644 index 0000000..73c5f76 --- /dev/null +++ b/app/views/asciicasts/show.html.slim @@ -0,0 +1,15 @@ +section#presentation.feature + .asciicast-wrapper + = player @asciicast + +section.supplimental + .wrapper + .main + h1 = @asciicast.title + .description = @asciicast.description + = render :partial => 'other_by_user' + .extras + = render :partial => 'author' + = render :partial => 'actions' + = render :partial => 'info' + = render :partial => 'share' diff --git a/app/views/asciicasts/show.js.erb b/app/views/asciicasts/show.js.erb new file mode 100644 index 0000000..32dd830 --- /dev/null +++ b/app/views/asciicasts/show.js.erb @@ -0,0 +1,36 @@ +// ascii.io - embeddable player + +(function() { + function receiveSize(e) { + if (e.origin === document.location.protocol + "//<%= request.host %>") { + var event = e.data[0]; + var data = e.data[1]; + if (event == 'asciicast:size' && data.id == <%= @asciicast.id %>) { + var player = document.getElementById("asciicast-iframe-<%= @asciicast.id %>"); + if (player) { + player.style.width = data.width; + player.style.height = data.height; + } + } + } + } + + function insertAfter(referenceNode, newNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); + } + + var scriptTag = document.getElementById("asciicast-<%= @asciicast.id %>"); + + if (scriptTag) { + window.addEventListener("message", receiveSize, false); + var container = document.createElement('div'); + container.className = 'asciicast'; + container.style.display = 'block'; + container.style.float = 'none'; + container.style.overflow = 'hidden'; + container.style.padding = '0'; + insertAfter(scriptTag, container); + var containerWidth = container.offsetWidth; + container.innerHTML = ''; + } +})(); diff --git a/app/views/layouts/raw.html.erb b/app/views/layouts/raw.html.erb deleted file mode 100644 index ecad73b..0000000 --- a/app/views/layouts/raw.html.erb +++ /dev/null @@ -1,25 +0,0 @@ - - - - - <%= page_title %> - - - - - <%= stylesheet_link_tag "player", :media => "all" %> - <%= javascript_include_tag "application" %> - <%= javascript_include_tag "player" %> - - - - - - <%= yield %> - - diff --git a/app/views/layouts/raw.html.slim b/app/views/layouts/raw.html.slim new file mode 100644 index 0000000..a544e07 --- /dev/null +++ b/app/views/layouts/raw.html.slim @@ -0,0 +1,12 @@ +doctype html +html[lang="en"] + head + meta[charset="utf-8"] + title = page_title + = stylesheet_link_tag 'embed', :media => 'all' + = javascript_include_tag 'embed' + script + | window.unpackWorkerPath = '#{javascript_path "unpack_worker"}'; + window.mainWorkerPath = '#{javascript_path "main_worker"}'; + body.iframe + = yield diff --git a/config/environments/production.rb b/config/environments/production.rb index 0058c5d..2bf8689 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -43,7 +43,7 @@ AsciiIo::Application.configure do # config.action_controller.asset_host = "http://assets.example.com" # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) - config.assets.precompile += %w( player.css player.js unpack_worker.js main_worker.js ) + config.assets.precompile += %w( player.css player.js unpack_worker.js main_worker.js embed.css embed.js ) config.action_mailer.delivery_method = :sendmail diff --git a/spec/controllers/asciicasts_controller_spec.rb b/spec/controllers/asciicasts_controller_spec.rb index 0803143..c9fc242 100644 --- a/spec/controllers/asciicasts_controller_spec.rb +++ b/spec/controllers/asciicasts_controller_spec.rb @@ -94,6 +94,18 @@ describe AsciicastsController do it { should be_success } end + + context 'for js request' do + let(:asciicast) { FactoryGirl.build(:asciicast, :id => 666) } + + before do + ViewCounter.should_not_receive(:new) + + get :show, :id => asciicast.id, :format => :js + end + + it { should be_success } + end end describe '#edit' do diff --git a/spec/decorators/asciicast_decorator_spec.rb b/spec/decorators/asciicast_decorator_spec.rb index 7da77a8..016d9c5 100644 --- a/spec/decorators/asciicast_decorator_spec.rb +++ b/spec/decorators/asciicast_decorator_spec.rb @@ -240,4 +240,14 @@ describe AsciicastDecorator do describe '#other_by_user' do pending end + + describe '#embed_script' do + before do + asciicast.stub!(:id => 123) + end + + it 'should be an async script tag including asciicast id' do + expect(decorated.embed_script).to match(/^]+\b123\b[^>]+><\/script>/) + end + end end