Chomper Stomping jQuery/JavaScript/CSS 3/HTML 5, Java/PHP/Python/ActionScript, Git, Chrome/Firefox Extensions, Wordpress/Game/iPhone App Development and other random techie tidbits I've collected

24Sep/120

Super Simple CSS3 Only Bookmark Ribbons

Screen Shot 2012-09-24 at 1.31.36 PM

Here are some extremely easy basic CSS 3 only triangular-bottomed bookmark split-ribbon things.

This is accomplished through some creative use of the "border" attribute on divs in CSS. It takes a main div (where the body of the ribbon is) and then two smaller divs that create the actual split ribbon effect.

You can change their color, or even assign a background image for some truly ribbon like effects.

You could probably even throw some nice shadows on it and make it look even better, but, this is just a very simple basic version.

You may have to scroll down past the share bar to see the embedded jsfiddle...

I whipped these up today for something I was working on and realized it was worth sharing with the world.

Thanks to CSS-Tricks.com for the basis of this effect (css triangles).

26Apr/120

dynode Batch Get Item

logo

Working a lot with node.js, dynode and dynamoDB recently. Still trying to wrap my head around it all. Had a horrible time getting dynode.batchGetItem to work. Here is the error I was getting:

{ name: 'AmazonError',
type: 'ValidationException',
serviceName: 'com.amazon.coral.validate',
message: 'One or more parameter values were invalid: Mismatching attribute types between location and schema',
statusCode: 400,
retry: [Getter] }

Basically it came down to that the range selector I was using was being passed through as a string instead of a number. This is *even though* I am chaining queries, having just gotten the range selector out of another table and am immediately using it in the query that is failing. The way I was able to fix it was by casting it from a string into a number (the way Amazon expects it to come across):

results.Items.forEach(function(element, index, array){
    console.log(element);

    element.events.SS.forEach(function(ielement, iindex, iarray){
        var batchVars = {"events": {keys:[ {hash: ielement, range: parseInt(element.timestamp.N)} ]}};
        dynode.batchGetItem(batchVars, events.debugOutput);
    }, query);
}, query);

The crucial part here being this line:
{keys:[ {hash: ielement, range: parseInt(element.timestamp.N)} ]}

Note the "parseInt" function there that gets run on the timestamp that was pulled out of the previous query results. That's the key to getting this to work.

UPDATE:
The dynode author asked me to submit an issue, so, I did!

https://github.com/Wantworthy/dynode/issues/18

I had just assumed this was a quirk of the library. I feel way too new to all of this to know what is a bug and what is just me doing something stupidly...

14Dec/115

Events Calendar Pro Nav Formatting Messed up on Empty Calendar

The Events Calendar Pro (from http://tri.be/) has a few problems.

If you are trying to figure out why a calendar with no events in that month has completely screwed up header navigation, just put this line of code inside of table.php in the top of the display_day function (put it directly before the for loop):

$thisisherebecausethispluginSUCKSdontremoveit = has_excerpt() ? TribeEvents::truncate($post->post_excerpt) : TribeEvents::truncate(get_the_content(), 30);

This basically just runs some "truncate" function on the "TribeEvents" object. For some reason this truncate function magically fixes the header nav display issues.

23Nov/110

WordPress Settings API – Adding Options to Existing Page

blue-xl

Adding new options to an existing page in the dashboard in wordpress can be maddening. I've literally spent 15+ hours dealing with this horrible API at this point. To the point where I wrote two different wrappers for it.

Here are some notes chiseled along the way for any poor soul following me down this obtuse path.

Today I'll focus on adding options to an existing page. Hopefully soon I'll do one on making a new page.

Checklist/Overview:
1. Hook into the admin init action (add_action('admin_init'))
2. Create your section (add_settings_section)
3. Output hidden fields so settings will save (settings_fields)
4. Create your fields (add_settings_field and register_setting)

Your options will be available through the usual "get_option" means...

Step 1) Hook into the admin init action:

Step 2) Create your init function (that you just hooked into the admin_init action):

Step 3) Create your own section register API

