/*! jQuery.counter.js (jQuery Character and Word Counter plugin)
 v2.0 (c) Wilkins Fernandez
 MIT License
 */
(function($) {
    $.fn.extend({
        counter : function(options) {
            var defaults = {
                type    : 'char',              // {char || word}
                count    : 'down',            // count {up || down} from or to the goal number
                goal    : 140,                // count {to || from} this number
                text    : true,                // Show description of counter
                msg        : ''
            }, $countObj = '', countIndex = '',
            // Pass {} as first argument to preserve defaults/options for comparision
            options = $.extend({}, defaults, options), methods = {
                /* Adds the counter to the page and binds counter to user input fields */
                init : function($obj) {
                    var objID = $obj.attr('id'), counterID = objID + '_count';
                    // Insert counter after text area/box
                    $('<div/>').attr('id', objID + '_counter').html("<span id=" + counterID + "/> " + methods.setMsg() ).insertAfter($obj);
                    // Set $countObj jQuery object
                    $countObj = $('#' + counterID);
                    // Bind methods to events
                    methods.bind($obj);
                },
                bind : function($obj) {
                    $obj.bind("keypress.counter keydown.counter keyup.counter blur.counter focus.counter change.counter paste.counter", methods.updateCounter);
                    $obj.bind("keydown.counter", methods.doStopTyping);
                    $obj.trigger('keydown');
                },
                /* Sets the appropriate message after counter */
                setMsg : function() {
                    if(options.msg !== '') {
                        return options.msg;
                    }
                    this.text = "character word left max".split(' ');
                    this.chars = "s ( )".split(' ');
                    this.msg = "";
                    switch (options.type) {
                        case "char":
                            if(options.count === defaults.count && options.text) {
                                // x character(s) left
                                this.msg = this.text[0] + this.chars[1] + this.chars[0] + this.chars[2] + " " + this.text[2];
                            } else if(options.count === "up" && options.text) {
                                // x characters (x max)
                                this.msg = this.text[0] + this.chars[0] + " " + this.chars[1] + options.goal + " " + this.text[3] + this.chars[2];
                            }
                            break;
                        case "word":
                            if(options.count === defaults.count && options.text) {
                                // x word(s) left
                                this.msg = this.text[1] + this.chars[1] + this.chars[0] + this.chars[2] + " " + this.text[2];
                            } else if(options.count === "up" && options.text) {
                                // x word(s) (x max)
                                this.msg = this.text[1] + this.chars[1] + this.chars[0] + this.chars[2] + " " + this.chars[1] + options.goal + " " + this.text[3] + this.chars[2];
                            }
                            break;
                        default:
                    }
                    return this.msg;
                },
                /* Returns the amount of words passed in the val argument
                 * @param val Words to count */
                getWords : function(val) {
                    if(val !== "") {
                        return $.trim(val).replace(/\s+/g, " ").split(" ").length;
                    } else {
                        return 0;
                    }
                },
                updateCounter : function(e) {
                    // Save reference to $(this)
                    var $this = $(this);
                    // Is the goal amount passed? (most common when pasting)
                    if(countIndex < 0 || countIndex > options.goal) {
                        methods.passedGoal($this);
                    }
                    // Counting characters...
                    if(options.type === defaults.type) {
                        // ...down
                        if(options.count === defaults.count) {
                            countIndex = options.goal - $this.val().length;
                            // ...up
                        } else if(options.count === 'up') {
                            countIndex = $this.val().length;
                        }
                        // Update text
                        $countObj.text(countIndex);
                        // Counting words...
                    } else if(options.type === 'word') {
                        // ...down
                        if(options.count === defaults.count) {
                            // Count words
                            countIndex = methods.getWords($this.val());
                            // Then subtract
                            countIndex = options.goal - countIndex;

                            // Update text
                            $countObj.text(countIndex);
                            // ...up
                        } else if(options.count === 'up') {
                            countIndex = methods.getWords($this.val());
                            $countObj.text(countIndex);
                        }
                    }
                    return;
                },                
                /* Stops the ability to type */
                doStopTyping : function(e) {
                    // backspace, delete, tab, left, up, right, down, end, home, spacebar
                    var keys = [46, 8, 9, 35, 36, 37, 38, 39, 40, 32], $this = $(this);
                    if(methods.isGoalReached(e)) {
                        // NOTE: Refactoring this condition into a function    was too slow; it'd let you type 1 more character, THEN stop you and remove it                        
                        if(e.keyCode !== keys[0] && e.keyCode !== keys[1] && e.keyCode !== keys[2] && e.keyCode !== keys[3] && e.keyCode !== keys[4] && e.keyCode !== keys[5] && e.keyCode !== keys[6] && e.keyCode !== keys[7] && e.keyCode !== keys[8]) {
                            // Stop typing when counting characters
                            if(options.type === defaults.type) {
                                return false;
                                // Counting words, only allow backspace & delete
                            } else if(e.keyCode !== keys[9] && e.keyCode !== keys[1] && options.type != defaults.type) {                                    
                                return true;
                            } else {
                                return false;
                            }
                        }
                    }
                },
                /* Checks to see if the goal number has been reached */
                isGoalReached : function(e) {
                    var _goal;
                    // Counting down
                    if(options.count === defaults.count) {
                        _goal = 0;
                        return (countIndex <= _goal ) ? true : false;
                    } else {
                        // Counting up
                        _goal = options.goal;
                        return (countIndex >= _goal ) ? true : false;
                    }
                },
                /* Removes extra words when the amount of words in the input go over the desired goal.
                 * @param {Number} numOfWords Amount of words you would like shown
                 * @param {String} text The full text to condense
                 * */
                wordStrip : function(numOfWords, text) {
                    var wordCount = text.replace(/\s+/g, ' ').split(' ').length;

                    // Get the word count by counting the spaces (after eliminating trailing white space)
                    text = $.trim(text);

                    // Make it worth executing
                    if(numOfWords <= 0 || numOfWords === wordCount) {
                        return text;
                    } else {
                        text = $.trim(text).split(' ');
                        text.splice(numOfWords, wordCount, '');
                        return $.trim(text.join(' '));
                    }
                },
                /* If the goal is passed, trim the chars/words down to what is allowed. Also, reset the counter. */
                passedGoal : function($obj) {
                    var userInput = $obj.val();
                    if(options.type === 'word') {
                        $obj.val(methods.wordStrip(options.goal, userInput));
                    }
                    if(options.type === 'char') {
                        $obj.val(userInput.substring(0, options.goal));
                    }
                    // Reset to 0
                    if(options.type === 'down') {
                        $countObj.val('0');
                    }
                    // Reset to goal
                    if(options.type === 'up') {
                        $countObj.val(options.goal);
                    }
                }
            };
            return this.each(function() {
                methods.init($(this));
            });
        }
    });
})(jQuery);
