window.SFMP = window.SFMP || {};
SFMP.Utility = (function (module, undefined) {

    var basicHtmlSanitationSpec = {
        allowedTags: ["div", "a", "h1", "h2", "h3", "h4", "h5", "h6", "p", "br", "ol", "ul", "li", "b", "i", "u", "strong", "em", "del", "sub", "sup"],
        tagAttributes: {
            a: [
                { name: "href", filter: cleanHref },
                { name: "target" }
            ]
        }
    };

    function cleanHref(value) {
        var uriExpression = /^http:|https:|mailto:/gi;
        if (uriExpression.test(value)) {
            return encodeURI(value);
        } else {
            return null;
        }
    }

    function escapeAngleBrackets(text) {
        text = text.replace(/</g, "&lt;");
        text = text.replace(/>/g, "&gt;");
        return text;
    }

    function matchToTagInfo(matchArray) {
        var m = matchArray || ["", "", "", ""];
        var tagName = m[2].toLowerCase();
        return {
            wholeTag: m[0],
            leftEnd: m[1],
            tagName: tagName,
            rightEnd: m[3],
            isClosingTag: (m[1] === "</"),
            bareTag: (m[1] + tagName + m[3])
        }
    }

    function parseTagAttribute(tagHtml, attributeName) {
        var pattern = attributeName + "=(\\\"[^\\\"]*\\\"|\\\'[^\\\']*\\\')";
        var re, match, quotedValue;

        re = new RegExp(pattern, "i");
        match = tagHtml.match(re);

        if (match === null) {
            return null;
        }

        quotedValue = match[1];
        return {
            name: attributeName,
            quote: quotedValue.substring(0, 1),
            value: quotedValue.substring(1, quotedValue.length - 1)
        };

    }

    function sanitizeOpeningTag(tagInfo, allowedAttributesSpec) {
        var i, name, filter, info, filtered;
        var attributes = [];
        var nullFilter = function (val) { return val; };

        for (i = 0; i < allowedAttributesSpec.length; i += 1) {
            name = allowedAttributesSpec[i].name;
            filter = allowedAttributesSpec[i].filter || nullFilter;
            info = parseTagAttribute(tagInfo.wholeTag, allowedAttributesSpec[i].name);
            if (info && info.value) {
                filtered = filter(info.value);
                if (filtered) {
                    attributes.push(info.name + "=" + info.quote + filtered + info.quote);
                }
            }
        }

        return "<" + tagInfo.tagName + " " + attributes.join(" ") + ">";
    }

    function sanitizeHtmlString(htmlString, sanitationSpec) {
        var htmlString = htmlString || "";
        var spec = sanitationSpec || basicHtmlSanitationSpec;
        var allowedTags = spec.allowedTags || [];
        var tagAttributes = spec.tagAttributes || {};

        var tagPattern = "(<\\/?)(" + allowedTags.join("|") + ")(?=\\W)(?:(?!>|\\/>).)*(\\/?>)";
        var tagExpression = new RegExp(tagPattern, "gi");

        var output = [];
        var match, tagInfo, allowedAttributes, i, attributeInfo, attributeOut, cleaned;
        var searchStart = 0;


        function getNonTagText(startIndex, endIndex) {
            var text = htmlString.substring(startIndex, endIndex);
            return escapeAngleBrackets(text);
        }

        while ((match = tagExpression.exec(htmlString)) !== null) {
            // Handle non-tag text preceding the next tag. 
            if (match.index > searchStart) {
                output.push(getNonTagText(searchStart, match.index));
            }

            // Handle the matched tag.
            tagInfo = matchToTagInfo(match);

            allowedAttributes = tagAttributes[tagInfo.tagName];
            if (allowedAttributes && !tagInfo.isClosingTag) {
                cleaned = sanitizeOpeningTag(tagInfo, allowedAttributes);
            } else {
                cleaned = tagInfo.bareTag;
            }
            output.push(cleaned);

            searchStart = tagExpression.lastIndex;
        }

        // Handle trailing non-tag text.
        if (htmlString.length > searchStart) {
            output.push(getNonTagText(searchStart, htmlString.length));
        }

        return output.join("");
    }
    


    function assign(target, varArgs) {
        var targetObj, i, sourceObj, propName;

        if (target == null) {
            throw new TypeError("Cannot convert undefined or null to object.");
        }

        targetObj = Object(target);

        for (i = 1; i < arguments.length; i += 1) {
            var sourceObj = arguments[i];

            if (sourceObj != null) {
                for (propName in sourceObj) {
                    if (Object.prototype.hasOwnProperty.call(sourceObj, propName)) {
                        targetObj[propName] = sourceObj[propName];
                    }
                }
            }
        }
        return targetObj;
    }

    function isEmbedded() {
        try {
            return (window.self !== window.top);
        } catch (e) {
            return true;
        }
    }

    function getQueryParam(name) {
        var regexS = "[\\?&]" + name + "=([^&#]*)";
        var regex = new RegExp(regexS, "i");
        var results = regex.exec(window.location.href);
        if (results === null) {
            return "";
        }

        var value = decodeURIComponent(results[1].replace(/\+/g, " "));

        return value;
    }

    function getAuthTicketFromCookie(resourceId) {
        // Note that this will stop working should auth ticket cookies be marked httponly in the future.
        var pattern = "MediasiteAuthTickets=(?:[a-fA-F0-9\\&=]*)" + resourceId + "=([a-fA-F0-9]*)";
        var regex = new RegExp(pattern, '');

        var match = document.cookie.match(regex);
        if (match && match.length === 2) {
            return match[1];
        } else {
            return null;
        }
    }

    module = module || {};

    module.isEmbedded = isEmbedded;
    module.getQueryParam = getQueryParam;
    module.assign = assign;
    module.sanitizeHtmlString = sanitizeHtmlString;
    module.getAuthTicketFromCookie = getAuthTicketFromCookie;

    module.queryStringParam = {
        COVER_ENABLE_OLD: "playcover",
        COVER_ENABLE: "cover",
        COVER_TITLE: "covertitle",
        COVER_PLAYBUTTON: "coverplaybutton",
        COVER_COUNTDOWN: "covercountdown",

        AUTHTICKET: "authticket",
        COLLECTION: "collection",
        CATALOG: "catalog"
    };

    return module;

})(SFMP.Utility);