Blog

Posts Tagged ‘javascript’

UPDATED: New Facebook Phonebook Script

Posted on:Monday, July 19th, 2010 by Ashish Datta

I realized this morning that Anonymous Coward’s Facebook Phonebook Greasemonkey script broke awhile back so I decided to rewrite it from scratch.

The original instructions for how to install the script are available here.

I updated the original Userscripts page with the new script so you can download it here.

Once again, this probably breaks your Facebook TOS so I can’t vouch for the safety of your account if you do decide to do this.

QR Bookmarklet

Posted on:Sunday, June 13th, 2010 by Ashish Datta

I got tired of having to find the same website (mostly recipes) on my phone after looking at them on my workstation or laptop so I decided to whip together a bookmarklet to throw a Google powered QR code on any page.

The bookmarklet will just slap a QR code image with the current page’s URL (window.location) so that you can open the page on your phone. ps. Barcode Scanner for Android will automatically open the URL in a browser.

Without further ado, QR Code Bookmarklet

jQuery.trigger weirdness

Posted on:Thursday, June 10th, 2010 by Ashish Datta

Earlier today, I was trying to use jQuery to trigger the submission of a form after a radio button was clicked. The form tag looked something like:

...form..

So for a regular submit button:


Everything works fine, you’ll see the alert() and the form won’t submit because of the return false.

I ran into issues when I tried to trigger() the submit event with jQuery. I was trying to trigger the submit() event on the form via jQuery. The problem I ran into, was that the saysomething() function was getting called, but the “return false” seemed to have no effect.

The final form looked something like:

For some reason, if you submit the form via a jQuery trigger the form submits even though saysomething() gets called. I’m not sure if this is the expected behavior but it was certainly something of a shock. Anyway, a live version of the form is running here.

jQuery blank() modified for password fields

Posted on:Wednesday, April 28th, 2010 by Ashish Datta

We’ve been using Jeff Hui’s very awesome jquery.blank plugin for sometime over at Setfive HQ. What blank() is allow you to basically move the labels for text inputs into the input themselves (to save space). We use this technique frequently for login boxes in headers since it’s easier not to have to stick in labels next to text boxes.

The problem is, you can’t use blank() on a password field since a password field won’t display clear text (obviously). To get around this, I’ve always manually stuck in a “shadow” text box next to the password field and toggled the text box or password box in order to make blank() work correctly.

Anyway, I finally got tired of doing this so I decided to patch the plugin to do this automatically. Jeff incorporated the code back into blank() and it’s available on GitHub here.

Happy coding.

javascript – $(document).ready getting called twice? Here’s why.

Posted on:Monday, February 22nd, 2010 by Matt Daum

Recently we found ourselves having a really weird problem on a project: Every time a page was loaded it seemed a bunch of different Javascript functions were being called multiple times and making many widgets on the page break and we couldn’t figure it out. Some of the functions were our own code, some part of the package we were using. After a while we narrowed it down to that all the functions in the

$(document).ready(...);

we’re being called twice. We had never seen this. After about an hour of removing javascript files and just headbanging, and many thanks to Ashish, we found the root cause. We had written in a quick hack for a late proof of concept to string replace on the entire HTML of a page a specific string. We did it this way:

$('body').html($('body').html().replace(/{REPLACETEXT}/i, "More important text"));

Basically we used a regex to parse the entire HTML tree and then replace it with the updated text. Unknowingly this caused the document ready event to be triggered again(though now it makes sense), causing many widgets to get extra HTML.

Let this save you some headbanging.

jQuery UI $.dialog – on the fly HTML

Posted on:Friday, November 13th, 2009 by Ashish Datta

Wow its been awhile!

We’ve been insanely busy over the last month or so. We launched Setfive Ventures and are anxiously anticipating the launch of both WeGov and OmniStrat in the immediate future. There are also a handful of internal project that should be rolling out before Christmas. Get Excited.

Anyway, the jQuery UI Dialog class is pretty sweet. Basically, it provides a class to display a modal dialog box from a regular old DOM element (a div, span, or whatever.)

One of the thing that isn’t explained well (or at all?) in the documentation is that you can create a dialog with on the fly HTML! I found this out after posting on the Google Group asking why this feature didn’t exist (it does. Ashish fail.)

