/jquery-preview

jQuery Preview is a plugin by Embedly that allows developers to create tools that enable users to share links with rich previews attached.

Primary LanguageJavaScriptBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

jQuery Preview

jQuery Preview is a plugin by Embedly that allows developers to create tools that enable users to share links with rich previews attached. This method of letting users select thumbnails, edit title and description has been adopted as the norm across the web. This plugin allows developers to easily implement this functionality without building the entire infrastructure themselves. Instead relying on on Embedly to generate the metadata.

We have made this plugin overly verbose and infinitely customizable. Our goal is not to dictate design, merely give a set of tools to make it easy to create a custom experience.

Basic Setup

To get started you need to put jQuery, Embedly jQuery, jquery.preview.js and preview.css. into your page:

<head>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
  <script src="http://cdn.embed.ly/jquery.embedly-3.0.5.min.js" type="text/javascript"></script>
  <script src="http://cdn.embed.ly/jquery.preview-0.3.2.min.js" type="text/javascript"></script>
  <link rel="stylesheet" href="http://cdn.embed.ly/jquery.preview-0.3.2.css" />
</head>

Next set up a simple form that allows a user to input link:

<form action="/update" method="POST">
    <input id="url" type="text" name="url"/>

    <!-- Placeholder that tells Preview where to put the selector-->
    <div class="selector-wrapper"></div>
</form>

You then need to tell preview what field to listen to:

<script>
    // Set up preview.
    $('#url').preview({key:'your_embedly_key'})

    // On submit add hidden inputs to the form.
    $('form').on('submit', function(){
      $(this).addInputs($('#url').data('preview'));
      return true;
    });
</script>

And you are done setting up the front end solution for jQuery Preview. The rest of this document will go into how to customize Preview.

How it Works

The plugin works by listening to the field on paste, keyup, preview and blur. If a URL is entered a call is made to Embedly's Extract endpoint and the result is then passed to the render function. render uses sprintf to create a selector, so the user can pick a thumbnail associated with the URL.

jQuery Preview just saves all the data and changes associated with the URL on the input using jQuery's data. It's up to the developer to choose how they want to deal with that data on submit. jQuery Preview comes with a helper function addInputs so all the data can be added to the form. This is the code to enable it:

// On submit add hidden inputs to the form.
$('form').on('submit', function(){
  $(this).addInputs($('#url').data('preview'));
  return true;
});

The developer can then choose how they wish to display the preview in the feed. We will show some simple examples for handling this in the documentation.

Preview

Preview holds all the logic for calls to Embedly and rendering the selector.

Options

bind

By default preview will bind to keyup, blur and paste if you wish to define your own bindings you can set this attribute to false:

// Don't bind the listeners
$('#url').preview({bind:false});

// When a use hits Attach, trigger a preview of the link.
$('a.attach').on('click', function(){$('#url').trigger('preview')});
error

Called when the url that the user inputed throws an error somewhere along the way. Depending on the use case you may want notify the user or hide it from them. The default behavior is to not notify the user. To notify them you can use:

$('#url').preview({
  error: function(obj){
    alert('The URL you entered was not processed.');
  }});
success

Called after a URL has been successfully rendered by the selector. Useful if you want to change state after the URL has been successfully processed. For example if you want to change a button from 'Share' to 'Submit' you can do it like so:

$('#url').preview({
  success : function(obj){
    $('button').text('Submit');
  }
});

render

If you would like to create your own selector, overwrite render with your own function. We will go in more detail, in the writing your own selector function, but here is the simplest example I could think of:

$('#url').preview({
  render: function(obj, options){
    // Put the title after the input.
    $(this).after(obj.title);
  }});

Events

preview

Manually trigger the input to fetch the URL. This is generally only needed if bind is set to false:

$('a.attach').on('click', function(){$('#url').trigger('preview')});
loading

Triggered when the request is being made to the API endpoint:

$('#url').on('loading', function(){$('.loading').show()});
loaded

