All Posts in javascript

August 15, 2014 - Comments Off on How to Build an Easy Embedabble Widget

How to Build an Easy Embedabble Widget

Building with iframes is a fantastic way to create seamless, easy to implement, embeddable widgets. Once set up, creating multiple instances linking to your service is easy to do. Existing within a website, an iframe is like a window onto another website. Rather than forcing open another window, a user can interact with another service within the context of the website they have navigated to. The experience is smooth for the visitor, who will find the relevant service presented inline.

Over here at The Mechanism, we have been using iframes to integrate our custom bug tracking solution with client websites during user assisted testing. We needed an inconspicuous tool which would allow clients to seamlessly review work and submit bugs as they find them.

The requirements for this front end bug catcher:

  1. Simple to embed and easy to implement across many projects
  2. Sandboxed so that it doesn't cause any conflicts with the project DOM, JS or CSS
  3. Simple architecture which works on all browsers
  4. Responsive; it must work on all device sizes and form factors
  5. Context Aware; diagnostic information will require knowledge of the parent document (the website under bug tracking )

Our first iteration of the bug tracking widget violated the second requirement. For our first prototype, we loaded a script and pulled in our view and styling files through JSONP (a method for circumventing Same-Origin Policy, you can read about it here). This worked in our limited prototype but caused a few issues. First of all, we had to give our DOM elements verbose ID's and class names to ensure there would no conflict with the parent document. Secondly, we ran the risk of causing script conflicts with our dependencies. We used a script loader to minimize this risk, however we could never be sure. Finally, we were at the mercy of the stylesheets loaded by the parent which required us to write additional resets to ensure consistent appearance across projects. However, repairs like this are equivalent to bailing a sinking ship rather than repairing the leak.

So for our second iteration we converted the widget into an iframe. To do so we still had to find a way to get around the Same-Origin Policy. The Same-Origin Policy restricts communication between two documents; the parent window and the iframe. Cross Document Messaging is a new addition to the HTML5 specification, which allows for simple string communication between documents. This is supported by most modern browsers, however, there are many hacks necessary for older browsers.

easyXDM

easyXDM is a great library built to cross this great divide introduced by iframes. It uses the HTML5 postMessage() method when available and uses many fallbacks when necessary, ensuring the free flow of information between documents. For the developer, it exposes two protocols for data transfer. The first is a socket, which will send a string between the documents. This method requires us to parse and decipher the string before performing the relevant action on that information. The second option is an RPC (Remote Procedure Call), specifically JSON-RPC. JSON-RPC is a specification for calling functions from remote software, and for data to be returned. This allows for a much more dynamic interaction between our two documents where each process exists in their relative scope and can communicate as discreet functions would be expected. For our needs, the simpler option and the one we will be employing is the RPC protocol.

To implement easyXDM we must load our dependency and create an RPC instance, with the necessary proxy objects and method stubs. We will initiate this within a script loaded on the parent document. This script will embed our iframe and act as our gateway to the bug tracking widget.

On our parent document we will place an asynchronous script call to our remote script

 

// footer.html

 

In our script we will start by loading our easyXDM dependency

 

// main.js

var serverURL = 'http://www.example.com/our-remote-service/',

iframeFile = 'iframe.html',

depends = {

'easyXDM': serverURL + 'js/easyXDM.min.js'

};

Object.size = function(obj) {

var size = 0, key;

for (key in obj) {

if (obj.hasOwnProperty(key)) size++;

}

return size;

}

var scriptCount = Object.size(depends); // count of scripts required

var scriptLoads = 0; // count of script loaded

for (var key in depends) {

if (depends.hasOwnProperty(key)) {

loadScript(key, depends[key], function() {

scriptLoads++;

if (scriptLoads === scriptCount) {

main();

}

});

}

}

