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. There are 5 different demos that you should take a look at before getting started. It will give you a quick overview of what you should be able to build.
Note: The Preview endpoint is available on our Paid plans, http://embed.ly/pricing.
Basic Setup
To get started you need to put jQuery,
jquery.preview.full.js
and preview.css. into
the head
of 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://scripts.embed.ly/p/0.2/jquery.preview.full.min.js" type="text/javascript"></script> <link rel="stylesheet" href="http://scripts.embed.ly/p/0.2/css/preview.css" /> </head>
jquery.preview.full.js bundles Underscore.js and Mustache.js in with jquery.preview.js. If you
already have or use these two libs you can see up the head
of the document
like so:
<head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script> <script src="lib/mustache.js" type="text/javascript"></script> <script src="lib/underscore.js" type="text/javascript"></script> <script src="http://scripts.embed.ly/p/0.2/jquery.preview.min.js" type="text/javascript"></script> <link rel="stylesheet" href="http://scripts.embed.ly/p/0.2/css/preview.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"/> </form>
You then need to tell preview what field to listen to:
<script> $('#url').preview({key:'your_embedly_key'}) </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.
Note that the Preview endpoint is only available to Embedly developers with a Starter plan or above. You can sign up for a Starter plan at embed.ly/pricing.
How it Works
The plugin works by listening to the field on paste
, keyup
, attach
and blur
. If a URL is entered a call is made to Embedly's Preview endpoint and the result is then
passed to the Selector object and all the elements are added to the form as
hidden inputs. The Selector is then rendered using a Mustache template. The
user can use the selector to pick a thumbnail associated with the URL.
When the form is submitted each attribute it passed back to the action URL. The server then saves the information and either echos or augments the object and passes it back to the fronted. The Display object then renders the obj into the feed.
Each object is infinitely customizable, but the flow is the same. We will go over each object and how to customize them.
Objects
There are 3 basic objects that you should get familiar with in order to customize the selector.
Preview
Preview holds all the logic for calls to Embedly. You shouldn't have to do a ton of customizations here.
Functions
submit
Called when the form is submitted and all the attributes are passed in as an object. If you want to do something other than a POST to the action you can overwrite the method like so:
var preview = { // Instead of posting to the server, send the object to display for // rendering to the feed. submit : function(e, data){ e.preventDefault(); this.display.create(data); } } $('#url').preview({preview:preview});
bind
Called when the preview is set up to bind all the listeners to the file and the form. If you want to listen on custom events you can call it like so:
var preview = { bind : function(){ // Instead of listening to the field, wait for the user to click // 'Attach Link'. $('a.attach').bind('click', this.fetch); // Still bind the form to ``_submit`` that cleans up the data // before calling ``submit`` $(this.form).bind('submit', this._submit); } } $('#url').preview({preview: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:
var preview = { error : function(){ alert('The URL you entered was not processed.'); } } $('#url').preview({preview:preview});
callback
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 'Attach' you can do it like so:
var preview = { callback : function(){ $('button').text('Attach'); } } $('#url').preview({preview:preview});
Using Attach
If you missed it jQuery Preview listens to a special event attach
on the
field. If you wish to have a two step process of attaching the link to the
status and then sharing the link you can set something up like this:
<form id="preview_form" class="form-vertical" method="post" action="."> <input type="text" name="url" id="id_url" value="http://vimeo.com/18150336" /> <button id="id_attach">Attach</button> </form> <script> $('#id_attach').bind('click', function(e){ e.preventDefault(); $('#id_url').trigger('attach'); $('#id_attach').replaceWith($('<input/>').attr({ 'id' : 'id_submit', 'value' : 'Share', 'type' : 'submit' })); }); </script>
This triggers the attach
event on the field and that tells Preview to go
fetch the url and show it in the Selector.
Selector
Builds the selector that allows users to pick out a thumbnail.
Attributes
type
There are 3 different built-in types that a developer can choose from
Each allows for a different type of feel. We suggest using one of them to get started and customizing from there.
template
If you would like to use your own Mustache template to render the selector you can add it here. The default looks like:
<div id="selector" class="small"> <div class="thumbnail"> <div class="controls"> <a class="left" href="#">◀</a> <a class="right" href="#">▶</a> <a class="nothumb" href="#">✕</a> </div> <div class="items"> <ul class="images"> {{#images}} <li><img src="{{url}}"/></li> {{/images}} </ul> </div> </div> <div class="attributes"> <a class="title" href="#">{{title}}</a> <p><a class="description" href="#">{{description}}</a></p> <span class="meta"> <img class="favicon" src="{{favicon_url}}"> <a class="provider" href="{{provider_url}}">{{provider_display}}</a> </span> </div> <div class="action"><a href="#" class="close">✕</a></div> </div>
By default Preview passes the following attributes to the template
type
original_url
url
title
description
favicon_url
provider_url
provider_display
safe
html
thumbnail_url
object_type
image_url
All these responses are defined here except for the following:
object_type
- The oEmbed of the preview response. Will be either
video
,rich
,photo
orlink
. We use this to decide weather to embed thehtml
attribute. image_url
- If the
type
isimage
or theobject_type
isphoto
animage_url
will be the full sized image URL. thumbnail_url
- The URL that the user selected as the thumbnail for the embed.
template
can also be a dictionary if you want to use different templates for different ``object_type``s. For instance:template : { 'video' : 'Video Template', 'rich' : 'Rich Template', 'photo' : 'Photo Template', 'link' : 'Link Template' }
Functions
title
Allows users to click on the title in the selector and edit the text. It is set up like this in the
bind
function:$('.selector .title').bind('click', this.title);
description
Allows users to click on the description in the selector and edit the text. It is set up like this in the
bind
function:$('.selector .description').bind('click', this.description);
scroll
Simple logic that scrolls the image left and right in selector. It takes a direction (
left
orright
) and an event. We set it up like so:$('.selector .left').bind('click', _.bind(this.scroll, {}, 'left')); $('.selector .right').bind('click', _.bind(this.scroll, {}, 'right'));
nothumb
Logic that tells the form NOT to use a thumbnail when rendering a preview.
bind
calls it like so:$('.selector .nothumb').bind('click', this.nothumb);
clear
- Clears the selector, hides it and all the hidden inputs.
update
Update can be set to an input field to also update the value in the selector. This is useful if you want to have a different input field for title and selector. We use this from the Preview object like so:
$('#id_title').bind('keyup', function(e){ $.preview.selector.update(e); });
The element must have a
name
attribute of thetitle
ordescription
.bind
- Sets the listeners for the above functions. Don't overwrite this function unless you know what you are doing.
render
- Uses Mustache to render the template chosen by the developer. Don't overwrite this function unless you know what you are doing.
Display
Create's a Feed item for a given object.
Attributes
type
There are 3 different built-in types that a developer can choose from
Each allows for a different type of feel. We suggest using one of them to get started and customizing from there. The
status
type is the same assmall
, but takes an additional fieldstatus
and displays that in the item as well.template
The Mustache template that you would like to use to render the obj. The default one looks like this:
<div class="item"> <div class="thumbnail"> <a href="{{original_url}}"> <img title="{{title}}" src="{{thumbnail_url}}"/> <span class="overlay"></span> </a> </div> <div class="attributes"> <a class="title" href="{{original_url}}">{{title}}</a> <p class="description">{{description}}</p> <span class="meta"> <img class="favicon" src="{{favicon_url}}"/> <a class="provider" href="{{provider_url}}">{{provider_display}}</a> </span> </div> </div>
By default Preview passes the following attributes to the template
type
original_url
url
title
description
favicon_url
provider_url
provider_display
safe
html
thumbnail_url
object_type
image_url
All these responses are defined here except for the following:
object_type
- The oEmbed of the preview response. Will be either
video
,rich
,photo
orlink
. We use this to decide weather to embed thehtml
attribute. image_url
- If the
type
isimage
or theobject_type
isphoto
animage_url
will be the full sized image URL. thumbnail_url
- The URL that the user selected as the thumbnail for the embed.
template
can also be a dictionary if you want to use different templates for different ``object_type``s. For instance:template : { 'video' : 'Video Template', 'rich' : 'Rich Template', 'photo' : 'Photo Template', 'link' : 'Link Template' }
selector
- Tells create where to prepend the Feed item html to. The default value is
#feed
.
Functions
create
- Uses Mustache to render the template chosen by the developer. Don't overwrite this function unless you know what you are doing.
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://scripts.embed.ly/p/0.2/jquery.preview.min.js http://scripts.embed.ly/p/0.2/jquery.preview.full.min.js http://scripts.embed.ly/p/0.2/css/preview.css
The most current version of jQuery Preview will be available here:
http://scripts.embed.ly/p/jquery.preview.min.js http://scripts.embed.ly/p/jquery.preview.full.min.js http://scripts.embed.ly/p/css/preview.css
Development
The code is broken down into modules in the src
folder and built by
watch.py
. If you want to modify anything in preview.jquery.js
please
find the code in the module and run python watch.py
to build
preview.jquery.js
. preview.css
is a sass
generated file. Please modify preview.scss
by running sass --watch
preview.scss:preview.css
Changelog
0.2
- Added the $.preview so it can be used before the a form is initialized.
- Added
error
andcallback
to the Preview object.