May 25, 2013

Bookmarklet: Scrub through a Vine video

I was watching a friend's Vine video on the web, and I got the idea that it would be cool to control the playback of the video. I wrote this little bookmarklet to scrub through the video as you move your mouse over it. Here's the original JavaScript:
// grab video element and pause it
var vid = document.getElementById('post_html5_api'); 
vid.pause(); 
// get x offset of video
var vidX = 0; 
var el = vid;
while (el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
  vidX += el.offsetLeft;
  el = el.offsetParent;
}
// scrub the video based on mouse x
var vidTime = vid.seekable.end(0); 
vid.addEventListener('mousemove', function(e) {
  var x = e.clientX - vidX;
  var percent = x / vid.offsetWidth;
  vid.currentTime = percent * vidTime;
}, false);
And the bookmarklet (Vine Scrubber):
javascript:(function()%7Bvar%20vid=document.getElementById('post_html5_api');vid.pause();var%20vidX=0;var%20el=vid;while(el&&!isNaN(el.offsetLeft)&&!isNaN(el.offsetTop))%7BvidX+=el.offsetLeft;el=el.offsetParent;%7Dvar%20vidTime=vid.seekable.end(0);vid.addEventListener('mousemove',function(e)%7Bvar%20x=e.clientX-vidX;var%20percent=x/vid.offsetWidth;vid.currentTime=percent*vidTime;%7D,false)%7D)();

April 28, 2013

Bookmarklet: Select & invite all friends on Facebook

This may be an evil thing, as I hate getting unwanted invites and spam on Facebook... But if you're throwing an event or have created a Facebook "Page", you might want to invite a bunch of people. You probably don't want to have to click each person's name/picture to add them to the invite, so I wrote a little bookmarklet to select them all at once. Simply scroll down to the bottom of your list of friends (it will load more in as you scroll). Once your (no-longer) friends have all loaded, click the bookmarklet to check them all. Here's the original code:
var checks = document.getElementsByClassName('checkableListItem');
for(i=0; i<checks.length; i++){ $(checks[i]).click(); }
And the same code, reformatted for a bookmarklet:
javascript:(function()%7Bvar checks%3Ddocument.getElementsByClassName(%27checkableListItem%27)%3Bfor(i%3D0%3Bi<checks.length%3Bi%2B%2B)%7B%24(checks%5Bi%5D).click()%3B%7D%7D)()%3B
I shall pay for this with spam karma.

April 6, 2013

JavaScript: Use the goo.gl link shortener from your own site

Here's a quick, stripped-down version of a javascript implementation of the goo.gl link-shortener service. It asynchronously loads the Google client API, then uses another callback when the link shortener service is loaded. After the service loads, you can call shortenUrl() as many times as you'd like. For simplicity, I've only shortened one URL here. It doesn't appear that you need an API key to simply shorten URLs, but certain calls to this service would require one. Here's the basic version, which should work in modern browsers.
var shortenUrl = function() {
  var request = gapi.client.urlshortener.url.insert({
    resource: {
      longUrl: 'http://plasticsoundsupply.com'
    }
  });
  request.execute(function(response) {
    var shortUrl = response.id;
    console.log('short url:', shortUrl);
  });
};

var googleApiLoaded = function() {
  // gapi.client.setApiKey("YOUR API KEY")
  gapi.client.load("urlshortener", "v1", shortenUrl);
};

window.googleApiLoaded = googleApiLoaded;
$(document.body).append('<script src="https://apis.google.com/js/client.js?onload=googleApiLoaded"></script>');

March 2, 2013

JavaScript: Antialias post-processing with THREE.js on a (non) retina screen

When drawing a basic Mesh object in THREE.js, the edges can be particularly jagged if your browser doesn't properly support antialiasing in webGL (most don't seem to at the moment). In my current project this became a sticking point, and I set out to fix the aliased edges of my 3D models and Mesh objects.