function loadScript (dependency, src, callback) {

// this function checks if the dependency is present.

// it waits for load before executing the callback.

if (window[dependency] === undefined) { // if dependency is not present

var scriptTag = document.createElement('script');

scriptTag.setAttribute('type', 'text/javascript');

scriptTag.setAttribute('src', src);

if (scriptTag.readyState) {

scriptTag.onreadystatechange = function () { // For old versions of IE

if (this.readyState == 'complete' || this.readyState == 'loaded') {

callback();

}

};

} else { // Other browsers

scriptTag.onload = callback;

}

(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(scriptTag);

} else {

callback();

}

}

 

  1. Lines 1-5 we are declaring a some variables that will be used later.
  2. Lines 7-16 we extend the Object object with a method which will return the length of our depends array on line 15, along with a variable to hold an index value.
  3. Lines 18-27, we run a loop through the depends array and call a function loadScript(), which takes the name of our dependency, the url it can be found at and a callback which will be run once the dependency is loaded.
  4. Lines 29-49, our function which will test the presence of the dependency and load the script if it is not found. It uses various methods to ensure the script is loaded before running the callback function.

Next we will create our RPC instance which will load the iframe

 

// main.js

.

.

.

var iframeContainer = document.createElement('div');

iframeContainer.style.position = 'fixed';

iframeContainer.style.zIndex = 999;

iframeContainer.style.bottom = 0;

iframeContainer.style.left = 0;

iframeContainer.style.top = "auto";

iframeContainer.style.right = "auto";

iframeContainer.style['max-height'] = '100%';

iframeContainer.style['max-width'] = '100%';

document.getElementsByTagName('body')[0].appendChild(iframeContainer);

var rpc = new easyXDM.Rpc({

remote: serverURL + iframeFile,

container: iframeContainer,

props: {

id: 'bug-iframe',

frameborder: '0',

scrolling: 'no',

marginwidth: '0',

marginheight: '0',

allowTransparency: 'true',

style: {

height: '100%',

width: '100%',

display: 'block'

}

}

},

{

local: {

resizeiFrame: function (widthReq, heightReq, allowScroll) {

var windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,

windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;

var width = (widthReq < windowWidth) ? widthReq : windowWidth;

var height = (heightReq < windowHeight) ? heightReq : windowHeight;

iframeContainer.style.width = width + 'px';

iframeContainer.style.height = height + 'px';

var sc = (allowScroll) ? 'yes' : 'no';

document.getElementById('mech-bug-iframe').scrolling = sc;

return {

x: width,

y: height

};

},

parentInfo: function () {

return {

width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,

height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,

url: window.location.href

};

}

}

});

 

  1. Lines 5-16; we are creating the iframes container with properties for it's layout within the parent documents DOM
  2. Lines 18-34; our RPC instance with the address to find the iframe contents, a container to place the div in, and some properties to control it's appearance
  3. Lines 34-64; is where we declare the methods we will be exposing to our iframe. resizeiFrame() and parentInfo() will allow us the adjust the size of the iframe and return diagnostic information respectively. They will be called from within our iframe

In our iframes markup we will load easyXDM and a shiv for older browsers without support for JSON, plus another .js file where we will instatiate our RPC connection.

 

 

In our iframe-main.js file, we will create another instance of easyXDM.Rpc and create stubs for our remote methods

 

// iframe-main.js

var rpc = new easyXDM.Rpc({},

{

remote: {

resizeiFrame: {},

parentInfo: {}

}

});

.

.

.

rpc.parentInfo(function(parentInfo) {

var diagObject = {

'width' = parentInfo.width,

'height' = parentInfo.height,

'url' = parentInfo.url

}

});

 

  1. Lines 2-8; we create our rpc object with the relevant stubs, referring to the remote methods
  2. Lines 14-20; an example of how we call our remote function. Notice the anonymous function we pass to the remote function to return our requested data. This is an asynchronous function

Stay tuned for more on the Venus project to find out where it goes next. Dhruv Mehrotra will be back in a few weeks with a blog post going over some of the steps taken to set up the Ruby on Rails server behind Venus. And we will have meetup at our offices the second week of September. Hope to see you there!

Published by: georgebrassey in The Programming Mechanism
Tags: , , , ,

July 29, 2014 - Comments Off on Important Advice – Talkback Tuesday

Important Advice – Talkback Tuesday

"Talkback Tuesdays" is an original weekly installment where a team member of The Mechanism is asked one question pertaining to digital design, inspiration, and experience. The Q&A will be featured here on The Mechanism Blog as well as on The Mechanism's Facebook, Twitter, and Instagram, every Tuesday. Feel free to offer up your 2ยข in the comments.

This week Dhruv Mehrotra, a key player on The Mechanism's development team, gives some valuable advice to anyone just getting their start in the digital/graphic design world.

What is the most important advice you can give to a starting graphic/digital designer?

I know this question is about design, but I'm going to take some liberties and answer as if I was speaking to someone interested in becoming a developer. Talking about the importance of computer literacy is an easy platitude to fall back on, and I assume that, if you are reading this, you already want to learn how to code but just don't know where to start.

I started with codecademy.com and worked my way through html, css, and basic javascript. Its easier than ever to learn this stuff, and a simple google search will yield more tutorials than there are cats on the internet. Regardless of where you get your information, I think it is important to start with basic HTML CSS and Javascript in order to get a grasp of what a simple website is about.

The next pieces of advice I have are simple. Build Stuff. Build anything. Build a portfolio. Build a website for your cat. Build a joke website. Rebuild a site you think is cool. Just hit the ground running and put as many hours as you can into this.

Next I would learn to share. Don't keep your websites hoarded on your local machine like a mom hoarding baby teeth. Buy a domain name and a hosting plan. While you're at it figure out what is hosting anyways? What's a server? How does the web work? What's a GET request? Gaining a conceptual understanding of what web development is is the most important part of your learning because it'll inform how you learn more.

For extra credit I would learn GIT, because source control make recently self-taught developers hireable.

June 20, 2014 - Comments Off on Building The BugTrap JavaScript Widget

Building The BugTrap JavaScript Widget

Over here at The Mechanism's headquarters, Team Mechanism has been busy working on a better way to track bugs, code named: project:Venus. We want to make it easier for our clients to report bugs while reviewing projects and improve our workflow by allowing internal communication on a bug by bug basis.

The idea came to us as while working on another project. We realized we had the technology to build a swift prototype by leveraging tools that were already part of our arsenal. More on this in later posts. For now, we will focus on the front end javascript widget, "The Bug Trapper" if you will.

As an agency, we often have many projects in process so our bug tracker needs to be easy to implement across multiple projects and domains: we wanted to use a simple script tag which would be added to projects during test phases, with the project id included in the script 'src' GET parameters.

This posed a couple problems:

  1. Scripts are not aware of the GET parameters from their requests
  2. AJAX requests cannot load anything other than scripts from other domains

 

1. Javascript and Parameters Passed through GET Requests

We came across this tip for pulling the parameters from the script request. It actually has little to do with a GET request as the parameters are parsed by the script on the client side. By providing the name of the script, it searches the DOM for itself (javascript has no awareness of how it has been called or where it exists in the DOM). And then we do some regex magic to construct an object of key/value pairs. We have abstracted the code slightly from the original. Below is our script:

 

// Extract "GET" parameters from a JS include querystring

function getScriptTag(script_name) {

// Find all script tags

var scripts = document.getElementsByTagName("script");

// Look through them trying to find ourselves

for(var i=0; i<scripts.length; i++) {

if(scripts[i].src.indexOf("/" + script_name) > -1) {

return scripts[i]

}

}

// No scripts match

return {};

}

function getParams(script_tag) {

// Get an array of key=value strings of params

var pa = script_tag.src.split("?").pop().split("&");

// Split each key=value into array, the construct js object

var p = {};

for(var j=0; j <pa.length; j++) {

var kv = pa[j].split("=");

p[kv[0]] = kv[1];

}

return p;

}

 

 

2. Cross Domain AJAX Requests

For security purposes, AJAX requests only accept scripts from other domains. This is a problem for our widget which we'd like to build modularly, separating the script logic, markup and styling into separate files, while maintaing the simplicity of including a single script when creating new instances.

There is a work around and it involves turning the response from our server into JSONP. JSONP is JSON with Padding. Essentially our server response is turned into a JSON object and then gets wrapped in a function, which will get called on the client side and return an object containing our data.

Thank the heavens for jQuery. jQuery's AJAX/getJSON method has baked in support for JSONP which will expect the callback function name to be a random string (an added layer of security), and will process the data, provided it gets the correct response from the server. On the client side, all we need to do is indicate we will be expecting the response to contain a callback function by adding "?callback=?" to our URL.

 

var stylesheetURL = "http://example.com/getmystylehsheet?callback=?";

$.getJSON(requestURL, function(data) {

$('head').append('

');

}

 

On our rails server we route this request by wrapping the intended response in a function, the name of which is passed by jQuery as a parameter through the request. Below is the ruby on rails controller code to do this on our server:

 

def getmystylesheet

css = File.read("path/to/stylesheet.css").to_s

json = {"css" => css}.to_json

callback = params[:callback]

jsonp = callback + "(" + json + ")"

render :text => jsonp, :content_type => "text/javascript"

end

 

We're beginning a test cycle with the bug tracker on some internal projects and we will be rolling this out on upcoming client work. Hopefully, with feedback from our clients, we will continue this project with a view to scaling it. Stay tuned for future updates!

Published by: georgebrassey in The Programming Mechanism
Tags: , , , ,

September 13, 2012 - Comments Off on Drush de Jour: The Case of the External Links and Broken JS

Drush de Jour: The Case of the External Links and Broken JS

The External Links module for Drupal is a nice, unobtrusive addition that adds an arrow icon next to off-site links to let users know the link will take them to a new site and open a new browser window. There is also a cool feature to include or exclude links based on regular expressions. Wheeeee!!!!!

Be warned, however, that you may enter a valid regular expression that could cause the javascript on your site to take a header. You will immediately know this is the case when you save your changes and the fieldset for the pattern matching is no longer collapsible. What happens is that the regular expression you entered is not validated against potential conflicts in the javascript that parses it on rendering. We had this issue when we tried to add an inclusion rule for PDF's. We wanted all PDF's hosted on the site to open a new window, despite the fact that they were 'internal'. Seems easy, add a wildcard and the extension and call it a day....

*.pdf

That didn't work. This isn't command line DOS, or *nix- add an escape to look for the period. It also broke Javascript on the site!

Drush to the rescue.

This went from a quick module addition to a murder mystery, minus the murder. Well, when clients aren't happy it might. Here's how we diagnosed the issue, which turned out to be fairly simple, but the troubleshooting process is important.
First off, since we work in a development site no one was going to be adversely affected. Next we know what was going on at the time things broke. I was the only one working on the site at the time so that narrowed it down to me and the changes I was making. If you have multiple developers working on a site concurrently, then version control, and backups are paramount.

So, we know I broke the site, and it was something to do with the External Links and specifically the regular expression I had just tried to save. This most likely means that the module saves data to the database in a custom table, or as a site variable. Drush can help inspect the second route very quickly-

% drush vget extlink
extlink_alert: 0
extlink_alert_text: "This link will take you to an external web site. We are not responsible for their content."
extlink_class: "ext"
extlink_exclude: ""
extlink_include: "*.pdf"
extlink_mailto_class: "mailto"
extlink_subdomains: 1
extlink_target: "_blank"

There it is! If no variables were found, then the next step is to check out the .install file for the offending module and look for a schema hook. But, we lucked out and can move along to spewing poorly formed regular expressions.

% drush vdel extlink_include and refresh, and we're back in business.
*.pdf

That didn't work, duh. This must be a 'javascript supported' pattern as the documentation says.

.*.pdf.

And now PDF's will open in a new window.

Links:

External Links

The Drush de Jour brings you the latest and possibly greatest bits of drush wisdom we use for developing and maintaining our Drupal sites.

 

Published by: chazcheadle in The Programming Mechanism
Tags: ,

November 10, 2008 - Comments Off on CSS Sprites2 in Prototype and script.aculo.us

CSS Sprites2 in Prototype and script.aculo.us

Over the weekend, I finally got around to porting Dave Shea's CSS Sprites2 technique to Prototype and script.aculo.us.

My version of CSS Sprites2 is almost exactly the same as Dave's, but it also includes keyboard support. There's a matching focus, blur, keydown and keyup event handler for each mouse event handler.

Get CSS Sprites2 for Prototype and script.aculo.us.

Jeffrey Barke is senior developer and information architect at theMechanism, a multimedia firm with offices in New York, London and Durban, South Africa.

Published by: jeffreybarke in The Programming Mechanism
Tags: ,