Hack together a jQuery Modal Popup in Salesforce

Recently I had a use case where I needed a jQuery popup alert to display on a standard Opportunity detail page when the page is loaded by the user. There are a number of solutions that use a VisualForce page element to hold the jQuery code and a custom button or link to launch the JavaScript, but all of them display the popup when the button is clicked. When the JavaScript attempts to launch the popup alert on page load like I wanted (instead of a button click) it gets trapped inside the VisualForce element instead of being displayed over the whole page.

As frustrating as it is, this behavior is by design and is there to prevent cross site scripting attacks. Salesforce serves the VisualForce pages from a different domain than the rest of the site so the browser does not allow any JavaScript contained in that VisualForce page element to act on the page that it's embedded in.

Another possible solution was to use a sidebar component to hold the popup code, but since I needed it to display over the Opportunity page only, and it had to work when the user was using the old Agent Console, the sidebar component wouldn't work.

Thanks to this solution on StackExchange I was able to hack this one to work the way I wanted. It relies on the ability to add a {!REQUIRESCRIPT()} function inside a custom button or link. The JavaScript referenced in the REQUIRESCRIPT function will be executed as the page loads so it's not dependant on the user clicking the button.

To use this hack, create a custom detail page button and choose 'Execute JavaScript' for Behavior and 'OnClick JavaScript' for Content Source. In the formula editor for your new button we first need to load some jQuery resources with the following REQUIRESCRIPT lines.
{!REQUIRESCRIPT("//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js")}
{!REQUIRESCRIPT("//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js")}
The next thing we need to add is some jQuery CSS to make our new model popup pretty. We can inject CSS into our standard layout using the following segment of code.
var csslink = "https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css";
var newStyleSheet = document.createElement('link');
newStyleSheet.rel='stylesheet';
newStyleSheet.type='text/css';
newStyleSheet.href=styles;
document.getElementsByTagName("head")[0].appendChild(newStyleSheet);
To embed this script into our custom button we need to use a Base64 encoder like this one to convert the code into a Data URI. We insert that into a third REQUIRESCRIPT function with the data:application/javascript;base64, prefix.
{!REQUIRESCRIPT("data:application/javascript;base64,dmFyIGNzc0xpbmsgPSAiaHR0cHM6Ly9jb2RlLmpxdWVyeS5jb20vdWkvMS4xMC40L3RoZW1lcy91aS1saWdodG5lc3MvanF1ZXJ5LXVpLmNzcyI7CnZhciBuZXdTdHlsZVNoZWV0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnbGluaycpOwpuZXdTdHlsZVNoZWV0LnJlbD0nc3R5bGVzaGVldCc7Cm5ld1N0eWxlU2hlZXQudHlwZT0ndGV4dC9jc3MnOwpuZXdTdHlsZVNoZWV0LmhyZWY9Y3NzTGluazsKZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoImhlYWQiKVswXS5hcHBlbmRDaGlsZChuZXdTdHlsZVNoZWV0KTs=")}
Now that we have our resources loaded, we need to launch the popup itself. Here's the basic jQuery code we want to execute when the page finishes loading.
var j$ = jQuery.noConflict();
var j$modalDialog = j$('<div></div>')
     .html('test message goes here')
     .dialog({
          autoOpen: false,
          title: 'My Modal Dialog',
          resizable: false,
          width: 400,
          height: 400,
          autoResize: true,
          modal: true,
          draggable: true
      });
j$(document).ready(function(){
    j$modalDialog.dialog('open');
});
Again, we'll encode this with the Base64 encoder and add it to a fourth REQUIRESCRIPT function.

{!REQUIRESCRIPT("data:application/javascript;base64,dmFyIGokID0galF1ZXJ5Lm5vQ29uZmxpY3QoKTsKdmFyIGokbW9kYWxEaWFsb2cgPSBqJCgnPGRpdj48L2Rpdj4nKQogICAgIC5odG1sKCd0ZXN0IG1lc3NhZ2UgZ29lcyBoZXJlJykKICAgICAuZGlhbG9nKHsKICAgICAgICAgIGF1dG9PcGVuOiBmYWxzZSwKICAgICAgICAgIHRpdGxlOiAnTXkgTW9kYWwgRGlhbG9nJywKICAgICAgICAgIHJlc2l6YWJsZTogZmFsc2UsCiAgICAgICAgICB3aWR0aDogNDAwLAogICAgICAgICAgaGVpZ2h0OiA0MDAsCiAgICAgICAgICBhdXRvUmVzaXplOiB0cnVlLAogICAgICAgICAgbW9kYWw6IHRydWUsCiAgICAgICAgICBkcmFnZ2FibGU6IHRydWUKICAgICAgfSk7CmokKGRvY3VtZW50KS5yZWFkeShmdW5jdGlvbigpewogICAgaiRtb2RhbERpYWxvZy5kaWFsb2coJ29wZW4nKTsKfSk7")}