Step 4) Create your section callback functions

It is really annoying that the API doesn't allow you to pass params to this callback function, which means you have to define each one individually...

Step 5) Create your own field register API

Step 6) Create your global callback function

Step 7) Register your section(s)

This goes in that init function you made in step 2

Step 8) Register your field(s)

This goes in that init function you made in step 2

Here's an example of a completed version of all this. This one adds a crap ton of custom thumbnail size options to the media panel. It is a really bad example as it is overly complex:

25May/110

Using two different identity files with ssh for rsa remote authentication keys

sshlogo

I have two different servers I need to connect to, each requiring two different types of remote authentication keys. One requires rsa, the other dss. So I had to make and use two different remote authentication keys, but was unsure as to how to tell my machine to serve them both up. It was, by default, just serving up the rsa key.

What I had to do was create a file called "config" (NO file extension) in the ~/.ssh directory on my machine. I then put two lines in this file:

IdentityFile ~/.ssh/id_rsa
IdentityFile ~/.ssh/id_dss

It works like a charm.

For the curious, I'm on windows using the git bash that comes built in with git (NOT cygwin). My ~/.ssh directory looks like this:

I generated these RSA keys with a command similar to this:

ssh-keygen -t dss -f ~/.ssh/id_dss

And copied and pasted the contents of the id_rsa.pub (and id_dss.pub) files into the appropriate place (something like ~/.ssh/authorized_keys) on the remote servers.

24May/115

Creating ATG Droplets and serving a default oparam

java-logo

Creating your own ATG droplets is not difficult. Servicing a default open parameter (oparam) in an ATG droplet is surprisingly extremely easy.

ATG has these things called "droplets" that you use from within your "dsp" tag library. You can --nay-- you SHOULD make your own droplets. You should have as little Java code in your JSP pages as humanly possible.

To do this takes a few steps:

  1. Create the .java file

    Create it under /modules/base/src/java/com/yourlibrary/

  2. Extend "DynamoServlet"

    The inside of your Java file should look something like this (to begin):

    package com.yourlibrary;

    import java.io.IOException;
    import javax.servlet.ServletException;
    import atg.nucleus.naming.ParameterName;
    import atg.repository.*;
    import atg.servlet.*;

    public class YourDroplet extends DynamoServlet{
    @Override
    public void service(DynamoHttpServletRequest request, DynamoHttpServletResponse response)
    throws ServletException, IOException
    {
    }
    }

  3. Create the .properties file

    Create it under /modules/base/config/yourlibrary/

  4. Define your properties


    $class=com.yourlibrary.YourDroplet
    $scope=global
    $description=For documentation purposes only

    global scope makes it so there is only 1 instance of the droplet per JVM. You can do session or request scope as well. This is all server admin magic voodoo. I can give you no recommendations here, that's a topic for it's own entire post

  5. Implement the droplet

    Create a jsp page somehwere (probably something like /modules/base/j2ee-apps/base/web-app.war) if you haven't already, and then add this code:


    <%@ taglib uri="/dspTaglib" prefix="dsp"%>
    <dsp:importbean bean="/yourlibrary/YourDroplet" />
    <dsp:page>
    <dsp:droplet name="YourDroplet">
    </dsp:droplet>
    </dsp:page>

OK, at this point you have the skeleton for your droplet created. Go ahead and burn incense, sacrafice a chicken and compile and run and see if it worked. If not, don't ask for help here, I'm just a poor Java hating front-end dev trying to document an insane procress. Go to StackOverflow.com instead.

Now, let's make it serve a purpose. Let's say you just want, like, the user name or something:

Your .java file


package com.yourlibrary;

import java.io.IOException;
import javax.servlet.ServletException;
import atg.nucleus.naming.ParameterName;
import atg.repository.*;
import atg.servlet.*;

public class YourDroplet extends DynamoServlet{
@Override
public void service(DynamoHttpServletRequest request, DynamoHttpServletResponse response)
throws ServletException, IOException
{
String username = null;

username = "do whatever you have to do to get the username and put that here";

request.setParameter("username", username);
request.serviceLocalParameter("ousername", request, response);
}
}

