Tuesday, August 14, 2018

Git untag

If you've ever forgotten to rebase your local repository before pushing a tagged commit to master
git push --tags origin master
you'll know the pain of having the push rejected with the tag still applied remotely. In this post by Nathan Hoad, you'll find the needed git commands to remove the remote tag, but it's not that easy to remember.
git tag -d 12345
git push origin :refs/tags/12345

So we're going to make a nice little shell command to do it for you!

First open the global user defined .gitconfig file. From the command line:

  • You can either find the user .gitconfig file location by using git config --list --show-origin and finding it in the wall of text. Then, open the file in your favorite editor, or
  • To load the file directly into your favorite editor, use ~/.gitconfig as the path to the config file. For example, if you have Atom installed, use atom ~/.gitconfig.

Now under the [alias] block (add it if you don't see it), add the untag alias:

[alias]
  untag = "!sh -c 'git tag -d $0 && git push origin :refs/tags/$0'"

And you're done!

To use the command, use the alias followed by the tag to delete & remove from the remote branch

git untag v1.0.0
You can find a lot more useful git aliases from the Git basics documentation.

Thursday, May 11, 2017

Convert RGB(A) output to Hex Color

Update from my Convert jQuery RGB output to Hex Color Post Modern browsers will soon support four and eight-digit hex colors, which means you now include an alpha channel with your color. As you can see in the comments of this CSS Tricks post, the alpha channel conversion is different from the other color channels and this makes it very confusing to convert:
  • The r (red), g (green), and b (blue) channels have decimal values ranging from 0 to 255.
  • the a (alpha) channel has a decimal value that ranges from 0 to 100.
My old post Convert jQuery RGB output to Hex Color was written to ignore the alpha channel when entering an RGBA value. This post updates both the javascript and the demo to output an #RRGGBB or #RRGGBBAA hex code value.

function rgb2hex(orig) {
  var a, isPercent,
    rgb = orig.replace(/\s/g, '').match(/^rgba?\((\d+),(\d+),(\d+),?([^,\s)]+)?/i),
    alpha = (rgb && rgb[4] || "").trim(),
    hex = rgb ? "#" +
    (rgb[1] | 1 << 8).toString(16).slice(1) +
    (rgb[2] | 1 << 8).toString(16).slice(1) +
    (rgb[3] | 1 << 8).toString(16).slice(1) : orig;
  if (alpha !== "") {
    isPercent = alpha.indexOf("%") > -1;
    a = parseFloat(alpha);
    if (!isPercent && a >= 0 && a <= 1) {
      a = Math.round(255 * a);
    } else if (isPercent && a >= 0 && a <= 100) {
      a = Math.round(255 * a / 100)
    } else {
      a = "";
    }
  }
  if (a) {
    hex += (a | 1 << 8).toString(16).slice(1);
  }
  return hex;
}
I've copied some of the above code from the rgb-hex node module, where I also contributed alpha channel support.

Thursday, April 3, 2014

Methods to add multi-line CSS content

I'm sure most of you know that you can add content before or after an element using css; And you can add a line break within that content (spec):

HTML
<div id="test1">Hello World</div>

CSS
#test1:after {
    content : ' of foo \A barred';
    white-space: pre; /* this line is essential */
}

Sadly, this same method doesn't work if you get your content from an attribute:

HTML
<div id="test1" data-extra=" of bar \A food">Hello World</div>

CSS
#test1:after {
    content : attr(data-extra);
    white-space: pre;
}

Example: So after some discussion with the developers of Firefox, I learned that the following alternatives do work; make sure to set the css white-space to pre.

I was hoping that these methods would work consistently and I wouldn't have to remember, so I made this blog post to remind me :)

Here is a condensed list:

content sourceCarriage ReturnExample
inline string
\A
content: "line1 \A line2"
javascript
\n
.setAttribute('data-extra', " line1 \n line2");
data-attributeinline carriage return
data-extra="line1
 line2"
&#10;
data-extra="line1 &#10; line2"
&#xA; (hex)
data-extra="line1 &#xA; line2"

Here is a full example:

Thursday, January 30, 2014

Add Anchors to jQuery UI Accordion Headers

If you want jQuery UI's accordion (version 1.10.0+) to be able to open the desired section based on the window hash, you are stuck with using a hash that looks something like this "#ui-accordion-accordion-header-1", and well, the panel won't open. It requires some extra coding.

With this code you can add a link within the header which opens the panel and updates the hash. It also opens the panel based on the hash after the accordion initializes.
I originally answered this code on Stackoverflow, but thought it would be nice to have a write up with demo.
var hashId = 0,
    $accordion = $('#accordion');