I found the FXAA post-processing shader effect in the THREE.js library, and it worked like a charm to smooth the rough edges. However, the THREE.EffectComposer utility doesn't automatically handle different pixel densities, and by default, the aliasing actually became twice as bad on the Retina screen of my Mac. After some fiddling, I found that you simply have to adjust the uniforms for the shader effect if it depends on knowing your screen size, as well as set the screen size for the EffectsComposer object.

See below, where I detect the pixel density, and use that to multiply your screen dimensions in the shader and EffectComposer:
var composer, dpr, effectFXAA, renderScene;

dpr = 1;
if (window.devicePixelRatio !== undefined) {
  dpr = window.devicePixelRatio;
}

renderScene = new THREE.RenderPass(scene, camera);
effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
effectFXAA.uniforms['resolution'].value.set(1 / (window.innerWidth * dpr), 1 / (window.innerHeight * dpr));
effectFXAA.renderToScreen = true;

composer = new THREE.EffectComposer(renderer);
composer.setSize(window.innerWidth * dpr, window.innerHeight * dpr);
composer.addPass(renderScene);
composer.addPass(effectFXAA);
You'll also probably want to update these settings if the window size changes, like so:
$(window).on('resize', onWindowResize);

function onWindowResize(e) {
  effectFXAA.uniforms['resolution'].value.set(1 / (window.innerWidth * dpr), 1 / (window.innerHeight * dpr));
  composer.setSize(window.innerWidth * dpr, window.innerHeight * dpr);
}

February 5, 2013

Base64 encode an image from the command line in OS X

I frequently use base64 encoding to include small images inline in my CSS. This helps me avoid loading lots of small images or managing an image sprite. Luckily, there's a super easy native command line tool in Mac OS X to do this. Use it like so:
openssl base64 -A -in your-image.png
If your image file is valid, your Terminal will generate a base64 string. You can then drop this into your CSS as a background-image or as the src of an img tag. You simply need to prepend the base64 string with the following header string:
data:image/png;base64,
In the 2 cases, your code would look something like this:
/* CSS */
#container {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAbCAYAAABr/T8RAAAB8qpOejw03OsRMxMR8RDujjC14YwEWEg/bF/6glXHxYm2JTCa4xRxoT/gW5/67s0Hu88AAAAABJRU5ErkJggg==")
}
<!-- HTML -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAbCAYAAABr/T8RAAAB8qpOejw03OsRMxMR8RDujjC14YwEWEg/bF/6glXHxYm2JTCa4xRxoT/gW5/67s0Hu88AAAAABJRU5ErkJggg==" />

October 24, 2012

JavaScript: Letterbox, pillowbox or crop an image to any container size

Very often, I've run across the situation of having images of an unspecified size that need to fit into a container of an unspecified size, either letterboxed, or cropped to fill the container. This is easily accomplished by scaling the image, positioning it within the container, and masking the container's contents. With the following methods, it's easy to crop or letterbox/pillowbox an image for your particular use case.

Here's a method that will give you the size and position of your image, based on its relation to the container's size. The method receives the container's dimensions, the image's original size, and a boolean to specify cropping or letterboxing. The method returns an array with the coordinates and dimensions to reposition and resize your image. If you don't know the size of your images beforehand, you can use this method to find out before using the following code.
var ImageUtil = ImageUtil || {};

ImageUtil.getOffsetAndSizeToCrop = function( containerW, containerH, imageW, imageH, cropFill ) {
  var ratioW = containerW / imageW;
  var ratioH = containerH / imageH;
  var shorterRatio = ratioW > ratioH ? ratioH : ratioW;
  var longerRatio = ratioW > ratioH ? ratioW : ratioH;
  var resizedW = cropFill ? Math.ceil(imageW * longerRatio) : Math.ceil(imageW * shorterRatio);
  var resizedH = cropFill ? Math.ceil(imageH * longerRatio) : Math.ceil(imageH * shorterRatio);
  var offsetX = Math.ceil((containerW - resizedW) * 0.5);
  var offsetY = Math.ceil((containerH - resizedH) * 0.5);
  return [offsetX, offsetY, resizedW, resizedH];
};