Your .jsp file


<%@ taglib uri="/dspTaglib" prefix="dsp"%>
<dsp:importbean bean="/yourlibrary/YourDroplet" />
<dsp:page>
<dsp:droplet name="YourDroplet">
<dsp:oparam name="ousername">
<dsp:valueof param="username" />
</dsp:oparam>
</dsp:droplet>
</dsp:page>

Now, let's say that some of your users are admins, and in some places you want to have a special thing that happens when the user is an admin, but in others, just the normal thing happens no matter what. You can use a default for this. Here's how:

Your .java file


package com.yourlibrary;

import java.io.IOException;
import javax.servlet.ServletException;
import atg.nucleus.naming.ParameterName;
import atg.repository.*;
import atg.servlet.*;

public class YourDroplet extends DynamoServlet{
@Override
public void service(DynamoHttpServletRequest request, DynamoHttpServletResponse response)
throws ServletException, IOException
{
String username = null;
boolean handled = false;
boolean isadmin = false;

username = "/*do whatever you have to do to get the username and put that here*/";
isadmin = /*find out if they are admin and set this boolean]*/;

request.setParameter("username", username);
if(isadmin){
handled = request.serviceLocalParameter("oadmin", request, response);
}

if(!handled){
request.serviceLocalParameter("ouser", request, response);
}
}
}

Your .jsp file


<%@ taglib uri="/dspTaglib" prefix="dsp"%>
<dsp:importbean bean="/yourlibrary/YourDroplet" />
<dsp:page>
<!--Special case for admin-->
<dsp:droplet name="YourDroplet">
<dsp:oparam name="oadmin">
Admin <dsp:valueof param="username" />
</dsp:oparam>
<dsp:oparam name="ouser">
<dsp:valueof param="username" />
</dsp:oparam>
</dsp:droplet>

<!--No special case for admin, admin falls back on ouser-->
<dsp:droplet name="YourDroplet">
<dsp:oparam name="ouser">
<dsp:valueof param="username" />
</dsp:oparam>
</dsp:droplet>
</dsp:page>

You can take this to any level you want. Here is a more complex basic example:

Your .java file


package com.yourlibrary;

import java.io.IOException;
import javax.servlet.ServletException;
import atg.nucleus.naming.ParameterName;
import atg.repository.*;
import atg.servlet.*;

public class YourDroplet extends DynamoServlet{
@Override
public void service(DynamoHttpServletRequest request, DynamoHttpServletResponse response)
throws ServletException, IOException
{
String username = null;
boolean handled = false;
boolean isadmin = false;
boolean ismoderator = false;

username = "/*do whatever you have to do to get the username and put that here*/";
isadmin = /*find out if they are admin and set this boolean]*/;
ismoderator = /*find out if they are mod and set this boolean]*/;

request.setParameter("username", username);
if(isadmin){
handled = request.serviceLocalParameter("oadmin", request, response);
}elseif(ismoderator){
handled = request.serviceLocalParameter("omoderator", request, response);
}else{
handled = request.serviceLocalParameter("ouser", request, response);
}

if(!handled){
request.serviceLocalParameter("default", request, response);
}
}
}

Your .jsp file


<%@ taglib uri="/dspTaglib" prefix="dsp"%>
<dsp:importbean bean="/yourlibrary/YourDroplet" />
<dsp:page>
<!--Each special case is used-->
<dsp:droplet name="YourDroplet">
<dsp:oparam name="oadmin">
Admin <dsp:valueof param="username" />
</dsp:oparam>
<dsp:oparam name="omoderator">
Moderator <dsp:valueof param="username" />
</dsp:oparam>
<dsp:oparam name="ouser">
User <dsp:valueof param="username" />
</dsp:oparam>
</dsp:droplet>

