PageSourceSearch

https://motorbikesimulator.com/wp-content/plugins/superstorefinder-wp/js/plugins/homebrew.js?ver=1.0

js motorbikesimulator.com collected 2026-10-09 23:00:51 UTC 178,899 bytes, 3,739 lines download raw bytes

1/*!
2 * 
3 * Homebrewed plugin functions!
4 *
5 * Basically it's a bunch of common functions used in the websites we
6 * build. They're built in the format of jQuery plugins for reusability.
7 *
8 * You may remove those that you don't need.
9 *
10 * - HC
11 *
12 * @TODO: Make these more extensible for easier future usage.
13 */
14var homebrew = {};
15(function($) {
16    /* Avoid `console` errors in browsers that lack a console. */
17    var method,
18        noop = function () {},
19        methods = [
20            'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
21            'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
22            'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
23            'timeStamp', 'trace', 'warn'
24        ],
25        length = methods.length,
26        console = (window.console = window.console || {});
27
28    while (length--) {
29        method = methods[length];
30
31        // Only stub undefined methods.
32        if (!console[method]) {
33            console[method] = noop;
34        }
35    }
36
37    /* Setup homebrew object */
38    var root = $('html');
39    $.extend(homebrew, {
40        browser : {
41            ie      : root.hasClass('ie'),
42            ie9     : root.hasClass('ie9'),
43            lt9     : root.hasClass('lt9'),
44            ie8     : root.hasClass('ie8'),
45            lt8     : root.hasClass('lt8'),
46            ie7     : root.hasClass('ie7'),
47            firefox : (window.mozIndexedDB !== undefined)
48        },
49
50        events : {
51            transitionEnd : 'oTransitionEnd otransitionend webkitTransitionEnd transitionend'
52        },
53
54        classes : {
55            transitionable: 'is-transitionable'
56        },
57
58        screenSize : {
59            small : false,
60            medium : false,
61            large : true
62        },
63
64        mediaQueries : {
65            small : 'only screen and (max-width: 40em)',
66            medium : 'only screen and (min-width: 40.063em)',
67            large : 'only screen and (min-width: 64.063em)'
68        },
69
70        mediaQueriesIE9 : {
71            small : { size : 640, method : 'max-width' },
72            medium : { size : 641, method : 'min-width' },
73            large : { size : 1025, method : 'min-width' }
74        }
75    });
76
77
78
79    $.extend(homebrew, {
80        utils : {
81            /*
82             * Executes a function a max of once every n milliseconds 
83             * 
84             * Arguments:
85             *    Func (Function): Function to be throttled.
86             * 
87             *    Delay (Integer): Function execution threshold in milliseconds.
88             * 
89             * Returns:
90             *    Lazy_function (Function): Function with throttling applied.
91             */
92            throttle : function(func, delay) {
93                var timer = null;
94
95                return function () {
96                    var context = this,
97                        args = arguments;
98
99                    clearTimeout(timer);
100
101                    timer = setTimeout(function () {
102                        func.apply(context, args);
103                    }, delay);
104                };
105            },
106            /* End throttle */
107
108            /*
109             * Executes a function when it stops being invoked for n seconds
110             * Modified version of _.debounce() http://underscorejs.org
111             *
112             * Arguments:
113             *    Func (Function): Function to be debounced.
114             *
115             *    Delay (Integer): Function execution threshold in milliseconds.
116             * 
117             *    Immediate (Bool): Whether the function should be called at the beginning 
118             *    of the delay instead of the end. Default is false.
119             *
120             * Returns:
121             *    Lazy_function (Function): Function with debouncing applied.
122             */
123            debounce : function(func, delay, immediate) {
124                var timeout, result;
125
126                return function() {
127                    var context = this,
128                        args = arguments,
129                        later = function() {
130                          timeout = null;
131                          if (!immediate) result = func.apply(context, args
131);
132                        };
133
134                    var callNow = immediate && !timeout;
135
136                    clearTimeout(timeout);
137                    timeout = setTimeout(later, delay);
138                    if (callNow) result = func.apply(context, args);
139
140                    return result;
141                };
142            }
143            /* End debounce */
144        },
145        /* End utils */
146
147        makePlugin : function(plugin) {
148            var pluginName = plugin.prototype.name;
149
150            $.fn[pluginName] = function(options) {
151                var args = $.makeArray(arguments),
152                    after = args.slice(1);
153
154                return this.each(function() {
155                    var instance = $.data(this, pluginName);
156
157                    if(instance) {
158                        if(typeof options === 'string') {
159                            instance[options].apply(instance, after);
160                        } else if(instance.update) {
161                            instance.update.apply(instance, args);
162                        }
163                    } else {
164                        new plugin(this, options);
165                    }
166                });
167            };
168        },
169        /* End makePlugin */
170
171        generateUniqueID : function() {
172            return String(new Date().getTime()) + String(Math.round(Math.random()*100000, 10));
173        },
174        getSelectorsFromHTMLString : function(str) {
175            if(typeof str !== 'string') {
176                console.error('homebrew.getSelectorsFromHTMLString(): Expecting a string as the first argument. Please check:');
177                console.log(str);
178                return {};
179            }
180
181            var tagMatch = str.match(/<(.*?) /g),
182                attributesMatch = str.match(/ (.*?)=("|')(.*?)("|')/g),
183                selectorsObj = {};
184
185            if(tagMatch) {
186                selectorsObj.tag = tagMatch[0].replace(/(<| )/g, '');
187            }
188
189            if(attributesMatch) {
190                var attributeSplitArray,
191                    classesArray;
192
193                while(attributesMatch.length) {
194                    attributeSplitArray = $.trim(attributesMatch.shift()).split('=');
195
196                    switch(attributeSplitArray[0]) {
197                        case 'class' :
198                            classesArray = attributeSplitArray[1].replace(/("|')/g, '').split(' ');
199
200                            for(var i = classesArray.length-1; i > -1; i--) {
201                                if(classesArray[i] === '') {
202                                    classesArray.splice(i, 1);
203                                }
204                            }
205
206                            selectorsObj.classes = classesArray;
207                        break;
208
209                        default :
210                            selectorsObj[attributeSplitArray[0]] = attributeSplitArray[1].replace(/("|')/g, '');
211                        break;
212                    }
213                }
214            }
215
216            return selectorsObj;
217        },
218        /* End getSelectorsFromHTMLString */
219
220        getClassFromHTMLString : function(str) {
221            var selectorsObj = this.getSelectorsFromHTMLString(str);
222
223            if(selectorsObj.classes && selectorsObj.classes.length) {
224                return selectorsObj.classes;
225            } else {
226                return [];
227            }
228        },
229
230        getKeyValuePairsFromString : function(str, pairSeparator, keyValueSeparator) {
231            pairSeparator = pairSeparator || ';';
232            keyValueSeparator = keyValueSeparator || ':';
233            var splitArray = str.split(pairSeparator),
234                keyValuePairs = {},
235                currentPair;
236            while(splitArray.length) {
237                currentPair = splitArray.shift();
238                if(!currentPair) continue;
239                currentPair = currentPair.split(keyValueSeparator);
240                currentPair = currentPair.map(function(str) {
241                    return $.trim(str);
242                });
243                if(currentPair[1] === 'true') {
244                    currentPair[1] = true;
245                } else if(currentPair[1] === 'false') {
246                    currentPair[1] = false;
247                }
248                keyValuePairs[currentPair[0]] = currentPair[1];
249            }
250            return keyValuePairs;
251        },
252        watchSize : function(mediaQuery, callback) {
253            var self = this,
254                _mediaQuery = self.mediaQueries[mediaQuery];
255
256            if(typeof _mediaQuery === 'undefined') {
257                throw new Error('homebrew.watchSize(): No match media query. mediaQuery provided is ' + mediaQuery);
258            }
259
260            /* For modern browsers, use native matchMedia.addListener */
261            if(typeof matchMedia === 'function' && matchMedia.addListener) {
262                var matchMediaObj = matchMedia(_mediaQuery);
263
264                callback(matchMediaObj.matches);
265
266                matchMediaObj.addListener(function(mq) {
267                    callback(mq.matches);
268                });
269            /* For IE9, simulate the matchMedia.addListener behaviour using
270             * a resize handler. */
271            } else if(!self.browser.lt9) {
272                var mediaQueryProps = self.mediaQueriesIE9[mediaQuery];
273                if(typeof mediaQueryProps === 'undefined') return;
274
275                var currentScreen,
276                    getCurrentScreen;
277
278                if(mediaQueryProps.method === 'min-width') {
279                    getCurrentScreen = function() {
280                        return ($(window).width() >= mediaQueryProps.size);
281                    };
282                } else if(mediaQueryProps.method === 'max-width') {
283                    getCurrentScreen = function() {
284                        return ($(window).width() <= mediaQueryProps.size);
285                    };
286                }
287
288                currentScreen = getCurrentScreen();
289
290                callback(currentScreen);
291
292                $(window).on('resize.watchMedia', self.utils.throttle(function() {
293                    if(currentScreen !== getCurrentScreen()) {
294                        currentScreen = !currentScreen;
295                        callback(currentScreen);
296                    }
297                }, 100));
298            /* For legacy browsers, only run the functions for medium
299             * screens and above. */
300            } else {
301                if(mediaQuery === 'small' || mediaQuery === 'xsmall') {
302                    callback(false);
303                } else {
304                    callback(true);
305                }
306            }
307        }
308        /* End watchSize */
309    });
310
311    homebrew.watchSize('small', function(isMediumScreen) {
312        homebrew.screenSize.small = isMediumScreen;
313        homebrew.screenSize.medium = !isMediumScreen;
314    });
315
316    homebrew.watchSize('large', function(isLargeScreen) {
317        homebrew.screenSize.medium = !isLargeScreen;
318        homebrew.screenSize.large = isLargeScreen;
319    });
320
321
322
323
324    /**---- Carouselify ---**\
325     *  Turn a list of elements into a rotating carousel.
326     *
327     *  Arguments:
328     *      $('.carousel').carouselify({
329     *          items : '.carousel-item',
330     *          activeItem : null,
331     *          classes : {
332     *              active : 'is-active',
333     *              hidden : 'is-hidden'
334     *          },
335     *          loop : true,
336     *          transitions : {
337     *              enable : true,
338     *              classes : {
339     *                  transitionIn : 'is-transitioning-in',
340     *                  transitionOut : 'is-transitioning-out',
341     *                  reverse : 'is-reverse'
342     *              },
343     *              onStart : null,
344     *              onEnd : null
345     *          },
346     *          switchers : {
347     *              enable : true,
348     *              markups : {
349     *                  nextSwitcher : '<div class="carousel__switcher carousel__switcher--next" />',
350     *                  prevSwitcher : '<div class="carousel__switcher carousel__switcher--prev" />'
351     *              }
352     *          },
353     *          pagers : {
354     *              enable : true,
355     *              markup : '<a href="#/" class="carousel__pager" />',
356     *              holder : '<div class="carousel__pagers" />'
357     *          },
358     *          timer : {
359     *              enable : true,
360     *              duration : 10000,
361     *              showBar : true,
362     *              barMarkup : '<div class="carousel__timer-bar" />'
363     *          },
364     *          onSwitch : null
365     *      });
366     *
367     *  - items
368     *      |-- Type: String
369     *      |-- Default: '.carousel-item'
370     *      |-- Pass in the selector of the carousel items.
371     *  - activeItem
372     *      |-- Type: Number
373     *      |-- Default: null
374     *      |-- Pass in the index of the carousel item that should be active
375     *          when the plugin is initialised.
376     *  - classes
377     *      |-- Type: Object
378     *      |-- Pass in an object containing strings of the classes to use.
379     *          Properties in this object:
380     *          - active
381     *              |-- Type: String
382     *              |-- Default: 'is-active'
383     *              |-- The class that will be added onto the active item.
384     *          - hidden
385     *              |-- Type: String
386     *              |-- Default: 'is-hidden'
387     *              |-- The class that will be added onto the switcher
388     *                  buttons when an unloopable carousel is at the first
389     *                  item and the last item. (At the first time, the
390     *                  prevSwitcher button will receive this class, while
391     *                  at the last item, the nextSwitcher button will
392     *                  receive it.)
393     *  - loop
394     *      |-- Type: Boolean
395     *      |-- Default: true
396     *      |-- Allows the carousel to loop back to the first item after it
397     *          reaches the last item and vice versa.
398     *  - transitions
399     *      |-- Type: Object
400     *      |-- Pass in an arguments object. Arguments in this object are:
401     *          - enable
402     *              |-- Type: Boolean
403     *              |-- Default: true
404     *              |-- Pass in `true` to have the plugin attempt to use
405     *                  CSS transitions by toggling the respective classes
406     *                  on the items and leveraging the `transitionEnd`
407     *                  event.
408     *          - classes
409     *              |-- Type: Object
410     *              |-- Pass in an object containing strings of the classes
411     *                  to use. Properties in this object:
412     *                  - transitionIn:
413     *                      |-- Type: String
414     *                      |-- Default: 'is-transitioning-in'
415     *                      |-- The class that will be added onto the next
416     *                          active item to trigger the transition.
417     *                  - transitionOut:
418     *                      |-- Type: String
419     *                      |-- Default: 'is-transitioning-out'
420     *                      |-- The class that will be added onto the current
421     *                          active item to trigger the transition.
422     *                  - reverse:
423     *                      |-- Type: String
424     *                      |-- Default: 'is-reverse'
425     *                      |-- The class that will be added onto both the
426     *                          current and the next active item when the
427     *                          carousel is triggered in the opposite direction.
428     *                          This is useful to trigger a reverse transition.
429     *          - onStart
430     *              |-- Type: Function
431     *              |-- Default: null
432     *              |-- Pass in a callback function to be executed after the
433     *                  plugin adds the general homebrew transition class but
434     *                  before the plugin adds the transitionIn class to the
435     *                  target item. The function will receive two arguments:
436     *                  (1) the jQuery object of the item that the carousel
437     *                      is switching to, and
438     *                  (2) the jQuery object of the item that the carousel
439     *                      is switching away from.
440     *          - onEnd
441     *              |-- Type: Function
442     *              |-- Default: null
443     *              |-- Pass in a callback function to be executed after the
444     *                  transition completes and the plugin removes all
445     *                  transition-related classes from the items. The
446     *                  function will receive two arguments:
447     *                  (1) the jQuery object of the item that the carousel
448     *                      is switching to, and
449     *                  (2) the jQuery object of the item that the carousel
450     *                      is switching away from.
451     *  - switchers
452     *      |-- Type: Object
453     *      |-- Pass in an arguments object. Arguments in this object are:
454     *          - enable
455     *              |-- Type: Boolean
456     *              |-- Default: true
457     *              |-- Option to have the plugin use switcher buttons.
458     *                  Switcher buttons make the carousel rotate between the
459     *                  next and previous items.
460     *          - markups
461     *              |-- Type: Object
462     *              |-- Pass in an object containing strings of the markups
463     *                  to use. These markup strings have TWO uses:
464     *                  (1) the plugin will extract the classes in the markup
465     *                      string and use it to find any pre-existing switcher
466     *                      buttons in the carousel; if no pre-existing
467     *                      switcher buttons are found, then...
468     *                  (2) the plugin will create its own switcher buttons
469     *                      using the provided markup.
470     *              |-- Properties in this object:
471     *                  - nextSwitcher:
472     *                      |-- Type: String
473     *                      |-- Default: ''<div class="carousel-switcher carousel-switcher--next" />'
474     *                      |-- The markup to be used for the switcher that
475     *                          rotates the carousel to the next item.
476     *                  - prevSwitcher:
477     *                      |-- Type: String
478     *                      |-- Default: ''<div class="carousel-switcher carousel-switcher--prev" />'
479     *                      |-- The markup to be used for the switcher that
480     *                          rotates the carousel to the previous item.
481     *  - pagers
482     *      |-- Type: Object
483     *      |-- Pass in an arguments object. Arguments in this object are:
484     *          - enable
485     *              |-- Type: Boolean
486     *              |-- Default: true
487     *              |-- Option to have the plugin use pager buttons.
488     *                  Pager buttons make the carousel rotate directly to the
489     *                  corresponding item.
490     *          - markup
491     *              |-- Type: String
492     *              |-- Default: '<div class="carousel__pager" />'
493     *              |-- Pass in the markup string to be used for pagers.
494     *              |-- Markup strings have TWO uses:
495     *                  (1) the plugin will extract the classes in the markup
496     *                      string and use it to find any pre-existing pager
497     *                      buttons in the carousel; if no pre-existing
498     *                      pager buttons are found, then...
499     *                  (2) the plugin will create its own pager buttons
500     *                      using the provided markup.
501     *          - holder
502     *              |-- Type: String
503     *              |-- Default: '<div class="carousel__pagers" />'
504     *              |-- Pass in the markup string to use for the holder of the
505     *                  holders. If a holder is used, then the plugin will look
506     *                  for pagers within the provided holders. 
507     *              |-- Pass in `null` or an empty string to refrain from using
508     *                  a holder. If a holder isn't used, then the plugin will
509     *                  search for pagers within the carousel element.
510     *              |-- Markup strings have TWO uses:
511     *                  (1) the plugin will extract the classes in the markup
512     *                      string and use it to find any pre-existing pager
513     *                      buttons in the carousel; if no pre-existing
514     *                      pager buttons are found, then...
515     *                  (2) the plugin will create its own pager buttons
516     *                      using the provided markup.
517     *  - timer
518     *      |-- Type: Object
519     *      |-- Pass in an arguments object. Arguments in this object are:
520     *          - enable
521     *              |-- Type: Boolean
522     *              |-- Default: true
523     *              |-- Option to have the plugin automatically rotate through
524     *                  the items based on a timer.
525     *          - duration
526     *              |-- Type: Number
527     *              |-- Default: 10000
528     *              |-- Determines the delay between each rotation. A larger
529     *                  number would cause the carousel to rotate less often.
530     *  - onSwitch
531     *      |-- Type: Function
532     *      |-- Default: null
533     *      |-- Pass in a callback function to be executed right before the
534     *          carousel switches between items. The function will receive two
535     *          arguments:
536     *          (1) the jQuery object of the item that the carousel is
537     *              switching to, and
538     *          (2) the jQuery object of the item that the carousel is
539     *              switching away from.
540     */
541    homebrew.Carousel = function(el, args) {
542        if(!el) return;
543        this.init(el, args);
544    };
545
546    $.extend(homebrew.Carousel.prototype, {
547        name : 'carouselify',
548
549        options : {
550            items : '.carousel-item',
551
552            activeItem : null,
553
554            classes : {
555                active : 'is-active',
556                hidden : 'is-hidden'
557            },
558
559            loop : true,
560
561            transitions : {
562                enable : true,
563                classes : {
564                    transitionIn : 'is-transitioning-in',
565                    transitionOut : 'is-transitioning-out',
566                    reverse : 'is-reverse'
567                },
568                onStart : null,
569                onEnd : null
570            },
571
572            switchers : {
573                enable : true,
574                markups : {
575                    nextSwitcher : '<div class="carousel__switcher carousel__switcher--next" />',
576                    prevSwitcher : '<div class="carousel__switcher carousel__switcher--prev" />'
577                }
578            },
579
580            pagers : {
581                enable : true,
582                markup : '<a href="#/" class="carousel__pager" />',
583                holder : '<div class="carousel__pagers" />'
584            },
585
586            timer : {
587                enable : true,
588                duration : 10000,
589                showBar : true,
590                barMarkup : '<div class="carousel__timer-bar" />'
591            },
592
593            onSwitch : null
594        },
595
596        init : function(el, args) {
597            var instance = this,
598                options = $.extend(true, {}, instance.options, args);
599
600            if(typeof options.items !== 'string') {
601                console.error('$.fn.carouselify: Expecting String type from `items` argument. Please check:');
602                console.log(options.items);
603                return;
604            }
605
606            var $el = $(el),
607                $items = $el.find(options.items);
608
609            if(!$items.length) return;
610
611            instance.$el = $el;
612            instance.$items = $items;
613            instance.totalItems = $items.length;
614            instance.options = options;
615
616            if(typeof options.activeItem === 'number') {
617                $items.eq(instance.activeItem).addClass(options.classes.active);
618                instance.activeItem = options.activeItem;
619            } else if(instance.$items.filter(options.classes.active).length) {
620                instance.activeItem = instance.$items.filter(options.classes.active).index();
621            } else {
622                instance.activeItem = 0;
623            }
624
625            if(options.switchers.enable) {
626                var $nextSwitcher,
627                    nextSwitcherClasses = homebrew.getClassFromHTMLString(options.switchers.markups.nextSwitcher);
628
629                if(nextSwitcherClasses.length) {
630                    $nextSwitcher = $el.find('.' + nextSwitcherClasses.join('.'));
631                }
632                if(!$nextSwitcher || !$nextSwitcher.length) {
633                    $nextSwitcher = $(options.switchers.markups.nextSwitcher);
634                    $el.append($prevSwitcher);
635                    instance.addDestroyable($nextSwitcher);
636                }
637
638                instance.$nextSwitcher = $nextSwitcher;
639
640                var $prevSwitcher,
641                    prevSwitcherClasses = homebrew.getClassFromHTMLString(options.switchers.markups.prevSwitcher);
642
643                if(prevSwitcherClasses.length) {
644                    $prevSwitcher = $el.find('.' + prevSwitcherClasses.join('.'));
645                }
646                if(!$prevSwitcher || !$prevSwitcher.length) {
647                    $prevSwitcher = $(options.switchers.markups.prevSwitcher);
648                    $el.prepend($prevSwitcher);
649                    instance.addDestroyable($prevSwitcher);
650                }
651
652                instance.$prevSwitcher = $prevSwitcher;
653            }
654
655            if(options.pagers.enable) {
656                var $holders,
657                    $pagers,
658                    pagerClasses = homebrew.getClassFromHTMLString(options.pagers.markup);
659
660                if(typeof options.pagers.holder === 'string'
661                && options.pagers.holder !== '') {
662                    var holderClasses = homebrew.getClassFromHTMLString(options.pagers.holder);
663
664                    if(holderClasses.length) {
665                        $holders = $el.find('.' + holderClasses.join('.'));
666                    }
667                    if(!$holders || !$holders.length) {
668                        $holders = $(options.pagers.holder).appendTo($el);
669                    }
670                }
671
672                if(!$holders || !$holders.length) {
673                    $holders = $el;
674                }
675
676                if(pagerClasses.length) {
677                    $pagers = $holders.find('.' + pagerClasses.join('.'));
678                }
679
680                if(!$pagers || !$pagers.length) {
681                    var finalPagersStr = [];
682                    for(var i = instance.totalItems-1; i > -1; i--) {
683                        finalPagersStr.push(options.pagers.markup);
684                    }
685                    $pagers = $(finalPagersStr.join('')).appendTo($holders);
686                    instance.addDestroyable($pagers);
687                }
688
689                $pagers.eq(instance.activeItem).addClass(options.classes.active);
690                instance.$pagers = $pagers;
691            }
692
693            if(options.timer.enable && options.showBar) {
694                var $timerBar,
695                    timerBarClasses = homebrew.getClassFromHTMLString(options.timer.barMarkup);
696
697                if(timerBarClasses.length) {
698                    $timerBar = $el.find('.' + timerBarClasses.join('.'));
699                }
700                if(!$timerBar || !$timerBar.length) {
701                    $timerBar = $(options.timer.barMarkup);
702                    instance.addDestroyable($timerBar);
703                }
704
705                instance.$timerBar = $timerBar;
706
707                if(typeof options.timer.duration !== 'number') {
708                    console.error('$.fn.carouselify(): Expecting a number for the timer duration. Please check:');
709                    console.log(options.timer.duration);
710                    console.error('Reverting back to default.');
711
712                    options.timer.duration = homebrew.Carousel.prototype.options.timer.duration;
713                }
714            }
715
716            instance.enable();
717
718            $.data(el, instance.name, instance);
719
720            return instance;
721        },
722
723        enable : function() {
724            var instance = this;
725
726            if(instance.$nextSwitcher && instance.$prevSwitcher) {
727                instance.$nextSwitcher.on('click.' + instance.name, function() {
728                    instance.switchTo('next');
729                });
730
731                instance.$prevSwitcher.on('click.' + instance.name, function() {
732                    instance.switchTo('prev');
733                });
734            }
735
736            if(instance.$pagers) {
737                instance.$pagers.each(function(index) {
738                    $(this).on('click.' + instance.name, function(e) {
739                        e.preventDefault();
740                        instance.switchTo(index);
741                    });
742                });
743            }
744
745            instance.runTimer();
746
747            if(instance.options.timer.enable) {
748                $(window)
749                    .on('focus.' + instance.name, function() {
750                        instance.runTimer();
751                    })
752                    .on('blur.' + instance.name, function() {
753                        clearTimeout(instance.timer);
754                    });
755            }
756
757            return instance;
758        },
759
760        disable : function() {
761            var instance = this;
762
763            clearTimeout(instance.timer);
764
765            if(instance.$nextSwitcher && instance.$prevSwitcher) {
766                instance.$nextSwitcher.add(instance.$prevSwitcher).off('click.'  + instance.name);
767            }
768
769            if(instance.$pagers) {
770                instance.$pagers.off('click.' + instance.name);
771            }
772
773            return instance;
774        },
775
776        switchTo : function(itemIndex) {
777            var instance = this;
778            if(instance.isSwitching === true) return instance;
779            instance.isSwitching = true;
780            var activeItem = instance.activeItem,
781                options = instance.options;
782
783            if(typeof itemIndex === 'string') {
784                switch(itemIndex) {
785                    case 'next' :
786                        activeItem++;
787
788                        if(activeItem >= instance.totalItems) {
789                            activeItem = 0;
790                        }
791                    break;
792
793                    case 'prev' :
794                        activeItem--;
795
796                        if(activeItem < 0) {
797                            activeItem = instance.totalItems - 1;
798                        }
799                    break;
800
801                    default:
802                        console.error('Homebrew.Carousel.switchTo(): Unrecognised string method `' + itemIndex + '`.');
803                        return;
804                    break;
805                }
806            } else if(typeof itemIndex === 'number') {
807                activeItem = itemIndex;
808            } else {
809                console.error('Homebrew.Carousel.switchTo(): Unsupported argument type: `' + typeof itemIndex + '`.');
810                console.log(itemIndex);
811                return;
812            }
813
814            if(activeItem === instance.activeItem) return instance;
815
816            var $currentItem = instance.$items.eq(activeItem),
817                $prevItem = instance.$items.eq(instance.activeItem),
818                activeClass = options.classes.active;
819
820            if(typeof options.onSwitch === 'function') {
821                options.onSwitch.call(instance.$el[0], $currentItem, $prevItem);
822            }
823
824            instance.runTimer();
825
826            if(options.transitions.enable) {
827                var transitionEvent = homebrew.events.transitionEnd,
828                    transitionClass = homebrew.classes.transitionable,
829                    transitionInClass = options.transitions.classes.transitionIn,
830                    transitionOutClass = options.transitions.classes.transitionOut,
831                    reverseClass = options.transitions.classes.reverse;
832
833                instance.$items.removeClass([
834                    reverseClass,
835                    transitionClass
836                ].join(' '));
837
838                instance.$items.not($prevItem).removeClass(activeClass);
839
840                $currentItem
841                    .one(transitionEvent, function() {
842                        $currentItem
843                            .off(transitionEvent)
844                            .add($prevItem)
845                                .removeClass([
846                                    transitionInClass,
847                                    transitionOutClass,
848                                    reverseClass,
849                                    transitionClass,
850                                    activeClass
851                                ].join(' '))
852                            .end()
853                            .addClass(activeClass);
854
855                        instance.isSwitching = false;
856                        if(typeof options.transitions.onEnd === 'function') {
857                            options.transitions.onEnd.call(instance.$el[0], $currentItem, $prevItem);
858                        }
859                    });
860
861                if(itemIndex === 'prev' && instance.activeItem === 0 && activeItem === instance.totalItems-1
862                || itemIndex !== 'next' && instance.activeItem > activeItem) {
863                    $prevItem.add($currentItem).addClass(reverseClass);
864                }
865
866                setTimeout(function() {
867                    $prevItem.add($currentItem).addClass(transitionClass);
868                    $prevItem.addClass(transitionOutClass);
869                    $currentItem.addClass(transitionInClass);
870
871                    if(typeof options.transitions.onStart === 'function') {
872                        options.transitions.onStart.call(instance.$el[0], $currentItem, $prevItem);
873                    }
874                }, 10);
875            } else {
876                instance.$items.removeClass(activeClass);
877                $currentItem.addClass(activeClass);
878                instance.isSwitching = false;
879            }
880
881            instance.$pagers.removeClass(activeClass)
882                            .eq(activeItem)
883                                .addClass(activeClass);
884
885            instance.activeItem = activeItem;
886
887            return instance;
888        },
889
890        runTimer : function() {
891            var instance = this,
892                options = instance.options;
893
894            if(!options.timer.enable) return instance;
895
896            clearTimeout(instance.timer);
897
898            if(options.loop || activeItem < instance.totalItems-1) {
899                instance.timer = setTimeout(function() {
900                    instance.switchTo('next');
901                }, options.timer.duration);
902            }
903
904            return instance;
905        },
906
907        addDestroyable : function($obj) {
908            var instance = this;
909            if(!instance.destroyables) instance.destroyables = [];
910            instance.destroyables.push($obj);
911            return instance;
912        },
913
914        destroy : function() {
915            var instance = this;
916
917            instance.disable();
918
919            if(instance.destroyable) {
920                while(instance.destroyable.length) {
921                    instance.destoryable.shift().remove();
922                }
923            }
924
925            $.removeData(instance.$el[0], instance.name);
926        }
927    });
928
929    homebrew.makePlugin(homebrew.Carousel);
930
931
932    /**---- Height Syncer ----**\
933     *  Sync the height of a collection of items.
934     *
935     *  Arguments:
936     *      $('.items-holder').heightSyncify({
937     *          items : [
938     *              $('.items-holder').find('.item')
939     *          ]
940     *      });
941     *
942     *  - items
943     *      |-- Type: Array
944     *      |-- Pass in an Array of the items to sync. The Array can consist
945     *          of either selector strings or jQuery Objects. If selector
946     *          strings are provided, the plugin will try to select the targets
947     *          within the currently iterated element. The sequence determines
948     *          which set of items' height get synced first. This is important
949     *          if you need to sync two items that are of parent-child relation
950     *          (you would most likely want to sync the children's height first
951     *          so that they also count towards the parents' height)
952     *
953     *  Methods:
954     *  - init
955     *      |-- $('.items-holder').heightSyncify();
956     *      |-- Initialise the plugin. Once the plugin is initialised, you can
957     *          pass in a string to trigger a specific method on it.
958     *      |-- Accepts an arguments object. Refer to the above for the list
959     *          of arguments available.
960     *  - update
961     *      |-- $('.items-holder').heightSyncify();
962     *      |-- Init the plugin again to update its options.
963     *      |-- Accepts an arguments object. Refer to the above for the list
964     *          of arguments available.
965     *  - enable
966     *      |-- $('.items-holder').heightSyncify('enable');
967     *      |-- Automatically re-sync the height when the window is resized.
968     *  - disable
969     *      |-- $('.items-holder').heightSyncify('disable');
970     *      |-- Stop re-syncing the height when the window is resized.
971     *  - sync
972     *      |-- $('.items-holder').heightSyncify('sync');
973     *      |-- Sync the items height.
974     *  - destroy
975     *      |-- $('.items-holder').heightSyncify('destroy');
976     *      |-- Removes the plugin.
977     */
978    homebrew.HeightSyncer = function(el, args) {
979        if(!el) return;
980        this.init(el, args);
981    };
982    $.extend(homebrew.HeightSyncer.prototype, {
983        name : 'heightSyncify',
984
985        init : function(el, args) {
986            args = args || {};
987            var instance = this;
988            instance.$el = $(el);
989            instance.uniqueID = homebrew.generateUniqueID();
990            instance.update(args)
991                    .enable();
992            $.data(el, instance.name, instance);
993            return instance;
994        },
995        update : function(args) {
996            args = args || {};
997            var instance = this;
998            instance.items = args.items.slice(0);
999            for(var i = instance.items.length-1; i > -1; i--) {
1000                if(typeof instance.items[i] === 'string') {
1001                    instance.items[i] = instance.$el.find(instance.items[i]);
1002                }
1003                instance.items[i].find('img').each(function() {
1004                    if(this.complete) return;
1005                    $(this).one('load', function() {
1006                        clearTimeout(instance.timer);
1007                        instance.timer = setTimeout(function() {
1008                            instance.sync();
1009                        }, 100);
1010                    });
1011                });
1012            }
1013            if(args.options) {
1014                $.extend(instance, args.options);
1015            }
1016            instance.sync();
1017            return instance;
1018        },
1019        enable : function() {
1020            var instance = this;
1021            if(instance.enabled) return instance;
1022            instance.enabled = true;
1023            $(window).on('resize.' + instance.uniqueID, homebrew.utils.throttle(function() {
1024                instance.sync();
1025            }, 30));
1026            return instance;
1027        },
1028        disable : function() {
1029            this.enabled = false;
1030            $(window).off('.' + this.uniqueID);
1031            return this;
1032        },
1033        sync : function() {
1034            var instance = this,
1035                $currentCollection,
1036                $items = $(),
1037                leftOffset,
1038                currentLeftThreshold,
1039                heights,
1040                tallestHeight;
1041
1042            if(!instance.items || !instance.items.length) return instance;
1043
1044                for(var i = 0, ii = instance.items.length; i < ii; i++) {
1045                    leftOffset = currentLeftThreshold = -9999;
1046                $currentCollection = instance.items[i];
1047
1048                $currentCollection.each(function(index) {
1049                    $items = $items.add($(this));
1050                        leftOffset = $(this).offset().left;
1051                    if(!$currentCollection.eq(index+1).length 
1052                    || $currentCollection.eq(index+1).offset().left <= leftOffset) {
1053                        heights = [];
1054                        $items.css('height', '');
1055
1056                        $items.each(function() {
1057                            heights.push($(this).outerHeight());
1058                    });
1059
1060                        tallestHeight = Math.max.apply(null, heights);
1061
1062                        $items.outerHeight(tallestHeight);
1063                        $items = $();
1064                        }
1065                    });
1066                }
1067                
1068            instance.$el.trigger('afterSync');
1069
1070            return instance;
1071        },
1072        destroy : function() {
1073            var instance = this;
1074            while(instance.items.length) {
1075                instance.items.shift().css('height', '');
1076            }
1077            instance.disable();
1078            $.removeData(instance.$el[0], instance.name);
1079        }
1080    });
1081    homebrew.makePlugin(homebrew.HeightSyncer);
1082    /**---- Tooltipify ---**\
1083     *  Initialise a custom tooltip on the element.
1084     *
1085     *  Arguments:
1086     *      $('.my-element').tooltipify({
1087     *          appendTo : 'body',
1088     *          classes : {
1089     *              active : 'is-active'
1090     *          },
1091     *          markups : {
1092     *              tooltip : '<div class="tooltip" />'
1093     *          },
1094     *          contents : function() {
1095     *              var instance = this,
1096     *                  title = instance.$el.attr('title');
1097     *
1098     *              instance.$el.data(instance.name + '-title', title);
1099     *              instance.$el.removeAttr('title');
1100     *
1101     *              return title;
1102     *          },
1103     *          transitions : {
1104     *              enable : true,
1105     *              classes : {
1106     *                  transitionIn : 'is-transitioning-in',
1107     *                  transitionOut : 'is-transitioning-out'
1108     *              }
1109     *          },
1110     *          hoverDuration : 400
1111     *      });
1112     *
1113     *  - appendTo
1114     *      |-- Type: Object | String
1115     *      |-- Default: 'body'
1116     *      |-- Determines where the tooltip will be appended to when it is
1117     *          is created to be shown.
1118     *      |-- You can pass in either a string, a Node element or a jQuery
1119     *          object. If what you passed in results in nothing being selected,
1120     *          the plugin will fallback to appending to the <body> element.
1121     *  - classes
1122     *      |-- An Object that contains strings of general classes to be used
1123     *          in the plugin. The classes are:
1124     *          - active
1125     *              |-- Default: 'is-active'
1126     *              |-- The class that will be added to the tooltip after it is
1127     *                  created and shown.
1128     *  - markups
1129     *      |-- An Object that contains strings of markups to be used in
1130     *          the plugin. The markups are:
1131     *          - tooltip
1132     *              |-- Default: '<div class="tooltip" />'
1133     *              |-- The markup used to contain the tooltip.
1134     *  - contents
1135     *      |-- Type: Function | String
1136     *      |-- Default: function() {
1137     *              var instance = this,
1138     *                  title = instance.$el.attr('title');
1139     *
1140     *              instance.$el.data(instance.name + '-title', title);
1141     *              instance.$el.removeAttr('title');
1142     *
1143     *              return title;
1144     *          }
1145     *      |-- Determines the content of the tooltip. There are two ways
1146     *          to set the content:
1147     *          (1) Use a function that returns the content string when it is
1148     *              run. This is useful if the element itself has a title
1149     *              attribute, as you can use this function to save the value
1150     *              and then proceed to remove the attribute (to prevent the
1151     *              default tooltip),
1152     *              OR
1153     *          (2) Directly pass in the content string itself.
1154     *      |-- The content string is inserted using the `.html()` method.
1155     *  - hoverDuration
1156     *      |-- Type: Number
1157     *      |-- Default: 400
1158     *      |-- Determines how long the mouse needs to hover over the element
1159     *          in order to trigger the tooltip. Lower number means a shorter
1160     *          duration to trigger.
1161     *  - transitions
1162     *      |-- An Object that contains the various properties to be used
1163     *          in the plugin. The properties are:
1164     *          - enable
1165     *              |-- Type: Boolean
1166     *              |-- Default: true
1167     *              |-- Determines whether or not the plugin should attempt
1168     *                  to leverage CSS transitions. 
1169     *          - classes
1170     *              |-- An Object that contains strings of transition classes
1171     *                  to be used in the plugin. The classes are:
1172     *                  - transition
1173     *                      |-- Default: homebrew.classes.transition
1174     *                      |-- This class is used to enable the transition
1175     *                          effect on the element.
1176     *                  - transitionIn
1177     *                      |-- Default: 'is-transitioning-in'
1178     *                      |-- This class is used to make the tooltip
1179     *                          transition in.
1180     *                  - transitionOut
1181     *                      |-- Default: 'is-transitioning-out'
1182     *                      |-- This class is used to make the tooltip
1183     *                          transition out.
1184     */
1185    homebrew.Tooltip = function(el, args) {
1186        if(!el) return;
1187        this.init(el, args);
1188    };
1189    $.extend(homebrew.Tooltip.prototype, {
1190        name : 'tooltipify',
1191        options : {
1192            appendTo : 'body',
1193            markups : {
1194                tooltip : '<div class="tooltip" />',
1195                closer : '<a href="#/" class="tooltip__closer" />'
1196            },
1197            classes : {
1198                active : 'is-active'
1199            },
1200            contents : function() {
1201                var instance = this,
1202                    title = instance.$el.attr('title');
1203                instance.$el.data(instance.name + '-title', title);
1204                instance.$el.removeAttr('title');
1205                return title;
1206            },
1207            hoverDuration : 400,
1208            transitions : {
1209                enable : true,
1210                classes : {
1211                    transition : homebrew.classes.transitionable,
1212                    transitionIn : 'is-transitioning-in',
1213                    transitionOut : 'is-transitioning-out'
1214                }
1215            }
1216        },
1217        init : function(el, args) {
1218            var instance = this,
1219                options = $.extend({}, instance.options, args);
1220            instance.$el = $(el).addClass(instance.name);
1221            instance.uniqueID = homebrew.generateUniqueID();
1222            instance.options = options;
1223            instance.$appendTo = $(options.appendTo);
1224            if(!instance.$appendTo.length) {
1225                instance.$appendTo = 'body';
1226            }
1227            if(typeof options.contents === 'function') {
1228                options.contents = options.contents.call(instance);
1229            }
1230            instance.enable();
1231            $.data(el, instance.name, instance);
1232        },
1233        enable : function() {
1234            var instance = this,
1235                $el = instance.$el,
1236                options = instance.options;
1237            $el.on('click.' + instance.uniqueID, function(e) {
1238                e.preventDefault();
1239                clearTimeout(instance.timer);
1240                instance.open();
1241            });
1242            $el.on('mouseenter.' + instance.uniqueID, function(e) {
1243                clearTimeout(instance.timer);
1244                instance.timer = setTimeout(function() {
1245                    instance.open();
1246                }, options.hoverDuration);
1247            });
1248            $el.on('mouseleave.' + instance.uniqueID, function(e) {
1249                clearTimeout(instance.timer);
1250                instance.timer = setTimeout(function() {
1251                    instance.close();
1252                }, options.hoverDuration);
1253            });
1254            return instance;
1255        },
1256        disable : function() {
1257            this.$el.off('.' + instance.uniqueID);
1258            return this;
1259        },
1260        getAltRender : function() {
1261            return Modernizr.touch && homebrew.screenSize.small;
1262        },
1263        open : function() {
1264            var instance = this;
1265            if(instance.$tooltip) return;
1266            var $el = instance.$el,
1267                options = instance.options,
1268                $tooltip = $(options.markups.tooltip).appendTo(instance.$appendTo).html(options.contents),
1269                activeClass = options.classes.active,
1270                altRender = instance.getAltRender();
1271            instance.$tooltip = $tooltip;
1272            if(altRender) {
1273                var $closer = $('.' + homebrew.getClassFromHTMLString(options.markups.closer).join('.'));
1274                if(!$closer.length) {
1275                    $closer = $(options.markups.closer).prependTo($tooltip);
1276                }
1277                $closer.on('click', function(e) {
1278                    e.preventDefault();
1279                    instance.close();
1280                });
1281            } else {
1282                if(homebrew.screenSize.small) {
1283                    if($tooltip.outerWidth() + parseInt($tooltip.css('margin-left'), 10) === $(window).width()) {
1284                        $tooltip.css({
1285                            left : '0px',
1286                            marginLeft : '0px'
1287                        });
1288                    } else if($el.offset().left + $tooltip.outerWidth() > $(window).width()) {
1289                        $tooltip.css('right', '0px');
1290                    } else {
1291                        $tooltip
1292                            .css('left', $el.offset().left + 'px');
1293                    }
1294                } else {
1295                    if($el.offset().left + $tooltip.outerWidth() > $(window).width()) {
1296                        $tooltip
1297                            .addClass('is-opposite')
1298                            .css('right', '0px');
1299                    } else {
1300                        $tooltip
1301                            .css('left', $el.offset().left + 'px');
1302                    }
1303                }
1304                $tooltip.css('top', $el.offset().top - $('#mainContent').offset().top - $tooltip.outerHeight() + 'px')
1305            }
1306            if(options.transitions.enable) {
1307                var transitionEvent = homebrew.events.transitionEnd,
1308                    transitionClass = homebrew.classes.transitionable,
1309                    transitionInClass = options.transitions.classes.transitionIn;
1310                $tooltip
1311                    .one(transitionEvent, function() {
1312                        $tooltip.removeClass([transitionClass, transitionInClass].join(' '))
1313                                .addClass(activeClass);
1314                        $(document).on([
1315                            'click.fauxBlur.', instance.uniqueID,
1316                            ' touchstart.fauxBlur.', instance.uniqueID
1317                        ].join(''), function(e) {
1318                            if(!$tooltip.is(e.target) && $tooltip.has(e.target).length === 0) {
1319                                instance.close();
1320                            }
1321                        });
1322                    })
1323                    .on({
1324                        mouseenter : function() {
1325                            clearTimeout(instance.timer);
1326                        },
1327                        mouseleave : function() {
1328                            clearTimeout(instance.timer);
1329                            instance.timer = setTimeout(function() {
1330                                instance.close();
1331                            }, options.hoverDuration);
1332                        }
1333                    });
1334                setTimeout(function() {
1335                    $tooltip.addClass(transitionClass);
1336                    if(altRender) {
1337                        $tooltip.css('margin-top', -$tooltip.outerHeight() + 'px');
1338                    } else {
1339                        $tooltip.addClass(transitionInClass);
1340                    }
1341                }, 10);
1342            } else {
1343                $tooltip.addClass(activeClass);
1344            }
1345            return instance;
1346        },
1347        close : function(args) {
1348            args = args || {};
1349            var instance = this,
1350                $tooltip = instance.$tooltip;
1351            if(!$tooltip) return;
1352            var options = instance.options,
1353                activeClass = options.classes.active,
1354                altRender = instance.getAltRender();
1355            $(document).off('.fauxBlur.' + instance.uniqueID);
1356            if(options.transitions.enable) {
1357                var transitionEvent = homebrew.events.transitionEnd,
1358                    transitionClass = homebrew.classes.transitionable,
1359                    transitionOutClass = options.transitions.classes.transitionOut;
1360                $tooltip
1361                    .trigger(transitionEvent)
1362                    .off('mouseenter mouseleave')
1363                    .one(transitionEvent, function() {
1364                        $tooltip.removeClass([transitionClass, transitionOutClass].join(' '))
1365                                .removeClass(activeClass)
1366                                .remove();
1367                        instance.$tooltip = null;
1368                        if(typeof args.onCloseEnd === 'function') {
1369                            args.onCloseEnd();
1370                        }
1371                    });
1372                setTimeout(function() {
1373                    $tooltip.addClass(transitionClass);
1374                    if(altRender) {
1375                        $tooltip.css('margin-top', '');
1376                    } else {
1377                        $tooltip.addClass(transitionOutClass);
1378                    }
1379                }, 10);
1380            } else {
1381                $tooltip.removeClass(activeClass);
1382                instance.$tooltip = null;
1383                if(typeof args.onClose === 'function') {
1384                    args.onClose();
1385                }
1386            }
1387            return instance;
1388        },
1389        destroy : function() {
1390            var instance = this,
1391                el = instance.$el[0],
1392                options = instance.options;
1393            instance.disable().close();
1394            $.removeData(el, instance.name);
1395            if($.data(el, instance.name + '-title')) {
1396                el.title = $.data(el, instance.name + '-title');
1397                $.removeData(el, instance.name + '-title');
1398            }
1399        }
1400    });
1401    homebrew.makePlugin(homebrew.Tooltip);
1402    /* Extend jQuery with our custom functions built in plugins format. */
1403    $.fn.extend({
1404        /**---- Dropdownify ----**\
1405         * Call this `$('select').dropdownify()` function on the <select> nodes that
1406         * you want to have custom visuals on.
1407         
1408         * Sample <select> node:
1409         
1410            <select id="foobar">
1411                <!-- All your options -->
1412            </select>
1413            
1414         * This will result in:
1415         
1416            <div class="dropdown">
1417                <select id="foobar">
1418                    <!-- All your options -->
1419                </select>
1420                
1421                <div class="dropdown-btn">
1422                    <div class="dropdown-arrow" />
1423                    <span>Label</span>
1424                </div>
1425            </div>
1426         
1427         * If there are any attributes that you need to set onto the
1428         * resulting dropdown node, you can do it through
1429         * `data-dropdown-attributes`. The value has to be a valid
1430         * JSON format, as shown below:
1431         
1432            <select data-dropdown-attributes='{ "class" : "sample-class", "style" : "width: 300px;", "id" : "sampleID" }' id="foobar">
1433                <!-- All your options -->
1434            </select>
1435
1436         * Alternatively, you can wrap the <select> with a parent node
1437         * (preferably a <span> node) and add your desired attributes on
1438         * that node. Then, instead of running dropdownify on the <select>
1439         * node, you run it on the parent node instead.
1440     
1441         * Example of wrapping with a parent node:
1442
1443            <span class="sample-class" style="width: 300px;" id="sampleID">
1444                <select id="foobar">
1445                    <!-- All your options -->
1446                </select>
1447            </span>
1448
1449            $('#sampleID').dropdownify();
1450            
1451         * Both of the above methods will result in this:
1452         
1453            <div class="sample-class dropdown" style="width: 300px;" id="sampleID">
1454                <select id="foobar">
1455                    <!-- All your options -->
1456                </select>
1457                
1458                <div class="dropdown-btn">
1459                    <div class="dropdown-arrow" />
1460                    <span>Label</span>
1461                </div>
1462            </div>
1463         */
1464        dropdownify : function() {
1465            var pluginName = 'dropdownify',
1466                options = {
1467                    markups : {
1468                        holder : '<div />',
1469                        button : '<div />',
1470                        arrow : '<div><i /></div>',
1471                        label : '<span />'
1472                    },
1473                    classes : {
1474                        holder : 'dropdown',
1475                        button : 'dropdown-btn',
1476                        arrow : 'dropdown-arrow',
1477                        label : 'dropdown-label'
1478                    }
1479                },
1480                methods = {
1481                    create: function() {
1482                        /**
1483                         * If these dropdowns are in <form> nodes, then we need to
1484                         * attach a handler to adjust our input visuals whenever
1485                         * the form is reset.
1486                         */
1487                        var forms = this.parents('form');
1488                        
1489                        if(forms.length) {
1490                            forms.off('.' + pluginName).on('reset.' + pluginName, function() {
1491                                var _this = $(this);
1492                                
1493                                setTimeout(function() {
1494                                    _this.find('select').trigger('change');
1495                                }, 25);
1496                            });
1497                        }
1498                        
1499                        this.each(function() {
1500                            var $this = $(this),
1501                                dropdown = ($this.is('select')) ? $this : $this.children('select');
1502                            if(!dropdown.length || typeof dropdown.data(pluginName) !== 'undefined') return;
1503
1504                            var dropdownAttr;
1505                            if($this.is('select')) {
1506                                dropdownAttr = $this.data('dropdown-attributes');
1507                            } else {
1508                                dropdownAttr = $this.getAttributes();
1509                                dropdown.unwrap();
1510                            }
1511                            
1512                            var holder = dropdown.closest('.' + options.classes.holder);
1513                            
1514                            if(holder.length < 1) {
1515                                holder = $(options.markups.holder).addClass(options.classes.holder);
1516                                dropdown.after(holder).prependTo(holder);
1517                            }
1518
1519                            if(typeof dropdownAttr === 'object') {
1520                                $.each(dropdownAttr, function(key, value) {
1521                                    if(key === 'class') {
1522                                        holder.addClass(value);
1523                                    } else {
1524                                        holder.attr(key, value);
1525                                    }
1526                                });
1527                            }
1528
1529                            var btn = holder.find('.' + options.classes.button);
1530                            if(btn.length < 1) btn = $(options.markups.button).addClass(options.classes.button).appendTo(holder);
1531
1532                            var arrow = btn.find('.' + options.classes.arrow);
1533                            if(arrow.length < 1) arrow = $(options.markups.arrow).addClass(options.classes.arrow).appendTo(btn);
1534
1535                            var label = btn.find('.' + options.classes.label);
1536                            if(label.length < 1) label = $(options.markups.label).addClass(options.classes.label).text(dropdown.find('option:checked').text()).appendTo(btn);
1537                            
1538                            dropdown.data(pluginName, {
1539                                activate: function() {
1540                                    var _this = this;
1541
1542                                    dropdown.on('change.' + pluginName, function() {
1543                                        _this.refresh();
1544                                    });
1545
1546                                    return _this;
1547                                },
1548
1549                                deactivate: function() {
1550                                    dropdown.off('.' + pluginName);
1551                                    return this;
1552                                },
1553
1554                                refresh: function() {
1555                                    var checkedText = dropdown.find('option:checked').text();
1556                                    if(!dropdown.data('ignore-asterisk') && checkedText.lastIndexOf('*') === checkedText.length-1) {
1557                                        checkedText = [
1558                                            checkedText.substr(0, checkedText.length-1),
1559                                            '<span class="text-red">*</span>'
1560                                        ].join('');
1561                                    }
1562                                    label.html(checkedText);
1563                                    btn.toggleClass('is-placeholder', Boolean(dropdown.val() === ''));
1564                                    return this;
1565                                },
1566
1567                                destroy: function() {
1568                                    dropdown
1569                                        .insertBefore(holder)
1570                                        .off('.' + pluginName)
1571                                        .removeData(pluginName)
1572                                        .closest('form')
1573                                            .off('.' + pluginName);
1574
1575                                    holder.remove();
1576                                },
1577
1578                                $elems: {
1579                                    arrow: arrow,
1580                                    button: btn,
1581                                    holder: holder,
1582                                    label: label
1583                                }
1584                            });
1585
1586                            dropdown.data(pluginName).activate().refresh();
1587                        });
1588                    },
1589
1590                    activate: function() {
1591                        runElemMethod.call(this, pluginName, 'activate');
1592                    },
1593
1594                    deactivate: function() {
1595                        runElemMethod.call(this, pluginName, 'deactivate');
1596                    },
1597
1598                    refresh: function() {
1599                        runElemMethod.call(this, pluginName, 'refresh');
1600                    },
1601                    
1602                    destroy: function() {
1603                        runElemMethod.call(this, pluginName, 'destroy');
1604                    }
1605                },
1606                _arguments = arguments;
1607
1608            for(var i = _arguments.length-1; i > -1; i--) {
1609                if(typeof _arguments[i] === 'object') $.extend(true, options, _arguments[i]);
1610            }
1611            
1612            if(typeof _arguments[0] === 'string' && typeof methods[_arguments[0]] === 'function') {
1613                methods[_arguments[0]].call(this);
1614            } else {
1615                methods.create.call(this);
1616            }
1617            
1618            return this;
1619        /* End dropdownify */
1620        },
1621
1622
1623        /**---- getAttributes ----**\
1624        
1625         * Call `$('.foo').getAttributes()` to get ALL attributes of the
1626         * element, which is returned as an Object Literal.
1627         
1628         * This is added in to be used by the `$.dropdownify()` function,
1629         * but feel free to use it whenever you need it.
1630         **/
1631        getAttributes : function() {
1632            var elem = this, 
1633                attr = {};
1634
1635            if(elem.length) {
1636                $.each(elem.get(0).attributes, function(v,n) { 
1637                    n = n.nodeName||n.name;
1638                    v = elem.attr(n); // relay on $.fn.attr, it makes some filtering and checks
1639                    if(v !== undefined && v !== false) attr[n] = v;
1640                });
1641            }
1642
1643            return attr;
1644        },
1645
1646
1647        /**---- Inputify ----**\
1648        
1649         * Call this `$('.foo').inputify()` function on the radio/checkboxes that
1650         * you want to have custom visuals on.
1651         
1652         * Sample input node:
1653         
1654            <label for="radio1">
1655                <input type="radio" name="radioGroup" id="radio1" />
1656            </label>
1657         
1658         * Supported input types are `radio` and `checkbox`, as
1659         * only those two require custom visuals so far.
1660         
1661         * `name` attribute is used for radio buttons to group
1662         * them up together, so that only one radio button in that
1663         * group can be checked at a time.
1664
1665         * Ideally, the input node should either (1) be wrapped inside
1666         * a <label> node, or (2) have a <label> that corresponds to the
1667         * ID of the input. If there is no <label> node to use, the script
1668         * will create its own.
1669         */
1670        inputify : function(_options) {
1671            var pluginName = 'inputify',
1672                options = {
1673                    inputIconClass : 'icon--input',
1674                    radioClass : 'icon--radio-btn',
1675                    checkboxClass : 'icon--checkbox',
1676                    checkedClass : 'is-checked',
1677                    hiddenClass : 'hidden-accessible',
1678                    inputVisualMarkupArray : ['<div class="icon ', 'inputIconClass', ' ', 'inputTypeClass', '"><i /></div>'],
1679                    labelMarkupArray : ['<label for="', 'inputID', '">', 'inputVisualMarkupArray', '</label>']
1680                },
1681                methods = {
1682                    create: function() {
1683                        var labels = $('label');
1684                                    
1685                        /**
1686                         * Setup the input visual DOM nodes
1687                         */
1688                        this.each(function(index) {
1689                            var input = $(this);
1690                            if(typeof input.data(pluginName) !== 'undefined') return;
1691
1692                            var inputType = input.attr('type'),
1693                                inputTypeClass = getInputTypeClass(input.attr('type')),
1694                                inputID = input.attr('id'),
1695                                label = labels.filter('[for="' + inputID + '"]');
1696                            
1697                            if(!label.length) label = input.closest('label');
1698
1699                            if(!label.length) {
1700                                if(typeof inputID !== 'string' || inputID === '') {
1701                                    input.attr('id', index + new Date().getTime());
1702                                    inputID = input.attr('id');
1703                                }
1704
1705                                options.labelMarkupArray[1] = inputID;
1706                                options.labelMarkupArray[3] = options.inputVisualMarkupArray.join('');
1707                                label = $(options.labelMarkupArray.join(''));
1708
1709                                label.data(pluginName, {
1710                                    destroyable: true
1711                                });
1712
1713                                input.after(label);
1714                            }
1715
1716                            var inputVisual = label.find('.' + inputTypeClass);
1717
1718                            if(!inputVisual.length) {
1719                                options.inputVisualMarkupArray[1] = options.inputIconClass;
1720                                options.inputVisualMarkupArray[3] = inputTypeClass;
1721                                if(input.is(':checked')) options.inputVisualMarkupArray[3] += ' is-checked';
1722
1723                                if(input.parents('label').length) {
1724                                    $(options.inputVisualMarkupArray.join('')).insertAfter(input);
1725                                } else {
1726                                    $(options.inputVisualMarkupArray.join('')).prependTo(label);
1727                                }
1728                            }
1729
1730                            input
1731                                .addClass(options.hiddenClass)
1732                                .data(pluginName, {
1733                                    '$els' : {
1734                                        'label' : label
1735                                    },
1736
1737                                    refresh : function() {
1738                                        var $inputVisual = this.$els.label.find('.' + options.inputIconClass);
1739
1740                                        if(input.is(':checked')) {
1741                                            $inputVisual.addClass(options.checkedClass);
1742                                        } else {
1743                                            $inputVisual.removeClass(options.checkedClass);
1744                                        }
1745
1746                                        return this;
1747                                    }
1748                                });
1749                        });
1750                        
1751                        /**
1752                         * Setup the change handler for radio buttons.
1753                         *
1754                         * It is more complicated for radios, because if the clicked
1755                         * radio is in a group, only the clicked radio will trigger
1756                         * the change event, the rest of the radios in the group won't.
1757                         * We need to work around this in order to update the custom
1758                         * radio visuals accordingly.
1759                         */
1760                        var radioInputs = this.filter('[type="radio"]');
1761                        
1762                        if(radioInputs.length) {
1763                            /**
1764                             * First, split the radio inputs into groups. This is done by
1765                             * pushing each group's name into an array, making sure
1766                             * that there are no duplicates.
1767                             */
1768                            var radioGroupNames = [];
1769                            
1770                            radioInputs.each(function() {
1771                                var radioInput = $(this),
1772                                    radioInputName = radioInput.attr('name');
1773                            
1774                                if(typeof radioInputName !== 'string') return;
1775
1776                                if(radioGroupNames.length < 1 && radioInputName !== '') {
1777                                    radioGroupNames.push(radioInputName);
1778                                } else {
1779                                    for(var i = radioGroupNames.length-1; i > -1; i--) {
1780                                        if(radioGroupNames[i] == radioInputName) {
1781                                            break;
1782                                        } else if(i === 0) {
1783                                            radioGroupNames.push(radioInputName);
1784                                        }
1785                                    }
1786                                }
1787                            });
1788                            
1789                            /**
1790                             * Now we iterate through each group.
1791                             *
1792                             * If at anytime, a radio input in the group triggers a
1793                             * change, we will loop through all the other radio inputs
1794                             * in the same group to update their visuals accordingly.
1795                             *
1796                             * An IIFE (Immediately Invoked Function Expression) is used
1797                             * so that the variable can be preserved to be used with the
1798                             * event handler.
1799                             */
1800                            for(var i=radioGroupNames.length; i > -1; i--) {
1801                                (function() {
1802                                    var $radioGroupInputs = radioInputs.filter('[name="' + radioGroupNames[i] + '"]');
1803
1804                                    $radioGroupInputs
1805                                        .off('change.' + pluginName)
1806                                        .on('change.' + pluginName, function() {
1807                                            $radioGroupInputs.each(function() {
1808                                                $(this).data(pluginName).refresh();
1809                                            });
1810                                        });
1811                                })();
1812                            }
1813                        }
1814                        
1815                        /**
1816                         * Setup the change handler for checkboxes.
1817                         */
1818                        var checkboxInputs = this.filter('[type="checkbox"]');
1819                        
1820                        if(checkboxInputs.length) {
1821                            checkboxInputs
1822                                .off('change.' + pluginName)
1823                                .on('change.' + pluginName, function() {
1824                                    $(this).data(pluginName).refresh();
1825                                });
1826                        }
1827                        
1828                        /**
1829                         * If these inputs are in <form> nodes, then we need to attach
1830                         * a handler to adjust our input visuals whenever the form
1831                         * is reset.
1832                         */
1833                        var forms = this.closest('form'),
1834                            inputs = this;
1835                        
1836                        if(forms.length) {
1837                            forms
1838                                .off('reset.inputify')
1839                                .on('reset.inputify', function() {
1840                                    setTimeout(function() {
1841                                        inputs.trigger('change');
1842                                    }, 25);
1843                                });
1844                        }
1845                    },
1846                    
1847                    destroy: function() {
1848                        var labels = $('label');
1849                        
1850                        this.each(function() {
1851                            var $this = $(this),
1852                                inputId = $this.attr('id');
1853
1854                            if(typeof label.data(pluginName) !== 'object'
1855                            && label.data(pluginName).destroyable) {
1856                                label.remove();
1857                            }
1858                        });
1859                        
1860                        var forms = this.closest('form');
1861                        
1862                        /* Remove all plugin event handlers from the input elements
1863                         * and their corresponding forms. */
1864                        this.off('.' + pluginName);
1865                        forms.off('.' + pluginName);
1866                    },
1867
1868                    refresh: function() {
1869                        runElemMethod.call(this, pluginName, 'refresh');
1870                    }
1871                },
1872                _arguments = arguments;
1873            
1874            function getInputTypeClass(type) {
1875                switch(type) {
1876                    case 'radio':      return options.radioClass;
1877                    case 'checkbox':   return options.checkboxClass;
1878                    default:           return undefined;
1879                }
1880            }
1881            
1882            for(var i = _arguments.length-1; i > -1; i--) {
1883                if(typeof _arguments[i] === 'object') $.extend(options, _arguments[i]);
1884            }
1885            
1886            if(typeof _arguments[0] === 'string' && typeof methods[_arguments[0]] === 'function') {
1887                methods[_arguments[0]].call(this);
1888            } else {
1889                methods.create.call(this);
1890            }
1891            
1892            return this;
1893        /* End inputify */
1894        },
1895
1896
1897        /**---- Popupify ----**\
1898        
1899         * Call this `$('.foo').popupify()` function on your desired popups and
1900         * popup togglers.
1901         
1902         * Sample popup toggler node:
1903         
1904            <a href="#corporateProfile" class="foo">Toggle popup</a>
1905            
1906            <div id="corporateProfile" />
1907         
1908         * `href` should be a hash, followed by the ID of the target popup.
1909         
1910         * Alternatively, you may pass a function into the `content` parameter to
1911         * programmatically select the element whose contents will be used as the
1912         * popup contents. For example:
1913        
1914            <button type="button" id="popupToggler">Click me to show popup</button>
1915            
1916            <div class="foobar-con">
1917                <div class="hello-world"> ... </div>
1918                <div class="popup-content"> ... </div>
1919            </div>
1920            
1921            <script>
1922                $(function() {
1923                    $('#popupToggler').popupify({
1924                        content : function() {
1925                            // `this` refers to the element currently being iterated over
1926                            return $(this).next().find('.popup-content');
1927                        }
1928                    });
1929                });
1930            </script>
1931         
1932         * While invoking this function, it is possible to specify a `height`
1933         * argument to create popups that have a set height:
1934         
1935            $('.foobar').popupify({ height : 500 });
1936            
1937         * Alternatively, you may also specify the height through the
1938         * `data-popup-height` attribute:
1939         
1940            <a href="#corporateProfile" data-popup-height="500">Toggle popup</a>
1941            
1942         * The height value MUST be a NUMBER.
1943
1944         * If the popup contents is taller than its set height, default
1945         * scrollbars will appear on the popup.
1946         
1947         * If the popup itself is taller than the page, then scrollbars
1948         * will appear on the popup holder.
1949         **/
1950        popupify : function(_options) {
1951            var pluginName = 'popupify',
1952                $body = $('body'),
1953                options = {
1954                    classes : {
1955                        mainPopupHolder : 'main-popup-holder',
1956                        shown : 'is-shown'
1957                    },
1958                    ids : {
1959                        mainPopupHolder : 'mainPopupHolder'
1960                    },
1961                    $elems : {
1962                        mainPopupHolder : null
1963                    },
1964                    markups : {
1965                        closeBtn : '<a href="#close" data-hide-popup="true" class="popup-closer">Back</a>'
1966                    },
1967                    content : undefined,
1968                    addCloseBtns : true,
1969                    closeOnOverlay : true
1970                },
1971                methods = {
1972                    create : function() {
1973                        var $mainPopupHolder = options.$elems.mainPopupHolder;
1974                    
1975                        if(!thisObjectExists($mainPopupHolder)) $mainPopupHolder = $('#' + options.ids.mainPopupHolder);
1976                        
1977                        if($mainPopupHolder.length < 1) {
1978                            $mainPopupHolder = $([
1979                                '<div class="', options.classes.mainPopupHolder, '" id="', options.ids.mainPopupHolder, '" ></div>'
1980                            ].join('')).appendTo($body);
1981
1982                            options.$elems.mainPopupHolder = $mainPopupHolder;
1983                        } else {
1984                            $mainPopupHolder.appendTo('body');
1985                        }
1986                        
1987                        /**
1988                         * If the main popup holder doesn't have any of its popup
1989                         * methods, add them in now.
1990                         **/
1991                        if(!thisDataIsValidObject($mainPopupHolder, pluginName)) {
1992                            $mainPopupHolder.data(pluginName, {
1993                                reveal : function() {
1994                                    $body.addClass('popup-is-shown');
1995                                    setTimeout(function() {
1996                                        $mainPopupHolder.addClass(options.classes.shown);
1997                                    }, 10);
1998                                    return this;
1999                                },
2000                                
2001                                conceal : function() {
2002                                    $body.removeClass('popup-is-shown');
2003                                    setTimeout(function() {
2004                                        $mainPopupHolder.removeClass(options.classes.shown);
2005                                    }, 10);
2006                                    return this;
2007                                },
2008
2009                                activate : function() {
2010                                    var _dataMethods = this;
2011
2012                                    _dataMethods.deactivate();
2013
2014                                        $mainPopupHolder.on("click." + pluginName, function() {
2015                                        if($.fn.popupify.closeOnOverlay === true) {
2016                                            _dataMethods.conceal();
2017                                        }
2018                                        });
2019
2020                                    $mainPopupHolder.on('click.' + pluginName, '.popup', function(e) {
2021                                        e.stopPropagation();
2022                                    });
2023                                        
2024                                    $mainPopupHolder.on('click.' + pluginName, '[data-hide-popup]', function(e) {
2025                                        e.preventDefault();
2026                                        _dataMethods.conceal();
2027                                    });
2028
2029                                    return _dataMethods;
2030                                },
2031
2032                                deactivate : function() {
2033                                    $mainPopupHolder.off('.' + pluginName);
2034                                    return this;
2035                                },
2036                                
2037                                destroy : function() {
2038                                    this.deactivate();
2039                                    $mainPopupHolder.removeData(pluginName);
2040                                }
2041                            });
2042                            
2043                            /* Attach the event handlers to the main popup holder */
2044                            $mainPopupHolder.data(pluginName).activate();
2045                            
2046                            /* Apply pseudo-elements for IE7. */
2047                            if(homebrew.browser.ie7) $mainPopupHolder.pseudofy({ method : 'before' });
2048                        }
2049                        
2050                        /** 
2051                         * If the `this` reference has nothing for us to iterate
2052                         * through, exit the function.
2053                         **/
2054                        if(this.length < 1) return;
2055                        
2056                        this.each(function() {
2057                            var $this = $(this),
2058                                dataTargetPopup = $this.data('target-popup'),
2059                                target;
2060                            
2061                            /* See if a target content is available */
2062                            if(typeof options.content === 'function') {
2063                                target = options.content.call(this);
2064                            } else if(typeof dataTargetPopup === 'string') {
2065                                target = $($this.data('target-popup'));
2066                            } else if(typeof dataTargetPopup === 'object' && dataTargetPopup instanceof jQuery) {
2067                                target = dataTargetPopup;
2068                            } else if(typeof $this.attr('href') !== 'undefined') {
2069                                target = $($this.attr('href'));
2070                            }
2071                            
2072                            /**
2073                             * If a target content available, then what we're currently
2074                             * iterating through should be the popup toggler. Proceed
2075                             * to attach event handlers on the toggler.
2076                             **/
2077                            if(thisObjectExists(target)) {
2078                                if(!thisDataIsValidObject($this, pluginName)) {
2079                                    $this.data(pluginName, {
2080                                        reveal : function() {
2081                                            target.data(pluginName).reveal();
2082                                            return this;
2083                                        },
2084                                        
2085                                        conceal : function() {
2086                                            $mainPopupHolder.data(pluginName).conceal();
2087                                            return this;
2088                                        },
2089                                        
2090                                        activate : function() {
2091                                            var _dataMethods = this;
2092                                            
2093                                            _dataMethods.deactivate();
2094
2095                                            $this.on('click.' + pluginName, function(e) {
2096                                                e.preventDefault();
2097                                                _dataMethods.reveal();
2098                                            });
2099                                            
2100                                            return $this;
2101                                        },
2102                                        
2103                                        deactivate : function() {
2104                                            $this.off('.' + pluginName);
2105                                            return $this;
2106                                        },
2107                                        
2108                                        destroy : function() {
2109                                            this.deactivate();
2110                                            target.data(pluginName).destroy();
2111                                            $this.removeData(pluginName);
2112                                        }
2113                                    });
2114                                
2115                                    $this.data(pluginName).activate();
2116                                }
2117                            } else {
2118                            /**
2119                             * Otherwise, assume that what we're iterating through
2120                             * is the target content itself.
2121                             **/
2122                                target = $(this);
2123                            }
2124                            
2125                            if(!target.parent().is($mainPopupHolder)) {
2126                                target.appendTo($mainPopupHolder);
2127                            }
2128
2129                            if(options.addCloseBtns && target.find('.popup-closer').length < 1) {
2130                                target.prepend(options.markups.closeBtn);
2131                            }
2132
2133                            if(typeof target.data(pluginName) === 'undefined') {
2134                                target.data(pluginName, {
2135                                    reveal : function() {
2136                                        $mainPopupHolder.find('.popup').removeClass(options.classes.shown);
2137                                        target.addClass(options.classes.shown);
2138                                        $mainPopupHolder.data(pluginName).reveal();
2139                                        $.fn.popupify.closeOnOverlay = options.closeOnOverlay;
2140                                        
2141                                        if(homebrew.browser.ie) {
2142                                            target.trigger('revealed');
2143                                        } else {
2144                                            $mainPopupHolder.on(homebrew.events.transitionEnd, function() {
2145                                                $mainPopupHolder.off(homebrew.events.transitionEnd);
2146                                                target.trigger('revealed');
2147                                            });
2148                                        }
2149                                        
2150                                        return this;
2151                                    },
2152                                    
2153                                    conceal : function() {
2154                                        $mainPopupHolder.data(pluginName).conceal();
2155                                        return this;
2156                                    },
2157                                    
2158                                    destroy : function() {
2159                                        target.removeData(pluginName);
2160                                    }
2161                                });
2162                            }
2163                        });
2164                    },
2165                    
2166                    activate : function() {
2167                        runElemMethod.call(this, pluginName, 'activate');
2168                    },
2169                    
2170                    deactivate : function() {
2171                        runElemMethod.call(this, pluginName, 'deactivate');
2172                    },
2173                    
2174                    destroy : function() {
2175                        var $mainPopupHolder = options.$elems.mainPopupHolder;
2176                    
2177                        if(!thisObjectExists($mainPopupHolder)) $mainPopupHolder = $('#' + options.ids.mainPopupHolder);
2178                        
2179                        if($mainPopupHolder.length && typeof $mainPopupHolder.data(pluginName) !== 'undefined') {
2180                            $mainPopupHolder.data(pluginName).destroy();
2181                        }
2182                        
2183                        runElemMethod.call(this, pluginName, 'destroy');
2184                    }
2185                },
2186                _arguments = arguments;
2187            
2188            /**
2189             * Run through the arguments. If an object is found, assume it's an
2190             * Object Literal with customised options for the function. 
2191             **/
2192            for(var i = _arguments.length-1; i > -1; i--) {
2193                if(typeof _arguments[i] === 'object') $.extend(options, _arguments[i]);
2194            }
2195            
2196            if(typeof _arguments[0] === 'string' && typeof methods[_arguments[0]] === 'function') {
2197                methods[_arguments[0]].call(this);
2198            } else {
2199                methods.create.call(this);
2200            }
2201            
2202            return this;
2203        /* End popupify */
2204        },
2205
2206
2207        /**---- Placeholderify ----**\
2208        
2209         * Call this `$('.foo').placeholderify()` function on any no
2209des that
2210         * require fallback for the `placeholder` HTML5 attribute.
2211         
2212         * You are required to perform your own browser/feature
2213         * checking before calling this function.
2214         */
2215        placeholderify : function(_options) {
2216            var options = $.extend({}, _options, {
2217                    placeholderClass : 'is-placeholder'
2218                });
2219            
2220            return this.each(function() {
2221                var input = $(this),
2222                    placeholderText = input.attr('placeholder');
2223                
2224                if(input.val() === '') {
2225                    if(input.attr('type') === 'password') {
2226                        input.attr('type', 'text');
2227                        input.data('was-password', true);
2228                    }
2229                    input.val(placeholderText).addClass(options.placeholderClass);
2230                }
2231                
2232                input.on({
2233                    'focus.placeholder' : function() {
2234                        if(input.val() === placeholderText) {
2235                            if(input.data('was-password')) {
2236                                input.attr('type', 'password');
2237                            }
2238                            input.val('').removeClass(options.placeholderClass);
2239                        }
2240                    },
2241                    
2242                    'blur.placeholder' : function() {
2243                        if(input.val() === '') {
2244                            if(input.data('was-password')) {
2245                                input.attr('type', 'text');
2246                            }
2247                            input.val(placeholderText).addClass(options.placeholderClass);
2248                        }
2249                    }
2250                });
2251            });
2252        },
2253
2254
2255        /**---- queryString ----**\
2256        
2257         * Function to get the query string. Let's say your URL is like this:
2258
2259            aaa.com?title=hello&desc=goodbye
2260
2261         * Calling `$.fn.queryStr()` would return you an Object Literal with all
2262         * the key-value pairs, as shown:
2263
2264             {
2265                title : 'hello',
2266                desc : 'goodbye'
2267             }
2268         
2269         * If no query string is available, the function will return `false`.
2270         
2271         * The function accepts a single argument, which should be the key
2272         * that you're searching for. If a matching key is found, its value is
2273         * returned.
2274
2275         * Using the above URL again, if you were to run `$.fn.queryStr('title')`,
2276         * then the function will return 'hello' to you. If the query string isn't
2277         * found, then the function will return `false`.
2278         **/
2279        queryStr : function(key) {
2280            var searchStr = window.location.search;
2281            
2282            if(searchStr === '') return false;
2283            
2284            var queryArr = window.location.search.substr(1).split('&'),
2285                tempArr;
2286            
2287            if(typeof key === 'string') {
2288                for(var i = 0, ii = queryArr.length; i < ii; i++) {
2289                    tempArr = queryArr[i].split('=');
2290                    if(tempArr[0] === key) return tempArr[1];
2291                }
2292                return false;
2293            } else {
2294                var resultValue = {};
2295                
2296                for(var i = 0, ii = queryArr.length; i < ii; i++) {
2297                    tempArr = queryArr[i].split('=');
2298                    resultValue[tempArr[0]] = tempArr[1];
2299                }
2300                
2301                return resultValue;
2302            }
2303        },
2304
2305
2306        /**---- Togglerify ----**\
2307        
2308         * `$('.togglers').togglerify()` turns its target nodes into
2309         * togglers; by default, it is a simple class toggler that
2310         * adds/removes a class on its corresponding target node.
2311         
2312         * An example:
2313         
2314            <a class="togglers" data-togglerify-target="togglerContentID">Toggler</a>
2315            
2316            <div id="togglerContentID" />
2317         
2318         * `data-togglerify-target` determines which element the function
2319         * will toggle the class on. The value should be the ID of the
2320         * desired node. Alternatively, you can pass in a function to the
2321         * `content` argument, which will be used to select the desired
2322         * element. For example:
2323
2324            <a class="togglers">Toggler</a>
2325            <div class="contents" />
2326
2327            $('.togglers').togglerify({
2328                content: function(index) {
2329                    return $(this).next('.contents);
2330                }
2331            });
2332
2333         * The `this` keyword refers to the toggler element that's being
2334         * iterated over. You're also given the toggler's index to make use.
2335         
2336         * By default, the class toggled is `is-toggled`. You may pass
2337         * in your own class to toggle, as follows:
2338         
2339            $('.foo').togglerify({ toggledClass : 'is-triggered' });
2340         
2341         * The parameters that let you adjust the behaviour:
2342         *   - `singleActive`
2343         *          = Default value: false
2344         *          = Setting this to `true` ensures that there is only
2345         *            one active toggler at any given time in each
2346         *            iterated group
2347         *   - `selfToggleable`
2348         *          = Default value: true
2349         *          = Setting this to `false` removes the toggler's
2350         *            ability to toggle itself off.
2351         *   - `slide`
2352         *          = Default value: false
2353         *          = Setting this to `true` causes the function to
2354         *            add a sliding transition effect, using CSS Transitions
2355         *            if available, and falling back to jQuery's slide
2356         *            methods otherwise.
2357         *   - `pretoggle`
2358         *          = Default value: undefined
2359         *          = Assigning an index to this parameter causes the
2360         *            function to immediately activate the toggler in
2361         *            question. Index should be zero-based.
2362         *   - `content`
2363         *          = Default value: undefined
2364         *          = Assigning a function to this parameter causes the
2365         *            function to run this in order to find its target
2366         *            content. If this parameter is provided, the function
2367         *            will ignore the `data-togglerify-target` attribute.
2368         **/
2369        togglerify : function() {
2370            var pluginName = 'togglerify',
2371                options = {
2372                    toggledClass : 'is-toggled',
2373                    singleActive : false,
2374                    selfToggleable : true,
2375                    
2376                    slide : false,
2377                    slideDuration : 300,
2378                    
2379                    pretoggle : undefined,
2380                    content : undefined,
2381
2382                    useCSSTransitions : !homebrew.browser.ie && root.hasClass('csstransitions'),
2383
2384                    classes : {
2385                        transitionable: homebrew.classes.transitionable
2386                    }
2387                },
2388                methods = {
2389                    create: function() {
2390                        var togglers = this;
2391
2392                        togglers.each(function(index) {
2393                            var thisToggler = $(this),
2394                                targetData = thisToggler.data('togglerify-target'),
2395                                target;
2396                            
2397                            if(typeof options.content === 'function') {
2398                                target = options.content.call(this, index);
2399                            } else if(typeof targetData === 'string') {
2400                                target = $('#' + targetData);
2401                            } else {
2402                                target = $(thisToggler.attr('href'));
2403                            }
2404                            
2405                            if(!target.length) return;
2406                            
2407                            var togglerAndTarget = thisToggler.add(target);
2408
2409                            togglerAndTarget.data(pluginName, {
2410                                activate : function() {
2411                                    this.deactivate();
2412                                    
2413                                    thisToggler.on('click.' + pluginName, function(e) {
2414                                        e.preventDefault();
2415                                        
2416                                        if(thisToggler.hasClass(options.toggledClass)) {
2417                                            /**
2418                                             * If the selfToggleable boolean is false, it means that the
2419                                             * toggler is not able to toggle itself off.
2420                                             **/
2421                                            if(options.selfToggleable === false) return;
2422
2423                                            /* Make this toggler inactive */
2424                                            thisToggler.data(pluginName).toggleOff();
2425                                            return;
2426                                        }
2427                                        
2428                                        /* Make this toggler active */
2429                                        thisToggler.data(pluginName).toggleOn();
2430                                    });
2431
2432                                    return this;
2433                                },
2434
2435                                deactivate : function() {
2436                                    thisToggler.off('click.' + pluginName);
2437                                    return this;
2438                                },
2439
2440                                toggleOn : function(methodSettings) {
2441                                    methodSettings = methodSettings || {};
2442
2443                                    togglerAndTarget
2444                                        .trigger('toggleOn', [thisToggler, target])
2445                                        .trigger('toggle', [thisToggler, target]);
2446                                    
2447                                    thisToggler.addClass(options.toggledClass);
2448                                    target.addClass(options.toggledClass);
2449
2450                                    /**
2451                                     * If the singleActive boolean is true, it means that only
2452                                     * one toggler in the group can be active at any given time. 
2453                                     **/
2454                                    if(options.singleActive) {
2455                                        togglers.not(thisToggler).togglerify('toggleOff');
2456                                    }
2457
2458                                    if(options.slide && !methodSettings.noSlide) {
2459                                        var targetHeight = methodSettings.contentHeight;
2460
2461                                        if(options.useCSSTransitions) {
2462                                            if(typeof targetHeight !== 'number') {
2463                                                target.css('height', 'auto');
2464                                                targetHeight = target.get(0).clientHeight;
2465                                            }
2466
2467                                            target.css('height', '0px').addClass(options.classes.transitionable);
2468                                            
2469                                            setTimeout(function() {
2470                                                target
2471                                                    .off(homebrew.events.transitionEnd)
2472                                                    .on(homebrew.events.transitionEnd, function() {
2473                                                        target
2474                                                            .off(homebrew.events.transitionEnd)
2475                                                            .removeClass(options.classes.transitionable);
2476
2477                                                        if(typeof methodSettings.contentHeight !== 'number') {
2478                                                            target.css('height', '');
2479                                                        }
2480
2481                                                        togglerAndTarget
2482                                                            .trigger('afterToggleOn', [thisToggler, target])
2483                                                            .trigger('afterToggle', [thisToggler, target]);
2484                                                    })
2485                                                    .css('height', targetHeight + 'px');
2486                                            }, 5);
2487                                        } else {
2488                                            if(typeof targetHeight !== 'number') {
2489                                                target.show();
2490                                                targetHeight = target.get(0).clientHeight;
2491                                            }
2492                                            target.hide();
2493                                            target.slideDown(options.slideDuration, function() {
2494                                                togglerAndTarget
2495                                                    .trigger('afterToggleOn', [thisToggler, target])
2496                                                    .trigger('afterToggle', [thisToggler, target]);
2497                                            });
2498                                        }
2499                                    } else {
2500                                        togglerAndTarget
2501                                            .trigger('afterToggleOn', [thisToggler, target])
2502                                            .trigger('afterToggle', [thisToggler, target]);
2503                                    }
2504
2505                                    return this;
2506                                },
2507                                
2508                                toggleOff : function(methodSettings) {
2509                                    if(!thisToggler.hasClass(options.toggledClass)) return;
2510                                    methodSettings = methodSettings || {};
2511
2512                                    togglerAndTarget
2513                                        .trigger('toggleOff', [thisToggler, target])
2514                                        .trigger('toggle', [thisToggler, target]);
2515
2516                                    thisToggler.removeClass(options.toggledClass);
2517                                
2518                                    if(options.slide && !methodSettings.noSlide) {
2519                                        if(options.useCSSTransitions) {
2520                                            var targetHeight = target.get(0).clientHeight;
2521
2522                                            target
2523                                                .css('height', targetHeight + 'px')
2524                                                .removeClass(options.toggledClass);
2525
2526                                            setTimeout(function() {
2527                                                target
2528                                                    .addClass(options.classes.transitionable)
2529                                                    .off(homebrew.events.transitionEnd)
2530                                                    .on(homebrew.events.transitionEnd, function() {
2531                                                        target
2532                                                            .off(homebrew.events.transitionEnd)
2533                                                            .removeClass(options.classes.transitionable)
2534                                                            .css('height', '');
2535
2536                                                        togglerAndTarget
2537                                                            .trigger('afterToggleOff', [thisToggler, target])
2538                                                            .trigger('afterToggle', [thisToggler, target]);
2539                                                    })
2540                                                    .css('height', '0px');
2541                                            }, 5);
2542                                        } else {
2543                                            target
2544                                                .slideUp(options.slideDuration, function() {
2545                                                    togglerAndTarget
2546                                                        .trigger('afterToggleOff', [thisToggler, target])
2547                                                        .trigger('afterToggle', [thisToggler, target]);
2548                                                })
2549                                                .removeClass(options.toggledClass);
2550                                        }
2551                                    } else {
2552                                        target.removeClass(options.toggledClass);
2553
2554                                        togglerAndTarget
2555                                            .trigger('afterToggleOff', [thisToggler, target])
2556                                            .trigger('afterToggle', [thisToggler, target]);
2557                                    }
2558
2559                                    return this;
2560                                },
2561
2562                                set : function(key, value) {
2563                                    if(typeof arguments[0] === 'object') {
2564                                        $.extend(true, options, arguments[0]);
2565                                    } else {
2566                                        options[key] = value;
2567                                    }
2568                                    return this;
2569                                },
2570
2571                                destroy : function() {
2572                                    this.deactivate();
2573                                    togglerAndTarget.removeData(pluginName);
2574                                }
2575                            });
2576
2577                            thisToggler.data(pluginName).activate();
2578                            
2579                            /* If need to pretoggle a set, this is the time to do it. */
2580                            if(typeof options.pretoggle === 'number' && index === options.pretoggle) {
2581                                thisToggler.addClass(options.toggledClass);
2582                                if(options.slide) target.addClass(options.toggledClass);
2583                                if(!options.useCSSTransitions) target.show();
2584                            }
2585                        });
2586                    },
2587
2588                    activate: function() {
2589                        runElemMethod.call(this, pluginName, 'activate', arguments);
2590                    },
2591
2592                    deactivate: function() {
2593                        runElemMethod.call(this, pluginName, 'deactivate', arguments);
2594                    },
2595
2596                    toggleOn: function() {
2597                        runElemMethod.call(this, pluginName, 'toggleOn', arguments);
2598                    },
2599                    
2600                    toggleOff: function() {
2601                        runElemMethod.call(this, pluginName, 'toggleOff', arguments);
2602                    },
2603
2604                    set: function() {
2605                        runElemMethod.call(this, pluginName, 'set', arguments);
2606                    },
2607
2608                    destroy : function() {
2609                        runElemMethod.call(this, pluginName, 'destroy', arguments);
2610                    }
2611                },
2612                _arguments = arguments;
2613            
2614            if(typeof _arguments[0] === 'string' && _arguments[0] !== 'create' && typeof methods[_arguments[0]] === 'function') {
2615                methods[_arguments[0]].apply(this, $.makeArray(_arguments).slice(1));
2616            } else {
2617                for(var i = _arguments.length-1; i > -1; i--) {
2618                    if(typeof _arguments[i] === 'object') $.extend(options, _arguments[i]);
2619                }
2620
2621                methods.create.call(this);
2622            }
2623            
2624            return this;
2625        /* End togglerify */
2626        },
2627
2628
2629        /**---- Validify ----**\
2630        
2631         * Call this `$('form').validify()` on form nodes that require
2632         * validation.
2633         
2634         * The reason we are using JS to do validation instead of the
2635         * built-in HTML5 validation attributes is because javascript
2636         * validation is capable of providing real-time feedback,
2637         * whereas HTML5 validation only provides feedback when the user
2638         * tries to submit their form. Additionally, JS validation will
2639         * work in older browsers that do not support HTML5 validation.
2640
2641         * To initialise the field with validation, you must explicitly
2642         * set the `data-validify` attribute on the element, as
2643         * shown below:
2644
2645            (A) <input type="text" data-validify="required: true, pattern: numbers, minlength: 6" />
2646            (B) <input type="text" data-validify />
2647
2648         * The field will NOT be initialised by the validation function if
2649         * it doesn't have the custom data attribute.
2650
2651         * On line (A), a comma-separated list of key-value pairs containing
2652         * validation criteria is set as the attribute's value. This
2653         * determines how the field should be validated. If omitted,
2654         * the field is simply initialised with the minimum validation method,
2655         * which checks whether or not the field is empty.
2656
2657         * List of criteria available properties in the JSON Object so far is:
2658         
2659            required : true
2660            pattern : numbers
2661            valid : true
2662            minlength : 6
2663            compareWith : #yourOtherInput
2664            noSuccess : true
2665            response : #yourResponseElem
2666            floater : #yourFloaterElem
2667            
2668         * It is possible to group fields together using the `data-validify-group`
2669         * attribute.
2670
2671            <fieldset data-validify-group-data="required: true, pattern: numbers">
2672                (A) <input data-validify />
2673                (B) <input data-validify="pattern: alphanumeric" />
2674            </fieldset>
2675
2676         * Any criteria set on the group element will be applied onto the fields
2677         * in the group. However, the criteria set on the fields themselves will
2678         * always take precedence. For example, for (A) above, its criteria
2679         * will be following the set declared on the group, which is
2680         * "required: true, pattern: numbers", while for (B), it will be
2681         * "required: true, pattern: alphanumeric" instead.
2682
2683         * Being in a group also results in slightly different response
2684         * behaviour, whereby the success/failed messages are only shown once
2685         * the entire group of fields have been validated to be successful or
2686         * failed.
2687
2688         * Majority of the useful functions and properties are exposed through
2689         * `$('input').data('validify')`, on both the form element and the field
2690         * elements. You will need to make use of these to determine whether or
2691         * not the form is valid, and then allow users to proceed based on that.
2692         
2693         * To add your own regex pattern, scroll down to the function options below
2694         * for more information.
2695         */
2696        validify : function(_options) {
2697            var pluginName = 'validify',
2698                options = {
2699                    selectors : {
2700                        all : 'input[data-validify], textarea[data-validify], select[data-validify]',
2701                        fields : 'input[data-validify], textarea[data-validify], select[data-validify]',
2702                        groups : '[data-validify-group]',
2703                        textFields : 'input[type="text"], input[type="tel"], input[type="number"], input[type="email"], input[type="password"], textarea',
2704                        checkButtons : 'input[type="radio"], input[type="checkbox"]',
2705                        dropdowns : 'select'
2706                    },
2707                    
2708                    classes : [
2709                        { key : 'success',      className : 'is-success' },
2710                        { key : 'hovered',      className : 'is-hovered' },
2711                        { key : 'focused',      className : 'is-focused' },
2712                        { key : 'failed',       className : 'is-failed' },
2713                        { key : 'pattern',      className : 'is-failed-pattern' },
2714                        { key : 'minlength',    className : 'is-failed-minlength' },
2715                        { key : 'required',     className : 'is-failed-required' },
2716                        { key : 'compare-with', className : 'is-failed-compare-with' },
2717                        { key : 'minchecked',   className : 'is-failed-minchecked' },
2718                        { key : 'custom',       className : 'is-failed-cu
2718stom' }
2719                    ],
2720                    
2721                    regexes : [
2722                        /**
2723                         * To add your own reusable regex pattern, just duplicate
2724                         * and modify as needed.
2725                         
2726                            { key : 'your-keyword', pattern : /your-pattern/ }
2727                         
2728                         * Replace `your-keyword` with the desired String value to
2729                         * use in the JSON data object, and `/your-pattern/` with
2730                         * your desired regex pattern.
2731
2732                         * Once added in, you can immediately start using it like below:
2733                         
2734                            <input type="text" data-validify='pattern: your-keyword' />
2735
2736                         **/
2737                        { key : 'name', pattern:  /^[^\d<>!@#$%^&*()=+|{}:;"',.`~_\/\\\]]+$/i },
2738                        { key : 'alphabet', pattern:  /^[a-z\s]+$/i },
2739                        { key : 'alphanumeric', pattern : /^[a-z0-9\s]+$/i },
2740                        { key : 'email', pattern : /^[^\s@]+@[^\s@]+\.[^\s@]+$/i },
2741						{ key : 'dob', pattern : /^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/ },
2742                        { key : 'numbers', pattern : /^\s*\d+\s*$/ },
2743                        { key : 'phone', pattern : /^[\+\-0-9\s]+$/ },
2744                        { key : 'mobile-number', pattern : /^01(|[\d]+)$/ },
2745                        { key : 'full-mobile-number', pattern : /^601(|[\d]+)$/ },
2746                        { key : 'basic', pattern : /^[^<>]+$/i }
2747                    ],
2748                
2749                    validationTimerDuration : 250,
2750                    debug: false
2751                },
2752                allClasses = (function() {
2753                    var tempArr = [];
2754                    
2755                    for(var i = options.classes.length-1; i > -1; i--) {
2756                        if(options.classes[i].key === 'focused') continue;
2757                        tempArr.push(options.classes[i].className);
2758                    }
2759                    
2760                    return tempArr.join(' ');
2761                })(),
2762                methods = {
2763                    create: function() {
2764                        this.each(function() {
2765                            var thisForm = $(this),
2766                                allInputs = thisForm.find(options.selectors.all);
2767                            
2768                            thisForm.data(pluginName, {
2769                                valid: false,
2770                                strict: false,
2771                                debug: options.debug,
2772                                allInputs: allInputs,
2773
2774                                /**
2775                                 * Force the relevant input nodes in this form to update their
2776                                 * own validation status, then make the form refresh itself
2777                                 * to see if it becomes valid based on the latest results.
2778                                 **/
2779                                validate: function() {
2780                                    var _this = this;
2781                                        
2782                                    debugMsg.call(thisForm, [
2783                                        ' /*===================================================*\\ ',
2784                                        '            Updating form validation status              ',
2785                                        ' ------------------------------------------------------- '
2786                                    ], 'start');
2787
2788                                    debugMsg.call(thisForm, [
2789                                        'Form: ',
2790                                        thisForm,
2791                                        ' '
2792                                    ]);
2793                                    
2794                                    /*----*/
2795
2796                                    allInputs.each(function() {
2797                                        $(this).data(pluginName).validate();
2798                                    });
2799                                    
2800                                    _this.refresh();
2801                                    
2802                                    /*----*/
2803
2804                                    debugMsg.call(thisForm, 'Form validation: ' + _this.valid);
2805
2806                                    debugMsg.call(thisForm, [
2807                                        ' ------------------------------------------------------- ',
2808                                        '         Form validation status update complete          ',
2809                                        ' \\*===================================================*/ ',
2810                                        ''
2811                                    ], 'end');
2812                                },
2813                                
2814                                /**
2815                                 * Check the form's validation status without forcing each
2816                                 * of its input children to update itself.
2817                                 *
2818                                 * This is used after an input element updates its own 
2819                                 * status, so that we can see whether or not the form
2820                                 * becomes valid.
2821                                 **/
2822                                refresh: function() {
2823                                    var _this = this;
2824                                    
2825                                    _this.valid = true;
2826                                    
2827                                    allInputs.each(function() {
2828                                        if($(this).data(pluginName).valid === false) {
2829                                            _this.valid = false;
2830                                            return false;
2831                                        }
2832                                    });
2833                                },
2834
2835                                set: function(key, value) {
2836                                    this[key] = value;
2837                                    return this;
2838                                },
2839
2840                                destroy: function() {
2841                                    runElemMethod.call(this.allInputs, pluginName, 'destroy');
2842                                    thisForm.removeData(pluginName);
2843                                }
2844                            });
2845                            
2846                            if(thisForm.is('form')) {
2847                                thisForm.off('.' + pluginName).on('reset.' + pluginName, function() {
2848                                    setTimeout(thisForm.data(pluginName).validate, 100);
2849                                });
2850                            }
2851                            
2852                            allInputs.each(function(index) {
2853                                var thisInput = $(this),
2854                                    validifyData = typeCast(convertCSVtoObject(thisInput.data(pluginName))),
2855                                    inputPluginObj = {
2856                                        group : thisInput.closest('[data-validify-group]')
2857                                    },
2858                                    triggerEvents;
2859
2860                                inputPluginObj.isInHolder = Boolean(thisInput.parent('.field-holder').length);
2861
2862                                /* If the input is in a group, get the entire group's data first */
2863                                if(thisObjectExists(inputPluginObj.group)) {
2864                                    var nestedGroupInputs = inputPluginObj.group.find(options.selectors.groups).find(options.selectors.all),
2865                                        groupValidifyData = typeCast(convertCSVtoObject(inputPluginObj.group.data(pluginName + '-group')));
2866
2867                                    if(thisIsJSON(groupValidifyData)) {
2868                                        if(typeof groupValidifyData === 'string') {
2869                                            $.extend(inputPluginObj, JSON.parse(groupValidifyData));
2870                                        } else {
2871                                            $.extend(inputPluginObj, groupValidifyData);
2872                                        }
2873                                    }
2874                                    
2875                                    inputPluginObj.groupInputs = inputPluginObj.group.find(options.selectors.all).not(nestedGroupInputs);
2876                                }
2877
2878                                /* If the input itself has data, then get it and overwrite the data
2879                                 * inferred from the group */
2880                                if(thisIsJSON(validifyData)) {
2881                                    if(typeof validifyData === 'string') {
2882                                        $.extend(true, inputPluginObj, JSON.parse(validifyData));
2883                                    } else {
2884                                        $.extend(true, inputPluginObj, validifyData);
2885                                    }
2886                                }
2887
2888                                if(thisInput.data('validify-pattern')) {
2889                                    inputPluginObj.pattern = thisInput.data('validify-pattern');
2890                                }
2891
2892                                if(typeof inputPluginObj.pattern === 'undefined') {
2893                                    if(thisInput.attr('type') === 'email') {
2894                                        inputPluginObj.pattern = 'email';
2895                                    } else {
2896                                        inputPluginObj.pattern = 'basic';
2897                                    }
2898                                }
2899                                
2900                                /* If an ID to compare with is provided, then select the element and
2901                                 * store it in the data object. */
2902                                if(typeof inputPluginObj.compareWith === 'string') {
2903                                    inputPluginObj.$compareWith = $(inputPluginObj.compareWith);
2904                                }
2905                                
2906                                /* Similar to above: if an ID to respond with is provided, then
2907                                 * select the element and store it in the data object. */
2908                                if(typeof inputPluginObj.response === 'string') {
2909                                    inputPluginObj.$response = $(inputPluginObj.response);
2910                                }
2911                                
2912                                /* Similar to above. */
2913                                if(typeof inputPluginObj.groupResponse === 'string') {
2914                                    inputPluginObj.$groupResponse = $(inputPluginObj.groupResponse);
2915                                }
2916                                
2917                                /* Similar to above, but with additional methods for the floater. */
2918                                if(typeof inputPluginObj.floater === 'string') {
2919                                    inputPluginObj.$floater = $(inputPluginObj.floater);
2920
2921                                    if(inputPluginObj.$floater.length) {
2922                                        inputPluginObj.$floater.data('floater', {
2923                                            $popup : inputPluginObj.$floater.closest('.popup')
2924                                        });
2925
2926                                        if(inputPluginObj.$floater.data('floater').$popup.length) {
2927                                            inputPluginObj.$floater.appendTo($('#mainPopupHolder'));
2928                                        } else {
2929                                            inputPluginObj.$floater.appendTo('body');
2930                                        }
2931
2932                                        $.extend(inputPluginObj.$floater.data('floater'), {
2933                                            refresh : function() {
2934                                                    var topPos = thisInput.offset().top - inputPluginObj.$floater.outerHeight();
2935                                                    if(this.$popup.length) topPos += $('#mainPopupHolder').scrollTop() - $(window).scrollTop();
2936
2937                                                    inputPluginObj.$floater
2938                                                        .off(FE.events.transitionEnd)
2939                                                        .css({
2940                                                            width : thisInput.outerWidth() + 'px',
2941                                                            marginTop : '',
2942                                                            top :  topPos + 'px',
2943                                                            left : thisInput.offset().left + 'px'
2944                                                        })
2945                                                        .addClass('is-shown');
2946
2947                                                return this;
2948                                            },
2949
2950                                            reveal : function() {
2951                                                var self = this;
2952
2953                                                self.refresh();
2954                                                inputPluginObj.$floater.addClass('is-shown');
2955
2956                                                $(window)
2957                                                    .off('.' + inputPluginObj.eventID)
2958                                                    //.off(FE.events.transitionEnd)
2959                                                    .on('resize.' + pluginName + '.' + inputPluginObj.eventID, homebrew.utils.throttle(function() {
2960                                                        self.refresh();
2961                                                    }, 100));
2962                                                    
2963                                                return self;
2964                                            },
2965
2966                                            conceal : function() {
2967                                                /*inputPluginObj.$floater
2968                                                    .on(FE.events.transitionEnd, function() {
2969                                                        inputPluginObj.$floater
2970                                                            .off(FE.events.transitionEnd)
2971                                                            .css({
2972                                                                top : '',
2973                                                                left : ''
2974                                                            });
2975                                                    })
2976                                                    .removeClass('is-shown')
2977                                                    .css('margin-top', '');*/
2978
2979                                                inputPluginObj.$floater
2980                                                    .removeClass('is-shown')
2981                                                    .css({
2982                                                        marginTop : '',
2983                                                        top : '',
2984                                                        left : ''
2985                                                    });
2986
2987                                                $(window).off('resize.' + inputPluginObj.eventID);
2988
2989                                                return this;
2990                                            }
2991                                        });
2992                                    } else {
2993                                        debugMsg.call(thisForm, 'A floater ID `' + inputPluginObj.floater + '` has been provided, but selection returns nothing.', 'error');
2994                                        inputPluginObj.$floater = undefined;
2995                                    }
2996                                }
2997                                
2998                                /**
2999                                 * Adjust what event to attach the handler to, depending on
3000                                 * what kind of element we're currently iterating over.
3001                                 **/
3002                                if(thisInput.is(options.selectors.checkButtons)) {
3003                                    triggerEvents = 'change.' + pluginName;
3004                                } else if(thisInput.is(options.selectors.textFields)) {
3005                                    triggerEvents = ['input.' + pluginName, 'blur.' + pluginName].join(' ');
3006                                } else if(thisInput.is(options.selectors.dropdowns)) {
3007                                    triggerEvents = ['change.' + pluginName, 'blur.' + pluginName].join(' ');
3008                                }
3009                                
3010                                thisInput.data(pluginName, $.extend(validifyData, {
3011                                    eventID : new Date().getTime() + index,
3012                                    parentForm: thisForm,
3013
3014                                    validate: function() {
3015                                        var _this = this;
3016                                        debugMsg.call(thisForm, thisInput);
3017                                        
3018                                        $.extend(_this, validateInput(thisInput, thisForm));
3019                                        reflectValidity(thisInput, thisForm);
3020
3021                                        debugMsg.call(thisForm, ' ');
3022                                        
3023                                        thisInput.trigger('validate');
3024                                        thisForm.data(pluginName).refresh();
3025                                        
3026                                        return _this;
3027                                    },
3028                                    
3029                                    activate: function() {
3030                                        var _this = this;
3031
3032                                        _this.deactivate();
3033                                        
3034                                        thisInput.on(triggerEvents, function() {
3035                                            clearTimeout(_this.timer);
3036                                            
3037                                            /**
3038                                             * validate() is run in an anonymous function, so that the
3039                                             * scope of the `this` keyword in the validate() function
3040                                             * will be preserved as the input plugin object.
3041                                             *
3042                                             * If you were to assign the function straight to the timeout
3043                                             * by using setTimeout(_this.validate, 250), the `this`
3044                                             * keyword becomes the Window object, which is NOT what we want.
3045                                             */
3046                                            _this.timer = setTimeout(function() {
3047                                                if(thisInput.attr('type') === 'radio' || thisInput.attr('type') === 'checkbox') {
3048                                                    allInputs.filter('[name="' + thisInput.attr('name') + '"]').each(function() {
3049                                                        $(this).data('validify').validate();
3050                                                    });
3051                                                } else {
3052                                                    _this.validate();
3053                                                }
3054                                            }, options.validationTimerDuration);
3055                                        });
3056                                        
3057                                        if(thisObjectExists(_this.$response)) {
3058                                            /* Trigger focus state on the response container. */
3059                                            thisInput.on('focus.' + pluginName, function() {
3060                                                _this.$response.addClass(getClassFromString('focused'));
3061                                            });
3062                                            
3063                                            thisInput.on('blur.' + pluginName, function() {
3064                                                _this.$response.removeClass(getClassFromString('focused'));
3065                                            });
3066                                        }
3067                                        
3068                                        if(thisObjectExists(_this.$groupResponse)) {
3069                                            /* Trigger focus state on the response container. */
3070                                            thisInput.on('focus.' + pluginName, function() {
3071                                                _this.$groupResponse.addClass(getClassFromString('focused'));
3072                                            });
3073                                            
3074                                            thisInput.on('blur.' + pluginName, function() {
3075                                                _this.$groupResponse.removeClass(getClassFromString('focused'));
3076                                            });
3077                                        }
3078                                        
3079                                        if(thisObjectExists(_this.$compareWith)) {
3080                                            /* Make sure that when the target node has
3081                                             * changed, we revalidate this node again. */
3082                                            _this.$compareWith.on('validate.' + pluginName, function() {
3083                                                _this.validate();
3084                                            });
3085                                        }
3086                                        
3087                                        if(thisObjectExists(_this.$floater)) {
3088                                            thisInput
3089                                                .on('focus.' + pluginName, function() {
3090                                                    _this.$floater.addClass(getClassFromString('focused'));
3091                                                    toggleFloater(_this);
3092                                                })
3093                                                .on('blur.' + pluginName, function() {
3094                                                    _this.$floater
3095                                                        .removeClass(getClassFromString('focused'))
3096                                                        .data('floater').conceal();
3097                                                });
3098
3099                                            var mouseTarget = (_this.isInHolder) ? thisInput.parent('.field-holder') : thisInput;
3100
3101                                            mouseTarget
3102                                                .on('mouseenter.' + pluginName, function() {
3103                                                    _this.$floater.addClass(getClassFromString('hovered'));
3104                                                    toggleFloater(_this);
3105                                                })
3106                                                .on('mouseleave.' + pluginName, function() {
3107                                                    _this.$floater.removeClass(getClassFromString('hovered'));
3108                                                    if(_this.$floater.hasClass(getClassFromString('focused'))) return;
3109                                                    _this.$floater.data('floater').conceal();
3110                                                });
3111                                        }
3112                                        
3113                                        return _this;
3114                                    },
3115                                    
3116                                    deactivate: function() {
3117                                        var _this = this;
3118                                    
3119                                        thisInput.off('.' + pluginName);
3120                                        
3121                                        if(thisObjectExists(_this.$compareWith)) {
3122                                            _this.$compareWith.off('validate.' + pluginName);
3123                                        }
3124                                        
3125                                        return _this;
3126                                    },
3127                                    
3128                                    forceValidate: function() {
3129                                        this.set('valid', true).set('error', false);
3130                                        clearTimeout(this.timer);
3131                                        reflectValidity(thisInput, thisForm);
3132                                        return this;
3133                                    },
3134                                    
3135                                    forceInvalidate: function(errorCode) {
3136                                        this.set('valid', false).set('error', errorCode);
3137                                        clearTimeout(this.timer);
3138                                        reflectValidity(thisInput, thisForm);
3139                                        return this;
3140                                    },
3141
3142                                    forceNeutral: function() {
3143                                        this.set('valid', 'neutral').set('error', false);
3144                                        clearTimeout(this.timer);
3145                                        reflectValidity(thisInput, thisForm);
3146                                        return this;
3147                                    },
3148                                    
3149                                    excludeFromValidation: function() {
3150                                        return this.set('required', false);
3151                                    },
3152                                    
3153                                    includeInValidation: function() {
3154                                        return this.set('required', true);
3155                                    },
3156
3157                                    destroy: function() {
3158                                        thisInput.removeData(pluginName);
3159                                    },
3160
3161                                    set: function(key, value) {
3162                                        this[key] = value;
3163                                        return this;
3164                                    }
3165                                }, inputPluginObj));
3166                            });
3167                            
3168                            allInputs.each(function() {
3169                                var thisInput = $(this),
3170                                    dataObj = thisInput.data(pluginName);
3171                                
3172                                if(typeof dataObj.valid === 'undefined' || dataObj.valid === null || dataObj.valid === 'neutral') {
3173                                    dataObj.validate();
3174                                } else {
3175                                    dataObj.originalState = {
3176                                        val : thisInput.val(),
3177                                        valid : dataObj.valid,
3178                                        error : dataObj.error
3179                                    };
3180                                }
3181                                
3182                                reflectValidity(thisInput, thisForm);
3183                                
3184                                dataObj.activate();
3185                            });
3186                        });
3187                    },
3188                    
3189                    activate: function() {
3190                        runElemMethod.call(this, pluginName, 'activate');
3191                    },
3192                    
3193                    deactivate: function() {
3194                        runElemMethod.call(this, pluginName, 'deactivate');
3195                    },
3196                    
3197                    forceValidate: function() {
3198                        runElemMethod.call(this, pluginName, 'forceValidate');
3199                    },
3200                    
3201                    forceInvalidate: function() {
3202                        runElemMethod.call(this, pluginName, 'forceInvalidate');
3203                    },
3204                    
3205                    forceNeutral: function() {
3206                        runElemMethod.call(this, pluginName, 'forceNeutral');
3207                    },
3208                    
3209                    excludeFromValidation: function() {
3210                        runElemMethod.call(this, pluginName, 'excludeFromValidation');
3211                    },
3212                    
3213                    includeInValidation: function() {
3214                        runElemMethod.call(this, pluginName, 'includeInValidation');
3215                    },
3216                    
3217                    destroy: function() {
3218                        runElemMethod.call(this, pluginName, 'destroy');
3219                    },
3220
3221                    set: function() {
3222                        runElemMethod.call(this, pluginName, 'set', arguments);
3223                    }
3224                },
3225                _arguments = arguments;
3226            
3227            for(var i = _arguments.length-1; i > -1; i--) {
3228                if(typeof _arguments[i] === 'object') $.extend(options, _arguments[i]);
3229            }
3230            
3231            if(typeof _arguments[0] === 'string' && typeof methods[_arguments[0]] === 'function') {
3232                methods[_arguments[0]].call(this);
3233            } else {
3234                methods.create.call(this);
3235            }
3236            
3237            function validateInput(targetInput, targetForm) {
3238                var dataObj = targetInput.data(pluginName),
3239                    formDataObj = targetForm.data(pluginName),
3240                    inputValue = targetInput.val();
3241                
3242                if(targetInput.is(options.selectors.checkButtons)) {
3243                    debugMsg.call(targetForm, 'Input is a :checked element (radio button / checkbox.)');
3244                    
3245                    /**
3246                     * If the node is in a group, then see if we need to
3247                     * validate it any differently.
3248                     */
3249                    if(thisObjectExists(dataObj.group)) {
3250                        debugMsg.call(targetForm, ':checked element is in a group.');
3251                    
3252                        var inputName = targetInput.attr('name'),
3253                            inputGroup = dataObj.groupInputs.filter('[name="' + inputName + '"]'),
3254                            minChecked = dataObj.minChecked,
3255                            maxChecked = dataObj.maxChecked;
3256                        
3257                        /**
3258                         * If a maximum amount of checked nodes has been
3259                         * specified, then disable the unchecked boxes when
3260                         * the limit is reached.
3261                         */
3262                        var maxChecked = getDataAttr(dataObj.group, 'validify-max-checked');
3263                            
3264                        if(typeof maxChecked === 'number') {
3265                            targetInput.on('change.' + pluginName, function() {
3266                                var isMaxed = (inputGroup.filter(':checked').length >= maxChecked);
3267                            
3268                                inputGroup.not(':checked').prop('disabled', isMaxed);
3269                            });
3270                        }
3271                        
3272                        /**
3273                         * If a minimum amount is specified, then see how
3274                         * many checked nodes are there. If it doesn't meet
3275                         * the requirements, then immediately fail this input.
3276                         */
3277                        if(typeof minChecked === 'number') {
3278                            debugMsg.call(targetForm, [
3279                                indentStr(0) + 'The group requires at least ' + minChecked + ':checked elements.',
3280                                indentStr(1) + 'Number of :checked elements in the group: ' + inputGroup.filter(':checked').length
3281                            ]);
3282                        
3283                            if(inputGroup.filter(':checked').length < minChecked) {
3284                                debugMsg.call(targetForm, indentStr(2) + 'Group does not meet the minimum number of :checked elements.');
3285                            
3286                                if(formDataObj.strict) {
3287                                    debugMsg.call(targetForm, indentStr(3) + 'Strict mode on. Input fails validation.', 'error');
3288                                    return { valid: false, error: 'min-checked' };
3289                                } else {
3290                                    debugMsg.call(targetForm, indentStr(3) + 'Strict mode off. Input is in neutral validation.', 'neutral');
3291                                    return { valid: 'neutral', error: false };
3292                                }
3293                            } else {
3294                                debugMsg.call(targetForm, indentStr(2) + 'Group meets the minimm number of :checked elements.');
3295                            }
3296                        }
3297                    }
3298
3299                    if(!targetInput.is(':checked')) {
3300                        debugMsg.call(targetForm, [
3301                            ":checked element isn't checked.",
3302                            indentStr(0) + 'Validation type: required.'
3303                        ]);
3304
3305                        if(typeof dataObj.required === 'boolean' && dataObj.required === true) {
3306                            debugMsg.call(targetForm, indentStr(1) + ":checked element is required.");
3307                            
3308                            debugMsg.call(targetForm, indentStr(2) + 'Check to see if any other buttons in its group has been checked.');
3309
3310                            if(targetForm.data('validify').allInputs.filter('[name="' + targetInput.attr('name') + '"]').is(':checked')) {
3311                                debugMsg.call(targetForm, indentStr(3) + 'There is a checked input in the group. Validation passes.', 'success');
3312                                return { valid: true, error: false };
3313                            } else {
3314                                debugMsg.call(targetForm, indentStr(3) + 'There are no checked inputs in the group.');
3315
3316                                if(formDataObj.strict) {
3317                                    debugMsg.call(targetForm, indentStr(4) + 'Strict mode on; Validation fails.', 'error');
3318                                    return { valid: false, error: 'required' };
3319                                } else {
3320                                    debugMsg.call(targetForm, indentStr(4) + 'Strict mode off; node is in neutral validation.', 'neutral');
3321                                    return { valid: 'neutral', error: false };
3322                                }
3323                            }
3324                        } else {
3325                            debugMsg.call(targetForm, indentStr(1) + ":checked element isn't required.");
3326                            debugMsg.call(targetForm, indentStr(2) + "Neutral validation.", 'neutral');
3327
3328                            return { valid: 'neutral', error: false };
3329                        }
3330                    }
3331
3332                    debugMsg.call(targetForm, ":checked element is checked.");
3333                } else {
3334                    if(typeof dataObj.originalState !== 'undefined') {
3335                        debugMsg.call(targetForm, "Input has an original state cached, checking to see if its value matches its original state.");
3336                        if(inputValue === dataObj.originalState.val) {
3337                            debugMsg.call(targetForm, indentStr(1) + "Value matches its original state. Returning to how it was.");
3338                            return { valid: dataObj.originalState.valid, error: dataObj.originalState.error };
3339                        }
3340                    }
3341
3342                    if(inputValue === '') {
3343                        debugMsg.call(targetForm, [
3344                            "Input is empty.",
3345                            indentStr(0) + 'Validation type: required.'
3346                        ]);
3347                    
3348                        /**
3349                         * If required, and field is blank, it should only fail validation
3350                         * when the form is about to be submitted. Otherwise, it should
3351                         * just pass off as neutral. This is because when the user is still
3352                         * filling the form, it is likely that they are aware of the required
3353                         * field being empty.
3354                         *
3355                         * `Strict Mode` is enabled when you call `.set('strict', true)` on the form
3356                         * data object.
3357                         */
3358                        if(typeof dataObj.required === 'boolean' && dataObj.required === true) {
3359                            if(formDataObj.strict) {
3360                                debugMsg.call(targetForm, indentStr(1) + 'Input is required. Strict mode on; input has failed validation', 'error');
3361                                return { valid: false, error: 'required' };
3362                            } else {
3363                                debugMsg.call(targetForm, indentStr(1) + 'Input is required. Strict mode off; input is in neutral validation', 'neutral');
3364                                return { valid: 'neutral', error: false };
3365                            }
3366                        } else {
3367                        /* If not required, then the blank field should pass as neutral validation. */
3368                            debugMsg.call(targetForm, indentStr(1) + "Input isn't required. Neutral validation.", 'neutral');
3369                            return { valid: 'neutral', error: false };
3370                        }
3371                    } else if(thisObjectExists(dataObj.$compareWith)) {
3372                        debugMsg.call(targetForm, "Input needs to be compared to: " + dataObj.compareWith);
3373                        
3374                        var targetComparison = dataObj.$compareWith;
3375                        /**
3376                         * I believe it is beneficial to be very strict with
3377                         * comparison nodes: if the script can't find the
3378                         * desired node to compare to, then throw an error
3379                         * so that we will know that this needs to be fixed.
3380                         */
3381                        if(targetComparison.length < 1) {
3382                            throw new Error('$.validify(): A target for comparison has been provided, but selection has returned with 
3382nothing. Please check your spelling, or make sure both this input and the target comparison input exist on the same page at the same time.');
3383                        }
3384                        
3385                        if(targetComparison.data(pluginName).valid !== true) {
3386                            debugMsg.call(targetForm, indentStr(0) + "The input to compare with hasn't passed validation yet. Neutral validation.", 'neutral');
3387                            return { valid: 'neutral', error: false };
3388                        } else if(targetInput.val() !== targetComparison.val()) {
3389                            debugMsg.call(targetForm, indentStr(0) + "Comparison failed. Input has failed validation.", 'error');
3390                            return { valid: false, error: 'compare-with' };
3391                        }
3392                    /* If the input is NOT validated through a comparison,
3393                     * then we proceed to attach the regular validation. */
3394                    } else {
3395                        debugMsg.call(targetForm, "Input isn't empty.");
3396                    
3397                        /**
3398                         * If a pattern has been provided, then we will need to
3399                         * test it against the corresponding regex pattern.
3400                         *
3401                         * If the pattern value is a string, then we will obtain
3402                         * the regex pattern from the function's default options.
3403                         * Otherwise, if the pattern value is an object, then
3404                         * we simply test with it.
3405                         */
3406                        if(typeof dataObj.pattern === 'string') {
3407                            debugMsg.call(targetForm, [
3408                                indentStr(0) + 'Validation type: pattern.',
3409                                indentStr(1) + 'Pattern provided: ' + dataObj.pattern
3410                            ]);
3411                            
3412                            var regExp;
3413                        
3414                            regExp = getRegexFromString(dataObj.pattern);
3415
3416                            if(typeof regExp === 'string') {
3417                                var patternArray = dataObj.pattern.split('/');
3418
3419                                if(patternArray.length > 2) {
3420                                    regExp = new RegExp(patternArray[1], patternArray[2]);
3421                                } else {
3422                                    regExp = new RegExp(dataObj.pattern);
3423                                }
3424                            }
3425
3426                            if(typeof regExp === 'object') {
3427                                if(regExp.test(inputValue)) {
3428                                    debugMsg.call(targetForm, indentStr(1) + "Input value matches the requierd pattern.", 'success');
3429                                } else {
3430                                    debugMsg.call(targetForm, indentStr(1) + "Input value doesn't match the required pattern. Input has failed validation.", 'error');
3431                                    return { valid: false, error: 'pattern' };
3432                                }
3433                            } else {
3434                                debugMsg.call(targetForm, indentStr(1) + "A pattern has been provided, but it doesn't seem to result in a valid RegExp object. Please check again.", 'error');
3435                            }
3436                        }
3437                        
3438                        /**
3439                         * Similar to above, this should only fail validation
3440                         * when the form is about to be submitted, hence the
3441                         * use of `Strict Mode`.
3442                         */
3443                        if(typeof dataObj.minlength === 'number') {
3444                            debugMsg.call(targetForm, indentStr(0) + 'Validation type: minlength.');
3445                        
3446                            var minlengthNum = parseInt(dataObj.minlength, 10);
3447                        
3448                            if(inputValue.length < minlengthNum) {
3449                                if(formDataObj.strict) {
3450                                    debugMsg.call(targetForm, indentStr(1) + 'Input value hasn\'t reached the minimum length. Strict mode on; input has failed validation.', 'error');
3451                                    return { valid: false, error: 'minlength' };
3452                                } else {
3453                                    debugMsg.call(targetForm, indentStr(1) + 'Input value hasn\'t reached the minimum length. Strict mode off; input is in neutral validation.', 'neutral');
3454                                    return { valid: 'neutral', error: false };
3455                                }
3456                            } else {
3457                                debugMsg.call(targetForm, indentStr(1) + "Input value has reached the minimum length.");
3458                            }
3459                        }
3460                    }
3461                }
3462                
3463                debugMsg.call(targetForm, 'Input has passed validation.', 'success');
3464                return { valid: true, error: false };
3465            }
3466                                    
3467            function reflectValidity(targetInput, targetForm) {
3468                var dataObj = targetInput.data(pluginName),
3469                    isInGroup = thisObjectExists(dataObj.group),
3470                    $response = $(dataObj.$response),
3471                    $floater = $(dataObj.$floater),
3472                    $groupResponse = $(dataObj.$groupResponse);
3473
3474                targetInput.add($response).add($floater).add($groupResponse).removeClass(allClasses);
3475
3476                if(isInGroup) {
3477                    var groupStatus = true;
3478                
3479                    debugMsg.call(targetForm, 'Input is in a group. Checking through all inputs in the group.');
3480                    
3481                    dataObj.groupInputs.each(function() {
3482                        var thisInput = $(this),
3483                            thisDataObj = thisInput.data(pluginName),
3484                            $validifyResponses = thisInput.add($(thisDataObj.$response)).add($(thisDataObj.$floater)),
3485                            thisInputStatus = thisDataObj.valid,
3486                            thisIsCheckInput = (targetInput.is(options.selectors.checkButtons));
3487                        
3488                        if(thisInputStatus === true) {
3489                            if(thisIsCheckInput && !thisInput.is(':checked')) {
3490                                $validifyResponses.removeClass(getClassFromString('success'));
3491                                return;
3492                            }
3493                            $validifyResponses.addClass(getClassFromString('success'));
3494                        } else if(thisInputStatus === false) {
3495                            $validifyResponses.addClass(getClassFromString('failed'));
3496                            
3497                            if(typeof thisDataObj.error === 'string') {
3498                                $validifyResponses.add($groupResponse).addClass(getClassFromString(thisDataObj.error));
3499                            }
3500                            
3501                            groupStatus = false;
3502                        } else if(typeof thisInputStatus === 'undefined' || thisInputStatus === 'neutral') {
3503                            if(groupStatus !== false) groupStatus = 'neutral';
3504                        }
3505
3506                        toggleFloater.call(thisInput, thisDataObj);
3507                    });
3508                    
3509                    if(groupStatus === true) {
3510                        debugMsg.call(targetForm, indentStr(0) + 'Input group has passed validation. Applying success classes onto the group response element.', 'success');
3511                        $groupResponse.addClass(getClassFromString('success'));
3512                    } else if(groupStatus === false) {
3513                        debugMsg.call(targetForm, indentStr(0) + 'Input group has failed validation. Applying failed classes onto the group response element.', 'error');
3514                        $groupResponse.addClass(getClassFromString('failed'));
3515                    } else if(groupStatus === 'neutral') {
3516                        debugMsg.call(targetForm, indentStr(0) + 'Input group has neutral validation. Group response element will not be modified.', 'neutral');
3517                    }
3518                } else {
3519                    var $validifyResponses = targetInput.add($response).add($floater);
3520
3521                    if(dataObj.valid === false) {
3522                        $validifyResponses.addClass(getClassFromString('failed'));
3523
3524                        if(typeof dataObj.error === 'string') {
3525                            $validifyResponses.addClass(getClassFromString(dataObj.error));
3526                        }
3527                    } else if(dataObj.valid === true) {
3528                        if(dataObj.noSuccess) {
3529                            debugMsg.call(targetForm, 'Input has `noSuccess` set to true. Leaving response in its neutral state.', 'neutral');
3530                        } else {
3531                        $validifyResponses.addClass(getClassFromString('success'));
3532                        }
3533                    }
3534
3535                    toggleFloater.call(targetInput, dataObj);
3536                }
3537            }
3538            
3539            function getRegexFromString(string) {
3540                /* Helper function to help us translate the pattern
3541                 * attribute's string value into a regex object. */
3542                for(var i = options.regexes.length-1; i > -1; i--) {
3543                    if(string === options.regexes[i].key) return options.regexes[i].pattern;
3544                }
3545                return '';
3546            }
3547            
3548            function getClassFromString(string) {
3549                /* Helper function to help us translate the status
3550                 * string value into its corresponding class. */
3551                var returnClass;
3552                for(var i = options.classes.length-1; i > -1; i--) {
3553                    if(string === options.classes[i].key) return options.classes[i].className;
3554                }
3555                return '';
3556            }
3557            
3558            function getDataAttr(target, key) {
3559                if(typeof target === 'undefined' || target === null) return;
3560                
3561                return target.data(key, null).data(key);
3562            }
3563
3564            function toggleFloater(dataObj) {
3565                var $theFloater = dataObj.$floater;
3566                if(typeof $theFloater === 'undefined') return;
3567
3568                if($theFloater.hasClass(getClassFromString('failed'))) {
3569                    if($theFloater.hasClass(getClassFromString('focused')) || $theFloater.hasClass(getClassFromString('hovered'))) {
3570                        $theFloater.data('floater').reveal();
3571                    }
3572                } else {
3573                    $theFloater.data('floater').conceal();
3574                }
3575            }
3576            
3577            function indentStr(levels) {
3578                var indentationStrArr = [];
3579                
3580                if(typeof levels === 'number' && levels > 0) {
3581                    for(var i = 0, ii = levels; i < ii; i++) {
3582                        indentationStrArr.push('   ');
3583                    }
3584                }
3585                
3586                indentationStrArr.push('`- ');
3587            
3588                return indentationStrArr.join('');
3589            }
3590            
3591            function thisIsJSON(value) {
3592                if(typeof value === 'string') {
3593                    return (value.substr(0,1) === '{' && value.substr(value.length-1, value.length) === '}');
3594                } else {
3595                    return (typeof value === 'object' && value !== null && value !== undefined);
3596                }
3597            }
3598            
3599            /* Helper function to show console logs only when
3600             * debug mode is set to true. */
3601            function debugMsg(message, styles) {
3602                if(!this.data(pluginName).debug) return;
3603
3604                switch(styles) {
3605                    case 'error':
3606                        styles = 'color: #ff8201;';
3607                    break;
3608
3609                    case 'neutral':
3610                        styles = 'color: #999;';
3611                    break;
3612
3613                    case 'success':
3614                        styles = 'color: #5cb21d;';
3615                    break;
3616
3617                    case 'start':
3618                        styles = 'background-color: #6b3269; color: #fff;';
3619                    break;
3620
3621                    case 'end':
3622                        styles = 'background-color: #444; color: #fff;';
3623                    break;
3624
3625                    default:
3626                        styles = '';
3627                    break;
3628                }
3629
3630                if(message instanceof Array) {
3631                    while(message.length) {
3632                        if(typeof message[0] === 'string') {
3633                            console.log('%c' + message[0], styles);
3634                        } else {
3635                            console.log(message[0]);
3636                        }
3637                        message.shift();
3638                    }
3639                } else {
3640                    if(typeof message[0] === 'string') {
3641                        console.log('%c' + message, styles);
3642                    } else {
3643                        console.log(message);
3644                    }
3645                }
3646            }
3647
3648            function typeCast(keyValueObj) {
3649                $.each(keyValueObj, function(key, value) {
3650                    switch(key) {
3651                        case 'valid':
3652                        case 'required':
3653                            keyValueObj[key] = (value === 'true') ? true : false;
3654                        break;
3655
3656                        case 'minlength':
3657                        case 'maxlength':
3658                        case 'minchecked':
3659                        case 'maxchecked':
3660                            keyValueObj[key] = parseInt(value, 10);
3661                        break;
3662                    }
3663                });
3664
3665                return keyValueObj;
3666            }
3667            
3668            return this;
3669            
3670            /* Removes all data and events related to validify. */
3671            /* function destroyValidify() {
3672                forms.each(function() {
3673                    var form = $(this),
3674                        allInputs = thisForm.find(options.selectors.all);
3675                    
3676                    allInputs.each(function() {
3677                        var currentInput = $(this);
3678                        
3679                        currentInput
3680                            .off('.validify')
3681                            .removeData('validify-status')
3682                            .removeClass(allClasses)
3683                            .prop('disabled', false); // For disabled checkboxes
3684                    
3685                        $('#' + currentInput.data('validify-response-id')).removeClass(allClasses);
3686                    });
3687                    
3688                    thisForm.find('fieldset').each(function() {
3689                        $('#' + $(this).data('validify-response-id')).removeClass(allClasses);
3690                    });
3691                    
3692                    thisForm.off('.validify');
3693                });
3694            } */
3695        /* End validify */
3696        }
3697    });
3698    
3699    function runElemMethod(pluginName, methodName, _args) {
3700        this.each(function() {
3701            var $this = $(this),
3702                pluginData = $this.data(pluginName);
3703            
3704            if(typeof pluginData === 'object'
3705            && typeof pluginData[methodName] === 'function') {
3706                pluginData[methodName].apply(pluginData, _args);
3707            }
3708        });
3709    }
3710
3711    function thisObjectExists(target) {
3712        return (typeof target === 'object' && target !== null && target.length);
3713    }
3714
3715    function thisDataIsValidObject($elem, dataName) {
3716        var elemData = $elem.data(dataName);
3717        return (typeof elemData === 'object' && elemData !== null && elemData !== undefined);
3718    }
3719
3720    function convertCSVtoObject(csv) {
3721        if(csv === '') return {};
3722
3723        var splitArray = csv.split(','),
3724            keyValuesArray = [],
3725            keyValuesObj = {};
3726
3727        while(splitArray.length) {
3728            keyValuesArray.push($.trim(splitArray[0]).split(':'));
3729            splitArray.shift();
3730        }
3731
3732        while(keyValuesArray.length) {
3733            keyValuesObj[$.trim(keyValuesArray[0][0])] = $.trim(keyValuesArray[0][1]);
3734            keyValuesArray.shift();
3735        }
3736
3737        return keyValuesObj;
3738    }
3739})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.