if (window.location.hash) {
    $accordion.children('h3').each(function (i) {
        var txt = this.textContent.toLowerCase().replace(/\s+/g, '_');
        if ( txt === window.location.hash.slice(1) ) {
            hashId = i;
        }
    });
}
This above first block of code sets a default hash id (zero-based index of the currently open header), then looks within each accordion header's text and checks if it matches the current window hash. The line with this.textContent (may not work in older browsers, so change it to $(this).text() as needed), gets the header text and replaces any spaces, tabs, etc that don't work within an element ID. If the header text is really long, contains punctuation like commas, exclamation points, etc, then you may need to truncate the text with something like this:
var txt = $(this).text()
    .toLowerCase()
    .replace(/[^a-z\s]/g,'')
    .replace(/\s+/g, '_')
    .substring(0,10)
This code replaces any text that isn't a letter in the alphabet, or a space, replaces spaces with an underscore, then saves the first 10 characters.
Then it compares it to the window hash. The .slice(1) removes the hash ("#") before comparing it to the accordion header text.
The hash id is then set using the header index.

Now we can initialize the accordion...
$accordion.accordion({
    active: hashId,
    animate: false,
    heightStyle: 'content',
    collapsible: true,
    create: function (event, ui) {
        $accordion.children('h3').each(function (i) {
            // set id here because jQuery UI sets them as "ui-accordion-#-header-#"
            this.id = this.textContent.toLowerCase().replace(/\s+/g, '_');
            // add the anchor
            $(this).before('');
        });
        $accordion.find('.accordion-link').click(function () {
            // the active option requires a numeric value (not a string, e.g. "1")
            $accordion.accordion( "option", "active", $(this).data('index') );

            // uncomment out the return false below to prevent the header jump
            // return false;
        });
    }
});

Set the active option to initialize the accordion with the hash tag matching section open, it uses a zero-based index.

The animate option is set to false because when the user clicks on a link, the page jumps to the section... then the animation opens the panel. It sometimes animates so the entire page scrolls up and the section header is no longer at the top of the page.

The heightStyle and collapsible options are set to my personal preference, change them as desired.

Now the create option needs to contain code to add the links and make them clickable. It cycles through all of the section headers, replaces the id to match the header text - use the code that was used to match the text in the first block of code, so the code will compare the text parsed in the same way - then add the link before the header. If the link is added after the header, the accordion messes up because it is set to look for the element (a <div>) immediately following the header. The link contains a "data-index" attribute which contains the header zero-based index used to set the active accordion panel.

And finally the code to make the link clickable. After setting the "active" accordion option, you can chose to return false, or not. If not included, the selected header will jump to the top of the browser page and if included, the page will not scroll.

Lastly, you'll need to include some css to position the link image within the section header:
.ui-accordion {
    position: relative;
}
.ui-accordion .accordion-link {
    position: absolute;
    right: 2%;
    margin-top: 16px; /* adjust as needed to vertically center the icon */
    z-index: 1;
    width: 12px; /* approx 12x12 link icon */
    height: 12px;
    background: url(http://i57.tinypic.com/fyfns4.png) center center no-repeat;
}
Here is a demo of what it looks like: or, try this full screen version of the same demo

Tuesday, April 2, 2013

Regex Replacement String Math

I needed a simple method to do math within a regex replacement. Basically, a simple way for someone using a plugin to choose between a zero or one based index.

Yes, I could have just added an additional replacement like this:
var string = '&index0={index}&index1={index+1}'
  .replace(/\{index\}/, index)
  .replace(/\{index\+1\}/, index + 1);


That works! But what you if for some unknown reason needed the index to start at 2, 3 or even 10? Easy, go back in and modify the code.

Or, just use some regex math that allows you to set any number. Try this demo, and change the last input string to use "{index+10}" or even "{index-5}":

The code isn't really that complicated:
var string = '&index0={index}&index1={index+1}'
  .replace(/\{index([-+]\d+)?\}/g, function(fullstring, match){
    return index + (match ? parseInt(match, 10) : 0);
  });
It uses a replace function to take the index and add the first regex matching string obtained from the match argument.

But before that, we need to use a ternary operator to check if there is a match. If there isn't one, then add zero to the index.

We then need to parse the matched string into a numerical value so we can add it to the index. Then return the result.

I hope someone finds this useful :)

Wednesday, January 30, 2013

More fun with the base tag!

I have a post from a few years ago about using the base tag for debugging. Well I just figured out an even cooler way to use it in jsFiddle demos!

I have a base jsFiddle demo for just about every plugin I've made. It makes it easier for others to experiment with all of the plugin options to get a better feel for it. Well, at least I think so. At the very least, it makes it easier for me to build a demo to help all of you ;)

Anyway, I wanted to link to the images from the AnythingSlider github repo. The urls end up being quite long: http://css-tricks.github.com/AnythingSlider/demos/images/slide-civil-1.jpg

Enter the base tag! I just add it to the top of the html frame, and BAM short url sweetness inside :P



I tried making this same demo with codepen.io, but sadly it didn't work =(
Update 1/18/2014: It works on codepen.io now!

Friday, December 21, 2012

Using Google Closure Compiler to write better code

So I ran into a bit of code that I wrote a while back. I thought could be written a lot more efficiently. So I thought, hey Google's Closure Compiler is good at that stuff right? Let see what happens!