<!--No special case for admin or moderator, they fall back on default-->
<dsp:droplet name="YourDroplet">
<dsp:oparam name="ouser">
User <dsp:valueof param="username" />, you are not permitted here. Begone!
</dsp:oparam>
<dsp:oparam name="default">
Welcome <dsp:valueof param="username" />. Access granted!
</dsp:oparam>
</dsp:droplet>
</dsp:page>

14Jan/110

Yet Another Reason to Namespace your JavaScript Variables

1306092411709_a923d

If you aren't namespacing your JS variables, you should be.

There are a plethora of great reasons to do so, which I won't go into here, but I just stumbled upon another great reason today.

It makes your code faster.

What? Yeah. According to my preliminary tests...

You can see here that we are running through three while loops two million times.

The first time we are creating a variable local to the while loop each time and incrementing it by 1 each time.

The second time we are referencing a global namespaced (namespaced to the random characters "GL" for absolutely no reason) variable and incrementing it by 1 each time.

The third time we are referencing a global variable (not namespaced) and incrementing it by 1 each time.

You'd think that the second and third times would be nearly identical. They aren't. Check out the results:

That's right, the local variables loop took 1584ms, the namespaced one took 1180ms and the global one took 1219ms. The namespaced one is consistently faster.

Now, think about an E-Commerce website with 50,000+ lines of JavaScript code. If these results mean what they seem to mean, imagine the performance improvements to be gained simply by namespacing your functions, variables, etc. Potentially huge? Maybe, this bears some looking into.

EDIT: These results were just bothering me. A lot. It didn't make any sense. So I Made a JSLitmus test to see if it was maybe just a firebug thing. Nope. Same results, actually even more disparate results in some browsers:

Try it for yourself (doesn't work in IE 9 for some reason).

Feel free to comment with any more tests/data you know of on this subject, I'd love to hear.

19Oct/100

Git it now?

gitCommitManagement


Git is amazing. The problem is, it's hard for a lot of people to get git. Specifically the remotes part. If you are used to CVS or SVN (Subversion) you're probably used to the idea that when you "commit" something it just flies out to the server and sits there available to anyone else with access to that server. Git doesn't do this.

Git is a distributed version control system, which means you have the entire project and the entire history of the entire project (or at least the branch you are on) sitting on your machine when you checkout the project(branch) from the server. So, when you "commit" something, you're really just creating a new version (kind of like timestamping a state of code and saving it) on YOUR machine. The remote server knows nothing about it. A lot of people new to git mistakenly think that "add" sort of saves a version of something on your machine and then commit sends it out to the server. Not so.

To help, I've created this image:

add: This takes the "work" off of your "work bench" and puts it on the "loading dock" (index).
commit: This takes everything on the "loading dock" (index) and puts it in a "crate" (commit) and places it on the "truck" (history).
push: This tells the "truck" (history) to drive itself to the "warehouse/hub" (remote) [and drop off copies of everything and come back] .

stash: This takes everything on your "work bench" and places it in a "box" (stash) on the side.

4Aug/100

POOF – Recursive Directory Listing in Python

I've begun working on a new project, POOF (Project Orphaned Object Finder). It searches through a directory and all sub-directories to detect files that are not referenced by any other files, or not referenced by a file you target or any of that file's targets etc. Basically, looking for unused or orphaned resources in a project of any language (but JS/HTML/CSS to start).

Tonight I worked on getting the directories listing recursively.

I'm sure there's some sort of built-in function or UNIX function I could call or something like that, but half of my goal with this project is learning Python, so, yeah. I wanted to do this one by hand.

General overview of my approach:

I made a function "listDirectories" which you pass three arguments to; directory, tabStops, path.

The directory is the directory you want to go to.
The tabStops is how many tabStops you want to display before the directory listing.
The path is the path you are coming from.

The function calls itself each time it encounters a subdirectory. You start the whole thing off with a hard-coded seed. The next step is to un-hard-code this seed so you can pass the seed values in from the command line, or just call the program from your current directory (which is how you will kick off the whole thing when this project is complete). Something like:

POOF.py -d=directory
or
POOF.py
or
POOF.py -d=directory -t=\t\t -p=C:\Projects\POOF

This will of course need to be ironed out because that third one looks UGLY.

Here's the current iteration of the code. Keep in mind this is my first real Python program:

23Jun/100

Flurl – Part 5: The Unicorn/Panda Rainbow Connection

2011-05-23_1043

Wait, where's parts 2 through 4? Not done yet, but I'm done with the project and I may never get around to posting those other parts and wanted to post the finished product.

Again, Flurl is a little practice exercise I did. A mashup of Flickr and Qurl and no external JS libraries used (so I wrote my own).

I'm taking this photo stream (Be careful, since the photos are completely random "popular" flickr photos, even though they purport to be "safe" there are definitely some NSFW photos now and then) and sending the URLs to Qurl for shortening (using their API). This is the end result (best experienced in Chrome): The Unicorn/Panda Rainbow Connection UPDATE: LOST FOREVER (maybe... When my site got hacked I deleted a whole bunch of stuff trying to flush out the bad code. Apparently this got whacked in the process. I *might* have a copy somewhere, but, can't find it right now).

