0

I'm testing jquery-oembed-all as a solution for another question I have asked. The script is a small javascript file loaded in the page and provides the oembed function for fetching media embed code.

I'm getting the oEmbed iframe back from a link easily like this:

<script>
  $(function(){
    tag = $(document.createElement('div')).oembed('https://vimeo.com/40179668');
    $('.header').append(tag);
  });
</script>

This works great and adds the oEmbed-generated iframe element to the .header on the page. However I need the generated HTML as a string. Poking around in tag[0].outerHTML and even tag[0].parent()[0].outerHTML just revealed tag to be the original empty div created in the script, yet it clearly has all of the embed code because the embedded video loads on the page.

How do I get the HTML returned by the oembed function as a text string? Do I need to traverse the DOM from the created div tag to find it?

EDIT: I added some alerts and it's all to do with timing. Since it's a network call the oEmbed function hasn't come back with an HTML block by the time I'm querying the outerHTML.

EDIT 2: Looks like this can be solved with JS callbacks since the oEmbed call is asynchronous. When I get time to look for a solution I'll post it back here as an answer.

Community
  • 1
  • 1
Dan Weaver
  • 711
  • 8
  • 20
  • Did you find a solution to this by chance? – Natetronn Dec 07 '13 at 08:55
  • @Natetronn I've added my solution below. It's pretty hacky right now but it works. I'll be refactoring this soon and will post back. It also assumes you're doing this in a Rails app. – Dan Weaver Dec 07 '13 at 20:24

1 Answers1

0

SOLUTION:

Since this is a Rails app I decided to go for the oembed gem and parse the video links when the text is displayed rather than when it is entered. This is the best solution for me since storing the bare video URLs is more future proof than storing the full embed iframe code returned by oembed and hoping it never has to change.

This code was hacked together pretty quickly and needs to be refactored soon but it's working fine for now and failure isn't in any way critical to the app.

When displaying the lesson I run parse_media_embeds on the lesson.notes which finds all the links and for any matching the media sites it fetches the oEmbed code (with a rescue for errors) and replaces the link with the iframe. Totally janky but it allowed me to move on with the app.

I'll post new code when I refactor it.

VIEW: lesson.html.rb

...
<section class='lesson-notes'>
  <p><%= lesson.parse_media_embeds.html_safe %></p>
</section>
...

MODEL: lesson.rb

def parse_media_embeds
  # get an array of links in the notes
  links = URI.extract self.notes, /http(s)?/
  # due to wysihtml5 parsing each link is stored twice, once as url and once as text
  # like <a href='http://google.com'>http://google.com</a>
  # so remove the double references
  for pos in (0..links.count)
    link.delete_at(pos)
  end
  # now replace each link that matches the Regexp pattern with oembed version
  links.each do |link|
    # only replace links for YouTube, Vimeo and Soundcloud
    if link.match(/youtu\.be|youtube|vimeo|soundcloud/)
      # get the full link tag, which includes both instances of the link text
      string = /(<a\w*[^>]*>#{Regexp.escape(link)}[^<]*<\/a>)/
      embed = get_oembed(link)
      self.notes = self.notes.gsub(string, embed) if embed
    end
  end
  self.notes
end

MODEL: lesson.rb

def get_oembed(link)
  # each provider has a different set of params
  if link.match(/youtu\.be|youtube/)
    begin
      resource = OEmbed::Providers::Youtube.get(link, {maxwidth: 320})
    rescue
      return
    end
    resource.html
  elsif link.match(/vimeo/)
    begin
      resource = OEmbed::Providers::Vimeo.get(link, {maxwidth: 320})
    rescue
      return
    end
    resource.html
  elsif link.match(/soundcloud/)
    begin
      resource = OEmbed::Providers::SoundCloud.get(link, {color: '39d', show_comments: false})
    rescue
      return
    end
    resource.html
  else
    return
  end
end
Dan Weaver
  • 711
  • 8
  • 20