So if you want to create a dialog with on the fly HTML all you need to do is:

$("<p>Hello World!</p>").dialog();

Pretty sweet.

Random acts of madness: JS+Flex+Rhino – WebWorkers for IE

Posted on:Tuesday, October 6th, 2009 by Ashish Datta

Preface: This is a bad idea with untested code. If you deploy it on a production server bad things will happen.

A few weeks ago I was trolling the Internet and ran across an interesting piece over at John Resig’s blog about Javascript WebWorkers. Basically, WebWorkers are a draft recommendation that allow you to run Javascript functions on a background (non-UI thread) thread. Essentially, they would allow you to do long running computations without hanging the browser’s UI. Pretty neat. Problem is that they are currently only available in Firefox 3.5+, Safari 4, and Chrome 3.0

In my never ending quest to use every buzzword at least once I decided to try and implement a compatibility layer to bring support for WebWorkers to other browsers. The plan was to use Java6′s new embeded Javascript interpreter (it’s just Rhino) to execute the WebWorker code server side and then pipe the output back to the client. Again, this is really just a proof of concept.

There are three parts to the rig: the client Javascript library, a Flex/AS3 application for streaming client to server communication, and a Java application that uses Rhino to execute the Javascript.

Client Javascript

The client Javascript detects the user’s browser and then will define a “Worker” object if the user’s browser doesn’t support WebWorkers. The new “Worker” object uses the Flex application to pass messages back and forth to the server and calls the user’s onmessage function when data arrives from the server.

I sniped the browser detection code from Quirksmode and it seems to work fairly well. The rest of the code is below:

BrowserDetect.init();

var sfWebWorkers = new Array();

var SF_WORKER_SERVER = "192.168.1.102";

var SF_WORKER_PORT = "9999";

var sfWwConduitIsLoaded = false;

function sfWebWorkersRecieveData(msg){

  var obj = $.evalJSON( msg );

  var e = new Object();

  e.data = obj.data;

  sfWebWorkers[ obj.sfWebWorkerId ].onmessage( e );

}

function sfWebWorkersSWFReady(isReady){

  sfWwConduitIsLoaded = true;

}