Some thoughts: Qurl sucks as far as response time. I had to limit my photos to five because Qurl was so darn slow responding to my requests and there is no way to do a batch request. BAD. What would I do to fix this? How about dump Qurl entirely. Flickr has their own shortening algorithm that doesn't even require an API call. If I had to keep using Qurl? I'd go ahead and load the photos to the page for the user with the long links, then I'd make a button on the photo (or link or something) that allowed them to request a shortened URL from Qurl. They click the button/link and an AJAX request fires off grabbing the URL and giving it to them.

I couldn't get the Flickr API to return only a certain number of Photos. I did everything I could find that it said I should do to get it to only return five or ten photos, but alas, it didn't work. So I had to make a loop that just used the first five/ten photos and ignore the rest. If it weren't for Qurl, which takes over 30 seconds most times to shorten 5 urls, I wouldn't care how many Flickr sent back. Still weird and wasteful and if I had more time I'd look into it until I got it working.

When I removed Qurl from the loop, the photos returned in less than five seconds flat (awesome!). However, with Qurl the response time ranges from 30s to 90s. So AS SOON AS I get the response back I fire off another request. If the response only took 5s total, I'd put a timeout or interval or something that queried only once a minute or so. Or, better yet, I'd make it fire off the request 10 seconds before my photo scroll ended and just put the new photos above my current scroll and make the scroll seem endless (like the pandas).

I spent far too much time on the library. I had big plans and it turned out I wrote way more code than I ended up needing because I was doing VERY LITTLE DOM manipulation. Of course if I worked on this for another forty hours or so the library really would have paid off because it would have saved me time as my interactions got more and more complex. If I had come up with the full design before I started writing the code I would have known I wasn't going to need much DOM interaction, but as it stands I didn't have any idea what the page was going to look like until I was almost completely finished with the cQuery JS library.

Queue. Something interesting I came up with was a way of handling mutliple simultaneous AJAX requests and multiple simultaneous animations. A queue.

For the AJAX requests I had an AJAX queue that just held all of my requests (didn't end up needing this, but it is there if I decide to do the Qurl thing separate from the photo retrieval). I hope to go into the AJAX queue in more detail in another post, but the reason I needed it was the callback function. I needed somewhere to put it until the request completed.

For the animation queue, I didn't want to set up a whole bunch of different "set intervals" or "set timeouts" so instead I made an "animations" array and then made ONE setInterval that called a function that looped through the animation array. Each spot in the array held an "animation" Object, which had an "animate()" function. The animate function would get called on the object and be allowed to run in the proper context (with "this" functioning as expected). This ended up saving me a lot of code and headaches and made my JS run way faster than it otherwise would have. Of course I ended up only having one animation run at a time and I have no standard way of removing from the queue, but I could add that to the library and there is definitely room for more animations.

One last thing, the song is from Jonathan Neal (who is hilarious). I converted it to .ogg format because Firefox didn't allow anything else, however it appears that Safari doesn't accept .ogg format, so if I had more time I'd make something to detect with browser I'm in and respond with the .mp3 format instead...