So, lets say we started out with this list of contacts:

var list = {
 'friends' : {
  'fred' : '123-4567',
  'lisa' : '123-6789'
 },
 'work' : {
  'joe'  : '234-5678'
 }
}

I need a function to add someone to the list, or update a number, but it need to check if the group already exists or if the person is already listed:

function addName(group, name, number){
 if (list[group] && list[group][name]){
  list[group][name] = number;
 } else {
  if (list[group]){
   list[group][name] = number;
  } else {
   list[group] = {};
   list[group][name] = number;
  }
 }
}

so the addName function starts out by seeing if the group exists, then if the name exists within that group:

if (list[group] && list[group][name]){

if that works, then just add/update the number. If not, then we drop through the else to the next set of evaluations. Does the group exist?

if (list[group]){

Yes? Add the number, if not, keep going to through the next else, from here we know that the group doesn't exist, so we define it, then add the name.

Seems efficient right?... hmm, but I see a lot of list[group][name] = number; in there. Lets see what Google's closure compiler does to it.

But first we need to keep the compiler from changing all the names. Lets pull out the code from within the function and force the compiler to keep our constants (see the first line):

/** @const */ var list = {}, group, name, number;

 if (list[group] && list[group][name]){
  list[group][name] = number;
 } else {
  if (list[group]){
   list[group][name] = number;
  } else {
   list[group] = {};
   list[group][name] = number;
  }
 }

The extra asterisk in the comment is important! (ref). Note, maybe there is a better way to force the compiler to keep your variable names within a function, but I don't know it.

Anyway, click the reset link and paste the code above into the compiler (use the "Simple" setting if it isn't already set). You'll end up with this:

var list={},group,name,number;if(!list[group]||!list[group][name])list[group]||(list[group]={});list[group][name]=number;

If it's too messy for you to clean up yourself, or you have a lot more code, copy that result and paste it into jsbeautifier and you'll end up with nicer formatted code.

Also, it's important to remember that compilers don't necessarily always use curly brackets after if or else statements, so the above code is equivalent to:

var list = {}, group, name, number;
if (!list[group] || !list[group][name]) {
  list[group] || (list[group] = {});
}
list[group][name] = number;

So now we can replace our function wrapper and remove the constants:

function addName(group, name, number){
  if (!list[group] || !list[group][name]) {
    list[group] || (list[group] = {});
  }
  list[group][name] = number;
}

That looks better, but I'm still seeing a bunch of list[group] in there. I'm sure the code can be a bit more DRY (don't repeat yourself). Also do we really need to check list[group][name]? It was checked initially but not checked again...

function addName(group, name, number){
  if (!list[group]) {
    list[group] || (list[group] = {});
  }
  list[group][name] = number;
}

Well now I see that list[group] is checked twice. Let's get rid of that and see what happens:

function addName(group, name, number){
  if (!list[group]) {
    list[group] = {};
  }
  list[group][name] = number;
}

Now lets see what we have. Check if list[group] exists, then add it if it doesn't. Add the number. Wow! Short and sweet. Why didn't I write it like that originally? Oh yeah, I'm still a noob :)

So after some testing, this new cleaned up function works exactly like the original! We went from 10 lines inside of the addName function down to 4! I'm happy, a happy noob!

I threw together this demo for testing.



I hope that my process of learning at least gives you some ideas. I don't claim to be an expert at javascript or jQuery, but I try to learn a little every day :)

As a redneck friend of mine once said, "Dem Google people is smart!"

Thursday, August 2, 2012

CSS-Tricks Organization

If you follow me on Github, you may notice that some of my repositories are now missing... Well, I moved them over to the CSS-Tricks Organization repositories. I thought it would make it easier for people to find and contribute to them, especially if I'm away on vacation ;)

All of the following repos have been transferred, so far:

I hope this move get more feedback and contributions to improve the code even more :)

And no, I'm not abandoning any of them.

Monday, July 23, 2012

jQuery unwrapInner()

Update: Well, I made a nice post about extending jQuery with an unwrapInner function, but I discovered that the same thing can be done by using jQuery's replaceWith() function...
Starting with this HTML:
<div class="test">
    <span class="red">
        red text
    </span>
    <div class="green">
        green text
    </div>
</div>
the span from around the red text can be removed by using the replaceWith() function as follows:
// unwrap red text
$('.test').find('.red')
    .replaceWith( $('.test .red').contents() );

So you can pretty much ignore the rest of this post =(

jQuery has a wrap and wrapAll which can be removed by using the unwrap function. But it only targets the parents of the selected element. What about wrapInner? It doesn't have a method to remove it. So I put together this small extension which does just that.

So say we start with this HTML:
<div class="test">
    <span>some text</span>
</div>
To remove that span from around "some text", we just call the unwrapInner code below
// unwrapInner function
jQuery.fn.extend({
    unwrapInner: function(selector) {
        return this.each(function() {
            var t = this,
                c = $(t).children(selector);
            if (c.length === 1) {
                c.contents().appendTo(t);
                c.remove();
            }
        });
    }
});
You can include a selector to target a specific element
// examples
$("div").unwrapInner(); // remove immediate child
$("div").unwrapInner("span"); // remove immediate child if it's a span
$("div").unwrapInner("span.inner"); // remove all immediate children of class '.inner'
Note that the one issue with this function is when there are multiple children to be unwrapped, it will add the unwrapped content to the end. For example, with this HTML:
<div class="test">
    <span class="red">
        red text
    </span>
    <div class="green">
        green text
    </div>
</div>
We want to unwrap span.red, so we use
$(".test").unwrapInner(".red");
but we get this result:
<div class="test">
    <div class="green">
        green text
    </div>
    red text
</div>
Check out this demo:

Friday, December 30, 2011

Adding the MovingBoxes plugin to Blogger

These instructions will allow you to add a MovingBoxes widget to any Blogger post. These instruction really apply to any plugin out there, but instead of making a generic post, I think a specific example is best.

  • picture

    Olivia News Heading

    A very short exerpt goes here... more
  • picture

    Alice News Heading

    A very short exerpt goes here... more and a whole lot more text goes here, so we can see the height adjust.
  • picture

    Yin News Heading

    A very short exerpt goes here... more
  • picture

    Gerri News Heading

    A very short exerpt goes here... more
  • picture

    Tabitha News Heading

    A very short exerpt goes here... more
  • picture

    Mary News Heading

    A very short exerpt goes here... more
  • picture

    Kitty News Heading

    A very short exerpt goes here... more

Note: In the next version of MovingBoxes, I plan to change the width and panelWidth options. At that time, I will update this post.

Code, CSS and image setup

Required Files
I don't know how github would feel about directly linking to their servers. Most businesses frown upon hot-linking and will most likely block your URL, or the entire domain if the hot-linking gets too rampant. So I'd recommend copying each file below and saving it to your own server.
Note that inside of the css, you'll need to change the arrows.png url to point to the new location of the image file.

/*** Left & Right Navigation Arrows ***/
a.mb-scrollButtons {
  background: transparent url(../images/arrows.png) no-repeat;
}

If you don't have a server, you can use dropbox or your iCloud (I don't own a Mac, so I'm only guessing that it'll allow you to save javascript & css files). If you don't have a server to save the files, then first, save the image to an image hosting site, like photobucket, Flickr or something. Then you can save the javascript and css into your own blog design. The main problem with saving into your page design is that it will increase your page size and possibly make the page loading take longer. Either way you'll need to add a gadget, or add to an already existing one.

Add a Gadget
So once you have your files saved and the css file updated:
  • Go to your Dashboard > Design > Add a Gadget > Pick the "HTML/Javascript" gadget.
  • Once the popup window opens, add the following plugin css and javascript in the content window, the URL should point to your hosted files
  • Note: After testing this, it seems that adding a <link> tag inside of the HTML/Javascript gadget gets removed. I'm not sure why, but in this case we can just paste in the entire css.

    <style>
    /*** Overall MovingBoxes Slider ***/
    .mb-wrapper {
     width: 900px; /* default, this is overridden by script settings */
     min-height: 200px;
     border: 5px solid #ccc;
     margin: 0 auto;
     position: relative;
     left: 0;
     top: 0;
     border-radius: 1em;
     -moz-border-radius: 1em;
     -webkit-border-radius: 1em;
     box-shadow: inset 0 0 10px #888;
     -moz-box-shadow: inset 0 0 10px #888;
     -webkit-box-shadow: inset 0 0 10px #888;
    }
    
    /* Panel Wrapper */
    .mb-slider, .mb-scroll {
     width: 100%;
     height: 100%;
     overflow: hidden;
     margin: 0 auto;
     padding: 0;
     position: relative;
     left: 0;
     top: 0;
    
     /***(>'-')> Control Panel Font size here <('-'<)***/
     font-size: 18px;
    }
    
    /* active slider border highlight */
    .mb-active-slider {
     border-color: #999bff;
    }
    
    /*** Slider panel ***/
    .mb-slider .mb-panel {
     width: 350px;  /* default, this is overridden by script settings */
     margin: 5px 0;
     padding: 5px;
     display: block;
     cursor: pointer;
     float: left;
     list-style: none;
    }
    
    /* Cursor to arrow over current panel, pointer for all others,
    change .current class name using plugin option, currentPanel : 'current' */
    .mb-slider .mb-panel.current {
     cursor: auto;
    }
    
    /*** Inside the panel ***/
    .mb-inside {
     padding: 10px;
     border: 1px solid #999;
    }
    
    .mb-inside * {
     max-width: 100%;
    }
    
    /*** Left & Right Navigation Arrows ***/
    a.mb-scrollButtons {
     display: block;
     width: 45px;
     height: 58px;
     background: transparent url(http://css-tricks.github.com/MovingBoxes/images/arrows.png) no-repeat;
     position: absolute;
     top: 50%;
     margin-top: -29px; /* if you change the arrow images, you may have to adjust this (1/2 height of arrow image) */
     cursor: pointer;
     text-decoration: none;
     outline: 0;
     border: 0;
    }
    a.mb-scrollButtons.mb-left {
     background-position: left top;
     left: -45px;
    }
    a.mb-scrollButtons.mb-right {
     background-position: right top;
     right: -45px;
    }
    a.mb-scrollButtons.mb-left:hover {
     background-position: left bottom;
    }
    a.mb-scrollButtons.mb-right:hover {
     background-position: right bottom;
    }
    a.mb-scrollButtons.disabled {
     display: none;
    }
    
    /*** Controls added below the panels ***/
    .mb-controls {
     margin: 0 auto;
     text-align: center;
     background: #ccc;
     position: relative;
     z-index: 100;
    }
    .mb-controls a {
     color: #444;
     font: 12px Georgia, Serif;
     display: inline-block;
     text-decoration: none;
     padding: 2px;
     height: 18px;
     margin: 0 5px 0 0;
     text-align: center;
     outline: 0;
    }
    .mb-controls a.current, .mb-controls a:hover {
     color: #fff;
    }
    .mb-active-slider .mb-controls {
     background: #999bff;
    }
    </style>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
    <script src="http://css-tricks.github.com/MovingBoxes/js/jquery.movingboxes.min.js"></script>
    <script>
    jQuery(function(){
    
      jQuery('.movingboxes').movingBoxes({
        startPanel   : 1,      // start with this panel
        width        : 300,    // overall width of movingBoxes
        panelWidth   : 0.5,    // current panel width adjusted to 50% of overall width
        wrap         : false,  // if true, the panel will "wrap" (it really rewinds/fast forwards) at the ends
        buildNav     : true,   // if true, navigation links will be added
        navFormatter : function(){ return "&#9679;"; } // function which returns the navigation text for each panel
      });
    
    });
    </script>

  • Modify the "width" and "panelWidth" options (in red) as desired. Or add and/or remove options following these instructions.
  • If you don't have your own server, then you can open the movingboxes.css file in a text editor and save it into the content window; again make sure the arrow.png url is pointing to the new location in the css below.

    <style>
      /* add movingboxes.css contents here */
    </style>
    <script>
      /* add jquery.movingboxes.min.js contents here */
    </script>
Adding a blog post

Now the first step here would be to open a new post.

Switch the editor to "Edit HTML" - tab in the upper right corner, then paste in the basic plugin HTML Note, keep the <img> and <li> on the same line or blogger will add a <br> in between.

<ul class="movingboxes">
  <li><img src="http://css-tricks.github.com/MovingBoxes/demo/1.jpg" alt="picture">
    <h2>Olivia News Heading</h2>
    <p>A very short exerpt goes here... <a href="http://flickr.com/photos/justbcuz/112479862/">more</a></p>
  </li>
  <li><img src="http://css-tricks.github.com/MovingBoxes/demo/2.jpg" alt="picture">
    <h2>Alice News Heading</h2>
    <p>A very short exerpt goes here... <a href="http://flickr.com/photos/joshuacraig/2698975899/">more</a> and a whole lot more text goes here, so we can see the height adjust.</p>
  </li>
  <li><img src="http://css-tricks.github.com/MovingBoxes/demo/3.jpg" alt="picture">
    <h2>Yin News Heading</h2>
    <p>A very short exerpt goes here... <a href="http://flickr.com/photos/ruudvanleeuwen/468309897/">more</a></p>
  </li>
  <li><img src="http://css-tricks.github.com/MovingBoxes/demo/4.jpg" alt="picture">
    <h2>Gerri News Heading</h2>
    <p>A very short exerpt goes here... <a href="http://flickr.com/photos/emikohime/294092478/">more</a></p>
  </li>
  <li><img src="http://css-tricks.github.com/MovingBoxes/demo/5.jpg" alt="picture">
    <h2>Tabitha News Heading</h2>
    <p>A very short exerpt goes here... <a href="http://www.flickr.com/photos/fensterbme/499006584/">more</a></p>
  </li>
  <li><img src="http://css-tricks.github.com/MovingBoxes/demo/6.jpg" alt="picture">
    <h2>Mary News Heading</h2>
    <p>A very short exerpt goes here... <a href="#">more</a></p>
  </li>
  <li><img src="http://css-tricks.github.com/MovingBoxes/demo/7.jpg" alt="picture">
    <h2>Kitty News Heading</h2>
    <p>A very short exerpt goes here... <a href="#">more</a></p>
  </li>
</ul>

Note that the UL has a class named "movingboxes", it originally had an ID, but to make this plugin work on any future blog entries we need to make sure it's a class name we can remember.

Now, just publish your post and reload your page. The MovingBoxes plugin should initialize and look like it does at the top of this post :)

Saturday, December 17, 2011

All Images Loaded (imagesLoaded)

*NOTE* this code causes infinite loops in IE with image load errors, so I improved and turned this into a real plugin and hosted it on github.


A little jQuery function that checks if all images are loaded. If they all are, the callback function is called. Check out the latest version in this gist.

Script below:
/*
 Check if all images are loaded
 - Callback occurs when all images are loaded
 - image load errors are ignored (complete will be true)
 - Use:
   $('.wrap img').imagesLoaded(function(){
     alert('all images loaded');
   });
*/

jQuery.fn.extend({
  imagesLoaded: function( callback ) {
    var i, c = true, t = this, l = t.length;
    for ( i = 0; i < l; i++ ) {
      if (this[i].tagName === "IMG") {
        c = (c && this[i].complete && this[i].height !== 0);
      }
    }
    if (c) {
      if (typeof callback === "function") { callback(); }
    } else {
      setTimeout(function(){
        jQuery(t).imagesLoaded( callback );
      }, 200);
    }
  }
});

Use it as follows:
$(function(){
  $('.wrap img').imagesLoaded(function(){
    alert('all images loaded');
  });
});

Tuesday, December 6, 2011

jQuery Pathslider

I just created this plugin that is a very basic UI Slider (similar to the jQuery slider), but it doesn't just allow you to move the handle horizontally and vertically, it will follow any shaped curve! If you've ever used Adobe Illustrator or CorelDraw then you probably recognize the Bezier curve below.

The handle, or grip as I call it, can be dragged along the black curve. For now, it only returns values from 0 (green dot) to 100 (red dot).


The plugin is in the early stages of development and still needs a lot of work, but it is usable now.

Check out the demo page, and the builder page. If you would like to help me out, submit an enhancement or issue, or fork a copy of the plugin on Github and send me a pull request! I'd love the input!

Monday, November 14, 2011

jQuery UI Side Scroller with buttons

Once again, I got up at 2:30am wide awake... that's what happens when I fall asleep at 9:30 LOL. Anyway, I was bored so I searched for something to do. I found a question on CSS Tricks asking how to add buttons to the jQuery UI Slider. So starting with the side scroll demo as a base, I put together this demo. That is all :P

Monday, October 31, 2011

Get All indexOf From An Array - Array.allIndexOf()

This array function is an extension of Array.indexOf(), if it exists, and will return all indexes of the search element. It's named allIndexOf(). This code extends javascript and does not require jQuery. It is designed to work in older versions of IE as well, albeit a tiny bit slower.

Originally if you wanted the second instance of a search element, you'd have to call "indexOf" twice. The second time with the starting index of the first result (plus one), or once with a guessed starting index. This should simplify the process for getting any or all of the indexes.

/*
Array.allIndexOf(searchElement)
  Array [Array] - the array to search within for the searchElement
  searchElement [String] - the desired element with which to find starting indexes
*/
(function(){
  Array.prototype.allIndexOf = function(searchElement) {
    if (this === null) { return [-1]; }
    var len = this.length,
    hasIndexOf = Array.prototype.indexOf, // you know, because of IE
    i = (hasIndexOf) ? this.indexOf(searchElement) : 0,
    n,
    indx = 0,
    result = [];
    if (len === 0 || i === -1) { return [-1]; }
    if (hasIndexOf) {
      // Array.indexOf does exist
      for (n = 0; n <= len; n++) {
        i = this.indexOf(searchElement, indx);
        if (i !== -1) {
          indx = i + 1;
          result.push(i);
        } else {
          return result;
        }
      }
      return result;
    } else {
    // Array.indexOf doesn't exist
      for (n = 0; n <= len; n++) {
        if (this[n] === searchElement) {
          result.push(n);
        }
      }
      return (result.length > 0) ? result : [-1];
    }
  };
})();
Use it as follows:
var s = ["red","green","blue","red","yellow","blue","green","purple","red"];
s.allIndexOf("r"); // result [ -1 ]
s.allIndexOf("red"); // result [ 0,3,8 ]
s.allIndexOf("blue"); // result [ 2,5 ]
Try out your own strings in the demo below or full screen.

Get All indexOf From A Search String - String.allIndexOf()

This string function is an extension of String.indexOf() and will return all indexes of a search string. It's named allIndexOf. This code extends javascript and does not require jQuery.

Originally if you wanted the second instance of a search string, you'd have to call "indexOf" twice. The second time with the starting index of the first result (plus one), or once with a guessed starting index. This should simplify the process for getting any or all of the indexes. And yes, I woke up with a strange urge to write this function LOL.

/*
String.allIndexOf(searchstring, ignoreCase)
  String [String] - the string to search within for the searchstring
  searchstring [String] - the desired string with which to find starting indexes
  ignoreCase [Boolean] - set to true to make both the string and searchstring case insensitive
*/
(function(){
  String.prototype.allIndexOf = function(string, ignoreCase) {
    if (this === null) { return [-1]; }
    var t = (ignoreCase) ? this.toLowerCase() : this,
    s = (ignoreCase) ? string.toString().toLowerCase() : string.toString(),
    i = this.indexOf(s),
    len = this.length,
    n,
    indx = 0,
    result = [];
    if (len === 0 || i === -1) { return [i]; } // "".indexOf("") is 0
    for (n = 0; n <= len; n++) {
      i = t.indexOf(s, indx);
      if (i !== -1) {
        indx = i + 1;
        result.push(i);
      } else {
        return result;
      }
    }
    return result;
  }
})();
Use it as follows:
var s = "The rain in Spain stays mainly in the plain";
s.allIndexOf("ain"); // result [ 5,14,25,40 ]
s.allIndexOf("the"); // result [ 34 ]
s.allIndexOf("THE", true); // result [ 0,34 ]
Try out your own strings in the demo below or full screen.

Thursday, October 20, 2011

jQuery Rename Attribute (renameAttr)

I woke up this morning with a very unusual urge to make a rename attribute function for jQuery. I know, right.. that's just too weird! So after messing around with it, I decided that it needed to update the data as well. Here is the code I ended up with:

jQuery.fn.extend({
  renameAttr: function( name, newName, removeData ) {
    var val;
    return this.each(function() {
      val = jQuery.attr( this, name );
      jQuery.attr( this, newName, val );
      jQuery.removeAttr( this, name );
      // remove original data
      if (removeData !== false){
        jQuery.removeData( this, name.replace('data-','') );
      }
    });
  }
});

Use it as follows:
// $(selector).renameAttr(original-attr, new-attr, removeData);

// removeData flag is true by default
$('#test').renameAttr('data-test', 'data-new' );

// removeData flag set to false will not remove the
// .data("test") value
$('#test').renameAttr('data-test', 'data-new', false );

Basically, it adds a new attribute with the same value and removes the old one. It automatically removes the jQuery data for the original attribute unless you set the "removeData" flag to false. Check out this demo

Thursday, October 6, 2011

Adding Swipe Support

I spent some time trying to figure out how to add mobile swipe support without using jQuery Mobile, because the swipe is all I really needed. I don't own an iPhone/iPad/iAnything, so it took a bit of back-and-forth testing - thanks to all the folks over at CSS-Tricks.com - to get this to finally work.



Here is the complete code:

var maxTime = 1000, // allow movement if < 1000 ms (1 sec)
    maxDistance = 50,  // swipe movement of 50 pixels triggers the swipe

    target = $('#box'),
    startX = 0,
    startTime = 0,
    touch = "ontouchend" in document,
    startEvent = (touch) ? 'touchstart' : 'mousedown',
    moveEvent = (touch) ? 'touchmove' : 'mousemove',
    endEvent = (touch) ? 'touchend' : 'mouseup';

target
    .bind(startEvent, function(e){
        // prevent image drag (Firefox)
        e.preventDefault();
        startTime = e.timeStamp;
        startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
    })
    .bind(endEvent, function(e){
        startTime = 0;
        startX = 0;
    })
    .bind(moveEvent, function(e){
        e.preventDefault();
        var currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX,
            currentDistance = (startX === 0) ? 0 : Math.abs(currentX - startX),
            // allow if movement < 1 sec
            currentTime = e.timeStamp;
        if (startTime !== 0 && currentTime - startTime < maxTime && currentDistance > maxDistance) {
            if (currentX < startX) {
                // swipe left code here
                target.find('h1').html('Swipe Left').fadeIn();
                setTimeout(function(){
                    target.find('h1').fadeOut();
                }, 1000);
            }
            if (currentX > startX) {
                // swipe right code here
                target.find('h1').html('Swipe Right').fadeIn();
                setTimeout(function(){
                    target.find('h1').fadeOut();
                }, 1000);
            }
            startTime = 0;
            startX = 0;
        }
    });


Now, lets break down what each line does. First, some definitions:

var maxTime = 1000, // allow movement if < 1000 ms (1 sec)
    maxDistance = 50,  // swipe movement of 50 pixels triggers the swipe

    target = $('#box'),
    startX = 0,
    startTime = 0,

The "maxTime" variable is a set to 1000 milliseconds. It is used to compare the touch start and touch end times, and if less than one second (1000 ms) it is considered to be a valid swipe, otherwise it is ignored. Basically, we don't like them slow swipers! Hehe, ok, actually, it to determine if the event ends up being a touch or a swipe.

"maxDistance" is the number of pixels from the touch start point that the mouse or a finger moved to determine if a swipe or touch is occurring.

The "target" variable, is the target element to add swipe support to. For the demo, I added an H1 tag inside of this element to add a swipe message. The "startX" and "startTime" variables are set to zero because the "touchmove" or "mousemove" event looks to see if they are set with the "startX" position and start time. If zero, it ignores the move.

In the next part we figure out of the device has touch support and set up the event names appropriately:

touch = "ontouchend" in document,
startEvent = (touch) ? 'touchstart' : 'mousedown',
moveEvent = (touch) ? 'touchmove' : 'mousemove',
endEvent = (touch) ? 'touchend' : 'mouseup';

The "touch" varible is true if the "ontouchend" event exists, which means touch is supported. All that line is doing is looking to see if that object exists.

The next variables, "startEvent", "moveEvent" and "endEvent" contain the necessary event names for start, move and end, respectively. Which the next lines of code will now bind to:
target
    .bind(startEvent, function(e){
        // prevent image drag (Firefox)
        e.preventDefault();
        startTime = e.timeStamp;
        startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;
    })
The "touchstart" or "mousedown" event code contains the following:

"e.preventDefault()" prevents the image drag function in Firefox which actually makes you think you're dragging the image instead of swiping. Now we define "startTime" and "startX" as the start time and position of the swipe. The "startX" needs to get the position of the touch from the touches variable, but only the first one. We don't want to swipe if there are multiple touches (gestures) but it shouldn't work anyway since we're not binding to the gestures event.
.bind(endEvent, function(e){
    startTime = 0;
    startX = 0;
})
Now the "touchend" or "mouseup" event code only clears the start time and position variables. The reason for this, which was a fun and painful lesson, is that these events don't fire when your mouse/finger leaves the element or goes off the screen. So the actual swipe calculations are done in the move event functions.

.bind(moveEvent, function(e){
    e.preventDefault();
    var currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX,
        currentDistance = (startX === 0) ? 0 : Math.abs(currentX - startX),
        // allow if movement < 1 sec
        currentTime = e.timeStamp;
    if (startTime !== 0 && currentTime - startTime < maxTime && currentDistance > maxDistance) {
        if (currentX < startX) {
            // swipe left code here
            target.find('h1').html('Swipe Left').fadeIn();
            setTimeout(function(){
                target.find('h1').fadeOut();
            }, 1000);
        }
        if (currentX > startX) {
            // swipe right code here
            target.find('h1').html('Swipe Right').fadeIn();
            setTimeout(function(){
                target.find('h1').fadeOut();
            }, 1000);
        }
        startTime = 0;
        startX = 0;
    }
});

The "touchmove" or "mousemove" events is where the main portion of this swipe code exists. Again, we want to prevent the default functionality of the move/drag. The "currentX" variable is set with the current x position of the finger/mouse. Again, the x position is obtained from the touches variable, if available. The difference "currentDistance" between the starting x and current x position is then determined. We need the absolute value of this number (ignore the negative sign if it is there) to see if it is more than the "maxDistance" variable. Also here is another trick to make sure the move event is occurring after a start event, check to make sure the starting x position isn't zero. If it is, set the "currentDistance" variable to zero. We also need determine the "currentTime" current time of the move event.

Now, we check to make sure the move event occurred after a start event. This can happen is the start event occurs outside of the element or window. So, first we make sure the "startTime" isn't zero, then we check that the time difference from start to current time is less than one second. And finally, we check that the current distance is more than the max distance setting. If it is, then we have a swipe!

YAY finally a swipe! Now to just figure out if it was going to the right or left. We do this by comparing the current position "currentX" to the starting position "startX"; if the current position is less than the start, then it's a swipe to the left and if it is greater, then the swipe was to the right.

Last but not least, we set the start time and position back to zero to prevent multiple firing of the swipe event.

I hope everything had a clear enough explanation. Please feel free to leave a comment or email me (gmail with wowmotty as the user) with questions or if you need further clarification.

Thursday, September 22, 2011

Print a Paragraph

By default, browsers will print the content of an entire page/web page or selected text. With some simple scripting you can make it easier for your users to get the browser to print the contents of a particular block or paragraph. The trick is to copy these contents into a new popup window, then print the contents of that window. I put together a very simple demo, which I should turn into a plugin, of how to do this:

Sunday, September 11, 2011

AnythingSlider Themes

I put together ten more themes for AnythingSlider! YAY!


  • All theme files are meant to be used independently from the plugin's "anythingslider.css" file. So you only need to include one of these theme files.
  • There is an additional stylesheet named "wrappers.css" which only targets the wrapper around the slider. It is independent of the theme files and meant to be used by extracting out any specific wrapper style you would like to use.
  • The first default theme is meant to be used as the base to make your own theme, but without using images or css3.
  • The second default theme is also meant to be used as a base for your own theme, but includes images (no css3 though)
  • The rest of the themes are free to use.
If you would like to contribute a theme, either fork the repository, add your theme then send me a pull request or just email me the files - my gmail account, user name is wowmotty.

Thanks and enjoy!

Wednesday, August 17, 2011

AnythingSlider FX Builder

So, to make it easier to figure out how to add FX to AnythingSlider, I put together a bookmarklet (get it from here) that allows you to build/play with the effects live :)



If you need instructions, I have some very basic information in the readme file on github.

Right now, it's still in beta and it only works on the first slider on the page. I'd appreciate any feedback on what could be improved, changed, or if you find any problems.