if(!((BrowserDetect.browser == "Firefox" && BrowserDetect.version == "3.5")

	    || (BrowserDetect.browser == "Safari" && BrowserDetect.version == "4")) ){

  $(document).ready( function(){

    var params = "{\"allowscriptaccess\": \"always\"}";

    var vars = "{\"server\": \"" + SF_WORKER_SERVER + "\"" 

                + ", \"port\": \"" + SF_WORKER_PORT + "\"}";

    $("body").append( "
" ); $("body").append( "" ); }); var Worker = function(fileName){ this.messages = new Array(); this.fileName = fileName; this.id = sfWebWorkers.length; this.isLoaded = false; sfWebWorkers.push( this ); var pathToFile = "http://" + window.location.hostname + ":" + window.location.port + "/" + fileName; var myId = this.id; var loadWorker = function(){ if( sfWwConduitIsLoaded ){ sfWebWorkers[ myId ].isLoaded = true; getFlashMovie("sfWebWorker").sendDataToFlash( $.toJSON( { message_type: 1, id: myId, message: pathToFile } ) ); }else{ window.setTimeout( function(){ loadWorker(); }, 500 ); } }; loadWorker(); }; Worker.prototype.postMessage = function(data){ var myId = this.id; var isLoaded = this.isLoaded; var sendData = function(data){ if( sfWwConduitIsLoaded ){ var e = new Object(); e.data = data; getFlashMovie("sfWebWorker").sendDataToFlash( $.toJSON( { message_type: 2, id: myId, message: $.toJSON(e) } ) ); }else{ window.setTimeout( function(){ sendData(data); }, 500 ); } }; sendData(data); }; } function getFlashMovie(movieName) { var isIE = navigator.appName.indexOf("Microsoft") != -1; return (isIE) ? window[movieName] : document[movieName]; }

Flex/AS3 Application

The Flex application is basically a dumb conduit between the server and the client. All it really does is pass messages between the Java on the server and the Javascript on the client.

The trickiest part of getting this to work was Adobe’s insane rules for allowing their Socket classes to connect to servers. In order for the client to successfully connect to the server you need to serve a XML policy file from port 843. Additionally, this file can’t be served by a HTTP server but must be a custom server that only spits back the file along with a null carriage return. A detailed description of this abortion is here http://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html

This really posses two problems. One, you need to be running some random “policy file server” for Flex sockets to be of any use. And two, since 843 is a privileged port, this server can’t be started by a regular user.

The most interesting parts of the ActionScript are probably the snippets that call out to Javascript functions:

ExternalInterface.call("sfWebWorkersSWFReady", true);

Java Server

The most complicated part of this whole thing is probably the Java application that actually executes the WebWorker Javascript. All the communication between the Flex and Java is done entirely with JSON. The server basically does the following:

  1. Listen for connections from the Flex and accept them when they come in.
  2. When a message comes in – it can either be a request to create a new web worker or a postMessage() event containing some data for an existing worker.
  3. If it’s a request for a new worker, the server will download the Javascript file and then execute it inside a Rhino container.
  4. Otherwise if Flex passed a postMessage() message the server will forward that data to the running web worker.
  5. The other event that happens is that a web worker can send messages back to the Flex.

Anyway, I tested this on IE7+ and it seemed to work decently well. Per the warning on top I don’t want to leave this running on a live server anywhere.

If you want to get it to actually run, do the following:

  1. Download the zip of all the sources here.
  2. Start the JAR in WebWorkerServer/WebWorkerServer.jar with java -jar WebWorkerServer.jar 9999
  3. On the top of web/sfwwcompat.js change the IP address or the server to where your Java server is located (localhost if you want)
  4. Open web/wwsha1.html in IE or Chrome 2.0 and you should see stuff happen.

What’s in the box:

  • web/ contains the Javascript and a demo.
  • WebWorkerConduit/ contains the Flex applicaiton.
  • WebWorkerServer/ contains the Java server.

Credits: I borrowed the WebWorker SHA1 implementation from John Resig who adapted it from Ray C Morgan.

So here is another crazy idea. Instead of executing the WebWorker code on the server, would it be possible to dynamically make the WebWorker code re-entrant using setTimeout() on the client where loop structures exist?

FanFeedr Widgets Are Live!

Posted on:Wednesday, September 30th, 2009 by Ashish Datta

Over the past few weeks we had the opportunity to work with FanFeedr to put together some widgets for their sports news platform. Previously, FanFeedr had been using Sprout to build their widgets but this required someone to hand build a Flash widget for every “resource” on FanFeedr (there are a lot). In addition, since the Sprout widgets are Flash they aren’t easily crawled by search engines.

Our widgets are different. They allow FanFeedr to generate widgets on the fly for any of their pages and allow users to customize the color schemes. Check out a widget builder for the NY Yankees here.

Basically, our widget builder works by allowing users to customize the size and colors used in the widget. This data is serialized as a JSON object and then base64 encoded so that it can be sent to the “generator” on the server. Then, the server unpacks the payload and builds a widget according to the data specified in the JSON object. In addition, our embed code includes a noscript tags so that search engines pick up the links in the widget as well.

Anyway, working with FanFeedr was a great experience and we hope to continue our relationship moving forward. Go build yourself a widget!

sfWidgetjQueryTimepickr – Symfony timepickr widget

Posted on:Monday, April 6th, 2009 by Ashish Datta

A couple of months ago John Resig posted on his blog about a “new” way for users to pick time.

The component is a jQuery plugin called timepickr and I thought it was particularly neat. Anyway, I finally needed to write a form which had a time component so I figured I’d drop in the timepickr jQuery plugin.

It didn’t look like there was a Symfony Forms widget for it so I whipped one up. You can grab it here.

The only issue with timepickr is that it introduces a ton of dependencies. It requires jQuery, jQuery-ui, jQuery.utils, jQuery.string, and ui.dropslide. Additionally, it needs the dropslide css as well as the timepickr css.

The form widget assumes that you will include jquery-ui by yourself since everyone usually has different naming conventions. It will include the other JS files and CSS files for you.

You can download a package with all of the JS and CSS you need here. Again this DOES NOT include the jQuery-ui stuff. YOU have to include that yourself in your Symfony project as well as on the page that you deploy this widget.

To use it, just drop it in your Symfony project somewhere where classes get autoloaded (projectdir/lib works) and then instantiate a widget with new sfWidgetjQueryTimepickr() . It currently will support all of the timepickr options passed in as widget options (on the constructor). Full documentation for timepickr is here.

Have fun picking time.

Use Greasemonkey to extract your Facebook Phonebook

Posted on:Friday, March 6th, 2009 by anonymous

7/19/2010 UPDATE: There is a BRAND NEW version of the script available on Userscripts here.

10/12/2009 UPDATE: Added fixes from Marcel Chastain

UPDATE: It looks like the version that got uploaded was missing a * in the trigger URL! That might be the issue everyone is having.


UPDATE: Video of the process: fbimport

Facebook’s API + FBConnect is great but it has some severe limitations. Notably, it doesn’t expose all the functionality available on the Facebook  site. Tonight in particular, I wanted to be able to copy a dump of my friends’ names and phone numbers off the site to load into a fresh cell phone. Unfortunately, looking at the API this isn’t possible.

Never fear – Greasemonkey provides enough of a hook into Firefox that it would be possible to write a UserScript to accomplish this.

Continuing beyond this point is probably against the Facebook TOS and will probably severely void your warranty.

You have been warned.

The following describes how to use this userscript to extract your Facebook “Phonebook”. It produces of a CSV of your friends’ names and phone numbers. Fair warning – this is a rough prototype and does almost no error handling. Also, since the “Phone” field is a free text field I can’t promise people will have formatted their numbers in any sane fashion. But either way it’s a good start to revering lost numbers.

So here is what you need to do to use the script:

1. Install Greasemonkey – https://addons.mozilla.org/en-US/firefox/addon/748

2. Follow these instructions to install the script – http://userscripts.org/about/installing

Edit: The script is also on Userscripts at http://userscripts.org/scripts/show/43681

3. Navigate over to http://m.facebook.com/friends.php? (You’ll have to login)

4. Answer yes to the prompt and sit back – the script will move through your phonebook and eventually dump you a CSV of the results.

5. Copy/Paste the CSV wherever you want.

6. Un-install the Greasemonkey script.

So that’s it, one less walled garden to worry about. And hopefully one less “I lost my cellphone!” event/group on facebook!

The script:

Facebook Phonebook Exporter

// ==UserScript==
// @name           fb phonebook
// @namespace      setfivefb
// @description    Scrapes your Facebook friend's phone #s and optionally email addresses. Login to http://m.facebook.com/ and navigate to http://m.facebook.com/friends.php?a&rf6e4c397&refid=5
// @include        http://m.facebook.com/*
// ==/UserScript==

var s = document.createElement('script');
s.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js';
s.type = 'text/javascript';

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

s.addEventListener('load', function(){
  jQuery = unsafeWindow['jQuery'];
  jQuery.noConflict();

  var queryString = unsafeWindow["window"].location.search;

  if( queryString.indexOf("sfScraper") > 0 ){

	  var localStorage = unsafeWindow['localStorage'];
	  var scrapeWhat = JSON.parse( localStorage.getItem("sfScrapeWhat") );

	  if( scrapeWhat.emailsDone ){
		  extractEmailandPhones();
		  return false;
	  }

	  if( scrapeWhat.phone ){
		  scrapePhoneContacts();
		  return false;
	  }

	  if( scrapeWhat.email ){
		  scrapeEmailContacts();
		  return false;
	  }

	  return false;
  }

  if( jQuery("b:contains('Everyone')").length ){

	  var localStorage = unsafeWindow['localStorage'];
	  localStorage.removeItem("sfStoredContacts");
	  localStorage.removeItem("sfScrapeWhat");
	  localStorage.removeItem("sfContactLinks");

	  jQuery("#title").after("
" + "What do you want to save?
Phonenumbers Phonenumbers and Emails" + "  
" + "
"); jQuery("#sf-scrapeBtn").click( function(){ var scrapeWhat = { phone: false, email: false, emailsDone: false }; jQuery("#sf-scraperFrm input:checked").each( function(){ if( jQuery(this).val() == "p" ){ scrapeWhat.phone = true; }else if( jQuery(this).val() == "e" ){ scrapeWhat.email = true; } }); localStorage.setItem("sfScrapeWhat", JSON.stringify(scrapeWhat)); if( scrapeWhat.phone ){ scrapePhoneContacts(); } if( scrapeWhat.email ){ scrapeEmailContacts(); } return false; }); }else{ jQuery("#marquee_tabs").after("
You need to switch to the Friends -> 'Everyone' tab for the scrapper to become active.
"); } }, false); function extractEmailandPhones(){ var contacts = localStorage.getItem("sfStoredContacts"); var contactLinks = JSON.parse( localStorage.getItem("sfContactLinks") ); if( contacts == null ){ contacts = [ ]; }else{ contacts = JSON.parse( contacts ); } var name = jQuery(".section_title:first").text(); var email = jQuery("td:contains('Email:')").next("td:first").text(); var phone = jQuery("td:contains('Mobile Number:')").next("td:first").text(); contacts.push( {"n": name, "p": phone, "e": email} ); localStorage.setItem("sfStoredContacts", JSON.stringify(contacts) ); jQuery(".section_title:first").after("
" + "Links left: " + contactLinks.length + "
"); if( contactLinks.length ){ unsafeWindow["window"].location = contactLinks.pop(); localStorage.setItem("sfContactLinks", JSON.stringify(contactLinks) ); return false; } var textArea = "Name,Phone,Email \n"; jQuery.each(contacts, function(i, val){ textArea += val.n + "," + val.p + "," + val.e + "\n"; }); jQuery("body").append(""); jQuery("#sf-fbContacts").val( textArea ); } function scrapeEmailContacts(){ var localStorage = unsafeWindow['localStorage']; var contactLinks = localStorage.getItem("sfContactLinks"); if( contactLinks == null ){ contactLinks = [ ]; }else{ contactLinks = JSON.parse( contacts ); } jQuery("tr[valign='top']").each( function(){ var url = "http://m.facebook.com" + jQuery(this).find("a:first").attr("href") + "&v=info&sfScraper=true"; contactLinks.push( url ); }); jQuery("#title").after("
" + "Saved: " + contactLinks.length + "
"); localStorage.setItem("sfContactLinks", JSON.stringify(contactLinks) ); if( jQuery("a:contains('Next')").length ){ var nextLink = jQuery("a:contains('Next')").attr("href") + "&sfScraper=true"; unsafeWindow["window"].location = "http://m.facebook.com" + nextLink; }else{ var scrapeWhat = JSON.parse( localStorage.getItem("sfScrapeWhat") ); scrapeWhat.emailsDone = true; localStorage.setItem("sfScrapeWhat", JSON.stringify(scrapeWhat) ); unsafeWindow["window"].location = contactLinks.pop(); localStorage.setItem("sfContactLinks", JSON.stringify(contactLinks) ); } return false; } function scrapePhoneContacts(){ var localStorage = unsafeWindow['localStorage']; var contacts = localStorage.getItem("sfStoredContacts"); if( contacts == null ){ contacts = [ ]; }else{ contacts = JSON.parse( contacts ); } jQuery("tr[valign='top']").each( function(){ var name = jQuery(this).find("a:first").text(); if( jQuery(this).find("a:contains('Call')").length ){ var number = jQuery(this).find("a:contains('Call')") .attr("href").replace("tel:", ""); contacts.push( {"n": name, "p": number, "e": ""} ); } }); jQuery("#title").after("
" + "Saved: " + contacts.length + "
"); localStorage.setItem("sfStoredContacts", JSON.stringify(contacts) ); if( jQuery("a:contains('Next')").length ){ var nextLink = jQuery("a:contains('Next')").attr("href") + "&sfScraper=true"; unsafeWindow["window"].location = "http://m.facebook.com" + nextLink; }else{ var textArea = "Name,Phone \n"; jQuery.each(contacts, function(i, val){ textArea += val.n + "," + val.p + "\n"; }); jQuery("body").append(""); jQuery("#sf-fbContacts").val( textArea ); } return false; }