Triggered when a response has been returned from the API endpoint:

$('#url').on('loaded', function(){$('.loading').hide()});
close

Triggered when the selector should be closed. If you are implementing a custom selector, you must listen for this event:

var render = function(data, options){
  // Insert selector
  $(this).on('close', function(){
    //Remove selector.
  });
}

Custom Selector

It's actually recommended that you build a custom selector. While the default one is pretty good, you are going to want to customize it more than just the default styles. Here's how you can create your own.

Render

To create our own selector you just need to pass in a function into the render option when calling preview:

var render = function(data, options){
  // custom render
};
$('#url').preview({render:render});

The render function should accept a data argument that is the response from the extract endpoint. options is also passed in if you would like to set custom options in preview. this is the HTMLElement of the input. This will let you listen for events or trigger events on the input.

The simplest version of a custom selector looks like so:

var render = function(data, options){
  // Add the title after the input.
  $(this).after('<span>'+data.title+'</span>');

  // remove the selector
  $(this).on('close', function(){
    $(this).siblings('span').remove();
  });
};

Display

jQuery Preview has no concept of displaying the item in a feed. This should be handled by the developer. This is a simple example from one of the demos:

$('#preview_form').on('submit', function(){
  // Preview data.
  var preview = $('#id_url').data('preview');

  // Close the selector
  $('#id_url').trigger('close');
  $('#id_url').val('');

  // Create a post using mustache, i.e. the nice way.
  var template = ['<div class="row">',
    '<div class="large-3 columns">',
      '<img class="thumb" src="{{thumbnail_url}}"></img>',
    '</div>',
    '<div class="large-9 column">',
      '<a href="{{original_url}}">{{title}}</a>',
      '<p>{{description}}</p>',
    '</div>',
  '</div>'].join('');

  html = $(Mustache.to_html(template, preview));
  html.data('preview', preview);
  html.on('click', function(){
    var data = $(this).data('preview');
    // Insert the video or rich object.
    if (data.media.type === 'video' || data.media.type === 'rich'){
      $(this).html(data.media.html);
      return false;
    }
    return true;
  });
  // Display the item in the feed.
  $('#feed').append(html);
  return false;
});

Server Side

When the user submits the link, we must save it and echo it back to the page so it can be displayed. The code contains a simple Tornado example of this dance in app.py. When we set up the form we need to specify where we are going to be making the the AJAX post. In this case it's '/update':

<form method="post" action="/update">
  <input type="text" class="xlarge" name="url" id="id_url" />
  <input id="id_submit" type="submit" class="btn btn-primary" value="Share"/>
</form>

We then build a handler to deal with the form data:

class UpdateHandler(tornado.web.RequestHandler):

    def post(self):
        #Overly verbose
        data = {}
        for name in ['type', 'original_url', 'url', 'title', 'description',
            'favicon_url', 'provider_url', 'provider_display', 'safe',
            'html', 'thumbnail_url', 'object_type', 'image_url']:
            data[name] = self.get_argument(name, None)

        # This also works
        # data = dict([(k, v[0]) for k, v in self.request.arguments.items()])

        # Save the data off and return the object that we will pass back.
        obj = db.save(data)

        # Write a json response
        self.write(json.dumps(obj))

Once the data is successfully posted the response is passed into the Display object to be rendered and displayed.

CDN

To get you going even faster, Embedly hosts all the files you need on scripts.embed.ly. The latest version is available here:

http://cdn.embed.ly/jquery.preview-0.3.2.min.js
http://cdn.embed.ly/jquery.preview-0.3.2.css

Changelog

0.3.2

  • Fixed a bug with media types in addInputs

0.3.1

  • Fixed a bug with sprintf and added secure images.

0.3

  • Full rewrite to simplify adding jQuery Preview to a site.

0.2

  • Added the $.preview so it can be used before the a form is initialized.
  • Added error and callback to the Preview object.