jQuery.fn.extend({
    insertAtCaret: function (myValue, addSpacing) {
        return this.each(function (i) {
            if (document.selection) {
                //Internet Explorer
                this.focus();
                var sel = document.selection.createRange();
                sel.text = myValue;
                this.focus();
            } else if (this.selectionStart || this.selectionStart == '0') {
                // Mozilla
                var startPos = this.selectionStart;
                var endPos = this.selectionEnd;
                var scrollTop = this.scrollTop;

                if (addSpacing == true) {
                    if (startPos != 0 && this.value.substr(startPos - 1, 1) != ' ') {
                        myValue = ' ' + myValue;
                    }

                    if (endPos != this.value.length && this.value.substr(endPos, 1) != ' ') {
                        myValue = myValue + ' ';
                    }
                }

                this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos, this.value.length);
                this.focus();
                this.selectionStart = startPos + myValue.length;
                this.selectionEnd = startPos + myValue.length;
                this.scrollTop = scrollTop;
            } else {
                this.value += myValue;
                this.focus();
            }
        });
    }
});
// LOCATION //
if (!location.baseUrl) {
    location.baseUrl = (function () {
        return window.location.href.match(/^(http|https)\:\/\/(localhost\/[^\/]+|[^\/]+)/)[0] + '/';
    })();
};

if (!location.query) {
    location.query = (function () {
        var u = location.href,
            s = u.indexOf('?') + 1,
            e = u.indexOf('#');

        if (!s) return {};

        var qs = (e > -1 ? u.substring(s, e) : u.substring(s)).split('&');
        for (var i = 0, r = {}; i < qs.length; i++) {
            qs[i] = qs[i].split('=');
            r[qs[i][0]] = qs[i].length > 1 ? decodeURIComponent(qs[i][1]) : qs[i][0];
        }
        return r;
    })();
};

if (!location.redirect) {
    location.redirect = function (href) {
        location.href = href;
    };
};

// WINDOW //

if (!window.dialog) {
    window.dialog = function (url, windowName, width, height, canScroll, canResize, leftPos, topPos) {
        var scrollValue = (canScroll) ? 'yes' : 'no';
        var resizableValue = (canResize) ? 'yes' : 'no';
        var left = (leftPos === undefined) ? (screen.width / 2) - width / 2 : leftPos;
        var top = (topPos === undefined) ? (screen.height / 2) - height / 2 : topPos;

        var newWindow = window.open(url, windowName, 'toolbar=no,location=no,directories=no,status=yes,menubar=no,scrollbars=' + scrollValue + ',resizable=' + resizableValue + ',copyhistory=no,width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',screenX=' + left + ',screenY=' + top);

        if (newWindow != null) {
            newWindow.name = windowName;

            if (window.focus) {
                newWindow.focus();
            }
        }
    }
}

if (!window.launch) {
    window.launch = function (href) {
        var w = window.open(href);
        w.focus();
        return false;
    };
};

if (!window.post) {
    window.post = function (href, formValues, newWindow) {
        var $form = $('<form action="' + href + '" method="post"' + (newWindow ? ' target="_blank"' : '') + '></form>');

        for (var prop in formValues) {
            $form.append('<input type="hidden" name="' + prop + '" value="' + formValues[prop] + '"></input>');
        }

        $('body').append($form);
        $form.submit();
    };
};

if (!window.clearSelection) {
    window.clearSelection = function () {
        if (window.getSelection) {
            if (window.getSelection().empty) {                      // Chrome
                window.getSelection().empty();
            } else if (window.getSelection().removeAllRanges) {     // Firefox
                window.getSelection().removeAllRanges();
            }
        } else if (document.selection) {                            // IE?
            document.selection.empty();
        }
    };
};

// DOCUMENT //

if (!document.newElement) {
    document.newElement = function (tagName, attributes, contents) {
        var element = document.createElement(tagName);

        if (attributes) {
            var attr, attrName, attrValue;
            for (var i = 0, len = attributes.length; i < len; i++) {
                attr = attributes[i];
                attrName = attr[0];
                attrValue = attr.length > 1 ? attr[1] : '';

                if (attrName == 'classList') {
                    for (var j = 0; j < attrValue.length; j++) {
                        element.classList.add(attrValue[j]);
                    }
                }
                else if (attrName == 'class') {
                    var classes = attrValue.split(' ');
                    for (var k = 0; k < classes.length; k++) {
                        element.classList.add(classes[k]);
                    }
                } else {
                    element.setAttribute(attrName, attrValue);
                }
            }
        }

        if (contents) {
            element.innerHTML = contents;
        }

        return element.outerHTML;
    };
};

// STRING //

String.prototype.replaceAll = function (target, replacement) {
    return this.split(target).join(replacement);
};

String.prototype.format = function () {
    var content = this;
    for (var i = 0; i < arguments.length; i++) {
        var replacement = '{' + i + '}';
        content = content.replaceAll(replacement, arguments[i]);
    }
    return content;
};

String.prototype.toSnakeCase = function () {
    return this.parseCamelCase().replace(/ /g, "-").replace(/--/g, "-").toLowerCase();
};

String.prototype.toCamelCase = function () {
    return this
        .replace(/([A-Z]+)/g, function (match, group1) {
            return group1.substr(0, 1).toLowerCase() + group1.toLowerCase().substr(1, group1.length);
        })
        .replace(/[\W_](.)/g, function (match, group1) {
            return group1.toUpperCase();
        });
};

String.prototype.parseCamelCase = function () {
    return this.replace(/^[a-z]|[^\s][A-Z]/g,
            function (match, index) {
                if (index == 0) {
                    return (match.toUpperCase());
                } else {
                    return (match.substr(0, 1) + ' ' + match.substr(1).toUpperCase());
                }
            });
};

String.prototype.toTitleCase = function () {
    return this.replace(/^[a-z]|[\s][a-z]/g,
            function (match, index) {
                if (index == 0) {
                    return (match.toUpperCase());
                } else {
                    return (match.substr(0, 1) + ' ' + match.substr(1).toUpperCase());
                }
            });
};

// ARRAY //

if (!Array.prototype.contains) {
    Array.prototype.contains = function (value) {
        var i = this.length;
        while (i--) {
            if (this[i] === value) {
                return true;
            }
        }
        return false;
    };
};

if (!Array.prototype.pluck) {
    Array.prototype.pluck = function (propName) {
        var props = propName.split('.');

        var results = this;
        for (var i = 0; i < props.length; i++) {
            results = _.pluck(results, props[i]);
        }
        return results;
    };
};


if (!Array.prototype.selectMany) {
    Array.prototype.selectMany = function (propName) {
        return _.chain(this).pluck(propName).flatten().value();
    };
};

if (!Array.prototype.where) {
    Array.prototype.where = function (iterator) {
        var results = [];
        for (var i = 0; i < this.length; i++) {
            var item = this[i];
            if (iterator(item)) {
                results.push(item);
            }
        }
        return results;
    };
};

if (!Array.prototype.first) {
    Array.prototype.first = function () {
        return this[0];
    };
};

if (!Array.prototype.last) {
    Array.prototype.last = function () {
        return this[this.length - 1];
    };
};

if (!Array.prototype.single) {
    Array.prototype.single = function (iterator) {
        for (var i = 0; i < this.length; i++) {
            var item = this[i];
            if (iterator(item)) {
                return item;
            }
        }
        return undefined;
    };
};