To wrap up the cropping functionality, you can use the following method to apply the css styles to your image element based on its size, the container's size, and the type of image resizing you'd like. I've included special cases to anchor the image to top or bottom of the container rather than vertically centering it, in case the images' content requires that type of positioning. Note that the method requires a raw html element reference for the image and container - this doesn't require jQuery or any such library.
ImageUtil.CROP = 'CROP';
ImageUtil.CROP_TOP = 'CROP_TOP';
ImageUtil.CROP_BOTTOM = 'CROP_BOTTOM';
ImageUtil.LETTERBOX = 'LETTERBOX';

ImageUtil.cropImage = function( containerEl, containerW, containerH, imageEl, imageW, imageH, cropType ) {
  var cropFill = ( cropType == ImageUtil.CROP || cropType == ImageUtil.CROP_TOP || cropType == ImageUtil.CROP_BOTTOM );
  var offsetAndSize = ImageUtil.getOffsetAndSizeToCrop(containerW, containerH, imageW, imageH, cropFill);

  // set outer container size
  containerEl.style.width = containerW+'px';
  containerEl.style.height = containerH+'px';

  // resize image
  imageEl.width = offsetAndSize[2];
  imageEl.height = offsetAndSize[3];
  imageEl.style.width = offsetAndSize[2]+'px';
  imageEl.style.height = offsetAndSize[3]+'px';

  // position image
  imageEl.style.left = offsetAndSize[0]+'px';
  imageEl.style.top = offsetAndSize[1]+'px';

  // special y-positioning 
  if( cropType == utensils.ImageUtil.CROP_TOP ) {
    imageEl.style.top = '0px';
    imageEl.style.bottom = '';
  } else if( cropType == utensils.ImageUtil.CROP_BOTTOM ) {
    imageEl.style.top = '';
    imageEl.style.bottom = '0px';
  }
};
The only default styles you'd need on your container and image are as follows:
.cropped-image {
  position: absolute;
}
.container {
  position: relative;
  overflow: hidden;
}

September 11, 2012

Obj-C: Center a view horizontally with autoresizingMask properties

Cocoa's UIView layout system gives the developer some nice tools to automatically resize and reposition views inside of each other. This is usually done in the nib/xib/storyboard editor with the Origin/Autoresizing View property inspectors, and honestly it's never made a ton of sense to me. I'm only an occasional obj-c dev, so bear with me. I wanted to horizontally center and fill some UIView elements to fit the width of different devices and orientations, and I knew there should be a simple way to accomplish this. I came up with several methods to help apply these settings to any number of outer containers that should fill the width of the parent view, and inner containers that should be centered within:
- (void)setViewCentered:(UIView*)view
{
    view.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin);
}

- (void)setContainerToParentWidth:(UIView*)view
{
    view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
}

- (int)getHorizCenterFromView:(UIView*)view
{
    return (self.view.bounds.size.width - view.frame.size.width)/2;
}

- (int)getHorizCenterFromInt:(int)width
{
    return (self.view.bounds.size.width - width)/2;
}
The key to centering a fixed-width view - this was the confusing part for me - is setting the x-position of the frame to 1/2 of the width of the parent container. The associated example call to center a view is here. From my researching, it seems that the autoresizingMask property should be changed after addSubview
    int viewW = 320;
    _controlsContainer = [[UIView alloc] initWithFrame:CGRectMake([self getHorizCenterFromInt:viewW], 0, viewW, controlsH)];
    [self.view addSubview:_controlsContainer];
    [self setViewCentered:_controlsContainer];
Note that self.view is the outer container that we're centering inside of. Then if you simply want to fill a container to the width of the parent, use something like this:
    _header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
    [self.view addSubview:_header];
    [self setContainerToParentWidth:_header];
Happy centering without having to change a UIView's frame!