When it's all said and done, your formula editor window should look something like this.

Add the new button to your Opportunity layout and when you open an Opportunity record you get a modal popup.

And it also works when viewing an Opportunity record in the Agent Console.

Create a Command Line Speed Tester

I love little command line tricks like this...  This comes from an OSX Daily article here.

Create an alias to give yourself a quick way to launch an internet speed testing tool using wget.

Add the following line to your .bash_profile and now you just need to type 'speedtest' to launch the test.
alias speedtest='wget -O /dev/null http://speedtest.wdc01.softlayer.com/downloads/test10.zip'
If you don't have wget on your mac yet, then simply install homebrew by running...
ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"
After Homebrew is installed you can install wget using this command...
brew install wget

Display New Episodes of Your Favorite Shows on Your Desktop (It's a GeekTool Geeklet)

With the fall TV season about to start I wanted a better way to keep track of when new episodes of my favorite shows are airing. Using GeekTool, this little script displays the series and title of new episodes airing today on my desktop.

If you're new to GeekTool, I suggest reading this tutorial.  It will make everything here much clearer.

This script uses thetvdb.com's new shows rss feed and compares it to the api feed of shows you've selected as favorites on the site.  You can find your account id at the bottom of your account settings on thetvdb.com.  Of course you'll need to register if you don't already have an account, but it's a free site.

It uses the feedparser python lib to handle the rss. You can download that here. (very easy to install)

thetvdb.com new shows list is only updated once a day, so please make sure to set your refresh rate to once a day so it doesn't cause too much traffic for the site. They are an underfunded project so it's important to avoid unnecessary hits.

Screenshot:

Once you have feedparser installed, just drag a shell module from GeekTool to the desktop and paste this script into the command field.

#!/usr/bin/env python
import feedparser, urllib2
from xml.etree import ElementTree as ET
# =========== CONFIG =============
# Insert your account id here. You can find it at 
# the bottom of your account settings page on thetvdb.com
ACCOUNTID = 'C2920FF6804FD948'
# ================================

new_shows = feedparser.parse('http://thetvdb.com/rss/newtoday.php')
favorites_feed = urllib2.urlopen('http://thetvdb.com/api/User_Favorites.php?accountid='+ACCOUNTID)
favorites = ET.XML(favorites_feed.read())
shows_list = []

for new_show in new_shows.entries:
 new_show_series = new_show.links[0].href
 new_show_series_id = new_show_series[new_show_series.find("seriesid=")+9:new_show_series.find("&seasonid")]
 for favorite in favorites:
  if(favorite.text == new_show_series_id):
   shows_list.append(new_show.title)

print "New episodes of favorite shows airing today:"   
if (len(shows_list)>0):
 for shows in shows_list:
  print "* "+shows
else:
 print "* No new shows today"


Leave a comment or post a screenshot of it on your desktop.

Cheers!

Pie Chart Clock Geeklet for Geektool

Here is another fun clock Geeklet for you to use with GeekTool on the Mac.  This one displays three pie chart layers that gives you sort of an abstract looking clock

Here's how it looks on my desktop:



The script creates the three layers separately so you can position them any way you like.  In my example I stacked the images on top of each other with decreasing levels of transparency.

This Geeklet uses ImageMagick so make sure you have that installed before trying to set this up.  If you're not sure how, just install MacPorts and then type this in terminal

sudo port install imagemagick

Once you have imagemagick ready to go, drag a shell module from the GeekTools preference pane to the desktop and past this script into the command field

#!/usr/bin/env python

# File pie_chart_clock.py
# Created 9/5/2011
# Scott McClung
# Generates pie chart images based on the current system time
# Pie chart calculations the build_slices function were taken from piechart.py 
# written by Gregory Pittman. He has a detailed explanation of drawing .svg pie
# charts at http://wiki.scribus.net/canvas/Making_a_Pie_Chart
##########################################
from __future__ import division

import os,time,math

# ============= CONFIG ==============
# location of ImageMagick
converter='/opt/local/bin/convert'
# this is the filename/path that will be used for the resulting images
hours_img_file='$HOME/pie_chart_clock_hrs.png'
minutes_img_file='$HOME/pie_chart_clock_mins.png'
seconds_img_file='$HOME/pie_chart_clock_secs.png'
# you can change these values to determine the size of the hour
# and minute pie charts
hours_radius=240
minutes_radius=160
seconds_radius=80
# colors for the time slice of each chart
hours_color='white'
minutes_color='white'
seconds_color='white'
# color for the background
# enter "none" for a transparent background
hours_background='none'
minutes_background='none'
seconds_background='none'
# add shadow under the pie charts by entering a value for the shadow depth
# for no shadow set the depth value to 0
hours_shadow_depth = 10
minutes_shadow_depth = 4
seconds_shadow_depth = 4

# ===================================

# populate the current time values
hrs=int(time.strftime("%I"))
mins=int(time.strftime("%M"))
secs=int(time.strftime("%S"))
# the two slices of each pie are the current time slice and the
# slice representing the time left to complete the circle
hrs_slices = [hrs,(12-hrs)]
mins_slices = [mins,(60-mins)]
secs_slices = [secs,(60-secs)]
# There are always two slices of the pies and we'll display one or the other based
# the time of day.  We'll put both of them in a list for now.
hrs_path = []
mins_path = []
secs_path = []

def build_slices(radius, slices, parity):
 path = []
 total = 0
 i = 0
 seg = 0
 startx = radius + 20  # The screen x-origin: center of pie chart (a little padding is added to radius)
 starty = radius + 20  # The screen y-origin: center of pie chart (a little padding is added to radius)
 lastx = radius    # Starting coordinates of 
 lasty = 0     # the first arc

 for n in slices:
  total = total + n  # we have to do this ahead, since we need the total for the next for loop

 for n in slices:
  arc = "0"                   # default is to draw short arc (< 180 degrees)
  seg = n/total * 360 + seg   # this angle will be current plus all previous
  if ((n/total * 360) > 180): # just in case this piece is > 180 degrees
   arc = "1"
  radseg = math.radians(seg)  # we need to convert to radians for cosine, sine functions
  nextx = math.cos(radseg) * radius
  nexty = (math.sin(radseg) * radius)
  if (n == 0 and not parity):
   arc = "1"
   nexty -= 1
  # The weirdly placed minus signs [eg, (-(lasty))] are due to the fact that
  # our calculations are for a graph with positive Y values going up, but on the
  # screen positive Y values go down.
  path.append("path 'M "+str(startx)+","+str(starty) + " l "+str(lastx)+","+str(-(lasty))+" a" + str(radius) + "," + str(radius) + " 0 " + arc + ",0 "+str(nextx - lastx)+","+str(-(nexty - lasty))+ " z'")
  
  # We are writing the XML commands one segment at a time, so we abandon old points
  # we don't need anymore, and nextx becomes lastx for the next segment
  lastx = nextx
  lasty = nexty
  i += 1

 return path

def create_shadow(depth):
 if (depth>0):
  shadow = " \( +clone -background black -shadow 60x"+str(depth)+"+"+str(depth)+"+"+str(depth)+" \) +swap -background none -mosaic "
 else:
  shadow = ""
 return shadow

def select_path(parity,time,arr_path):
 if(parity):
  return arr_path[0]
 elif(time == 0):
  return arr_path[0]
 else:
  return arr_path[1]

# build hours pie
parity = bool(time.strftime("%p")=="AM")
hrs_path = build_slices(hours_radius,hrs_slices,parity)
hrs_img_size = str((hours_radius + 20) * 2)
hrs_path_command = select_path(parity,hrs,hrs_path)

# build minutes pie
parity = not bool(hrs & 1)
mins_path = build_slices(minutes_radius,mins_slices, parity)
mins_img_size = str((minutes_radius + 20) * 2)
mins_path_command = select_path(parity,mins,mins_path)

#build seconds pie
parity = not bool(mins & 1)
secs_path = build_slices(seconds_radius,secs_slices, parity)
secs_img_size = str((seconds_radius + 20) * 2)
secs_path_command = select_path(parity,secs,secs_path)


# Draw the images using ImageMagick
# the nature of the pie chart math results in the path drawing the pie charts
# counter clockwise and starting from the 15 minute mark.  The simplest solution
# is to just have ImageMagik rotate and flop the image after it's drawn.
command_string = converter
command_string += " -size "+hrs_img_size+"x"+hrs_img_size+" xc:"+hours_background+" -fill "+hours_color+" -stroke none -draw \""+hrs_path_command+"\" -rotate \"-90\" -flop "+ create_shadow(hours_shadow_depth) + hours_img_file
os.system(command_string)

command_string = converter
command_string += " -size "+mins_img_size+"x"+mins_img_size+" xc:"+minutes_background+" -fill "+minutes_color+" -stroke none -draw \""+mins_path_command+"\" -rotate \"-90\" -flop " + create_shadow(minutes_shadow_depth) + minutes_img_file
os.system(command_string)

command_string = converter
command_string += " -size "+secs_img_size+"x"+secs_img_size+" xc:"+seconds_background+" -fill "+seconds_color+" -stroke none -draw \""+secs_path_command+"\" -rotate \"-90\" -flop " + create_shadow(seconds_shadow_depth) + seconds_img_file
os.system(command_string)

Set it to update as frequently as you like.  I don't recommend setting to update too often if you're using a laptop as it will shorten your battery life a bit.  I find running it every 5 or 10 seconds still has the visual effect I want but doesn't chew up too many cycles.

Then drag 3 image modules to the desktop and point one to each of the three images created in your home directory.
  • pie_chart_clock_hrs.png
  • pie_chart_clock_mins.png
  • pie_chart_clock_secs.png

Set them to update on the same schedule as your shell module and then position and size to taste.

You can change the file names and location, pie radius, pie slice color, background color, and shadow depth by updating the values in the CONFIG section of the script if you'd like a different look and feel.

Thanks to Gregory Pittman and his demo on generating SVG pie charts with python.  You can see his work here: http://wiki.scribus.net/canvas/Making_a_Pie_Chart  Could not have figured this out without that tutorial.

If you installed ImageMagick using a different method than MacPorts, you will probably need to update the CONFIG section to point to the correct installation location for your system.

Feel free to leave some feedback or post a screenshot of your GeekTool setup!

Cheers!
Scott



Binary Clock Geeklet for GeekTool

I've always been fascinated by the binary clock so I made a desktop version that works with GeekTool.  If you haven't played with GeekTool for Mac yet, check it out here.

Edit: One of the downsides to GeekTool is the lack of documentation.  If you're just getting started with it, I just came across this great tutorial to help get you going.

Here is a screen shot of the final product



This geeklet requires ImageMagick so make sure you have that installed first.  If you're not sure how, start by installing MacPorts and then enter this command in terminal.

sudo port install imagemagick

Once you have ImageMagick installed, you need two image files to represent the on and off state of the lights.  These are the two images I'm using in the screen shot above:
Make sure that you save them as .png so the transparency is preserved.  By default the script will look for them in your home directory so just put them there for now.  It really doesn't matter what two images you use, just make sure that both files have the same outer dimensions so the columns line up correctly.  You can change the image location and the image filenames in the CONFIG section of the script.

Now drag a shell script module from GeekTool to the desktop and paste this script in to the command field.

#!/bin/bash
# ======= CONFIG ============================================
# set this to the location of ImageMagick's convert command
    CONVERTER="/opt/local/bin/convert"
# set this to the location where your Image geeklet will look
    OUTFILE="$HOME/binary_clock_img.png"
# set these to the image files you're using for on and off
# do not use '0' or '1' in your image filenames or it will cause confusion in the script below.
    SWITCH_ON="$HOME/red_sphere_on.png"
    SWITCH_OFF="$HOME/red_sphere_off.png"
# set this to the binary reference you want to use for each number. (0-9)
# the image is designed to reflect time from the bottom up, but
# since the column stack is built from the top down, these numbers may
# appear to be backwards.
    BINARY=("0 0 0 0" "0 0 0 1" "0 0 1 0" "0 0 1 1" "0 1 0 0" "0 1 0 1" "0 1 1 0" "0 1 1 1" "1 0 0 0" "1 0 0 1")
# ===========================================================

hr=$(date '+%I')
min=$(date '+%M')
sec=$(date '+%S')
digit[1]=${hr:0:1}
digit[2]=${hr:1:1}
digit[3]=${min:0:1}
digit[4]=${min:1:1}
digit[5]=${sec:0:1}
digit[6]=${sec:1:1}

for index in 1 2 3 4 5 6
do
    pos=${digit[index]}
    col[index]=${BINARY[pos]}
    col[index]=${col[index]//'0'/$SWITCH_OFF}
    col[index]=${col[index]//'1'/$SWITCH_ON}
done

$CONVERTER     \( ${col[1]} -append \) \
            \( ${col[2]} -append \) \
            \( ${col[3]} -append \) \
            \( ${col[4]} -append \) \
            \( ${col[5]} -append \) \
            \( ${col[6]} -append \) \
            +append $OUTFILE


Set the shell Geeklet to refresh every 5 seconds or so.  You can have it update more frequently, just be aware of system resources.

Now drag an image module from GeekTool to the desktop and point the url field to the binary_clock_img.png file that should now appear in your home directory.  Set it to update at the same frequency as the shell Geeklet (5 seconds) and then size and position it to taste.

If you find that the binary_clock_img.png file is not being created, check that the CONVERTER file location in the script is set correctly for your ImageMagick installation.  By default, MacPorts will put it in /opt/local/bin/ but it may be in a different spot if you used some other method to install. (e.g. /usr/local/bin/)

Please leave constructive feedback below

Cheers!
Scott