IFrame Bookmarklet Example
Drag the link below into your bookmark bar.
Fvb IframeInitial Setup
XDM and IFrame bookmarklets allow you to insert an iframe from your app into the 'host' page which can communicate seamlessly with the bookmarklet code running in the 'host' page.
To enable cross domain communictaion between your site and other pages on the 'net we need to be able to generate a fully qualified URL to your bookmarklet code. To set the correct host you should add this to each of your confing/environment/*.rb files. Adjust the :host accordingly. :port is optional.
config.action_controller.default_url_options = {:host => 'localhost', :port => 3000}
XDM Basics
To enable cross-domain communication Easymarklet uses easyXDM to insert an iframe into the remote page which points back to your app. The code running in the context of the remote page is called the consumer. The code running on the page that has been iframed in is called the producer.
Function calls can be passed from the consumer to the producer, and the then the producer can interact with your app and then push a message back to the consumer. You could also use polling or websockets to allow your app to 'push' data to the remote page.
Generating this bookmarklet
rails g easymarklet:iframe fvb_iframe
You'll get a bunch of files.
$ rails g easymarklet:iframe fvb_iframe
create app/assets/javascripts/fvb_iframe_bookmarklet.js
create app/assets/javascripts/fvb_iframe_consumer.js
create app/assets/javascripts/fvb_iframe_producer.js
create app/controllers/fvb_iframe_producer_controller.rb
create app/views/fvb_iframe_producer/index.html.erb
create app/views/layouts/fvb_iframe_producer.html.erb
route match 'fvb_iframe_producer' => 'fvb_iframe_producer#index'
You can link to your new bookmarklet with this :
<%= link_to 'Fvb Iframe', easymarklet_js('fvb_iframe_consumer.js') %>
File Overview
- *_bookmarklet.js
- This is where your code goes. It's the central interface between your consumer (the code that runs on other pages), and your producer (the pages on your app). You don't link directly to this.
- *_consumer.js
-
A manifest that includes a consumer helper from Easymarklet and your bookmarklet code.
You link to this with the
easymarklet_js
helper. It will be loaded into the page after someone clicks your bookmarklet. - *_producer.js
- A manifest that includes a producer helper from Easymarklet and your bookmarklet code. This is included on page on your app that provides the service.
- *_producer_controller.rb
- An empty controller to handle serveing the producer page. Customize this if you need to make data available to the producer at load time.
- index.html.erb
- A basic template for the producer page.
- layouts/*_producer.html.erb
- A simple layout that includes the *_producer.js manifest.
Update the code
In the fvb_iframe_bookmarklet.js
file we just need to add
a link to a stylesheet to use for styling the iframe, and alter the init
function to set up our AJAX interaction with the server.
app/assets/javascripts/fvb_iframe_bookmarklet.js
(function(){
var FvbIframeBookmarklet = {
visible : true,
consumer : {
css : ['/assets/fvb_iframe_bookmarklet.css'], // could be an array or a string
methods : { // The methods that the producer can call
}
},
producer : {
path : "/fvb_iframe_producer", // The path on your app that provides your data service
init : function(consumer_url,consumer){
// Set up some click handlers
$('#vote_foo').click(function(){
doPost('Foo')
return false;
});
$('#vote_baz').click(function(){
doPost('Baz')
return false;
});
$('.fvb_close').click(function(){
consumer.closeFrame();
return false;
});
// Send the vote to the server via AJAX and pass the rusults off to the handleResults function
function doPost(vote){
url = consumer_url;
$.post( '/pages', { page : { url : url, vote : vote } }, handleResults, 'json')
}
// A handler to display the current voting results
function handleResults(data){
$("#foo_count").html(data.foo_count);
$("#baz_count").html(data.baz_count);
$('#vote').hide();
$('#results').show();
}
},
methods : { // The methods that the consumer can call
// We only need one way communication for this one.
}
}
}
window.FvbIframeBookmarklet = FvbIframeBookmarklet;
})();
Now we add just a touch of CSS that will control the appearance of our floating iframe. Since the UI for the bookmarklet will run inside the iframe (on our site, instead of directly in another page) we don't have to include CSS for any of our actual UI elements.
app/assets/stylesheets/fvb_iframe_bookmarklet.css
#easymarklet_div{
position:fixed;
top:10px;
left:10px;
background:black;
border:3px double white;
padding:5px;
-webkit-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);
-moz-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);
box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);
border-radius: 8px;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
z-index:10000;
}
Next we update the index.html.erb
template to include our voting options.
app/views/fvb_iframe_producer/index.html.erb
<div id="vote">
<p>Cast your vote now!</p>
<%= link_to "Vote Foo", "", :class => 'btn', :id => 'vote_foo' %>
<%= link_to "Vote Baz", "", :class => 'btn', :id => 'vote_baz' %>
<%= link_to "Nevermind", "", :class => 'fvb_close' %>
</div>
<div id="results" style="display:none;">
<p>Congratulations! Your vote has been counted.</p>
<ul>
<li>Foo : <span id="foo_count"></span></li>
<li>Baz : <span id="baz_count"></span></li>
</ul>
<%= link_to "Thank you, come again!", "", :class=>"fvb_close" %>
</div>
We also tweak the laoyout to include our logo bar.
app/views/layouts/fvb_iframe_producer.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Fvb Iframe Producer</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<%= render 'layouts/fvb_logo_bar' %>
<div id="main_content" class="well">
<%= yield %>
</div>
<%= javascript_include_tag "fvb_iframe_producer" %>
<script type="text/javascript" charset="utf-8">
var easymarklet_consumer_url = "<%= params['url'] %>";
</script>
</body>
</html>
Going to production
Before deploying you need to make sure that a few things are available as individual
files through the asset pipeline. Add this to the bottom of the config
block in config/application.rb
.
config.assets.precompile += %w( fvb_iframe_consumer.js )
config.assets.precompile += %w( fvb_iframe_producer.js )
config.assets.precompile += %w( fvb_iframe_bookmarklet.css )