morteng / Skakt tweaker

// ==UserScript==
// @name            Skakt tweaker
// @namespace       http://sidelinien.dk
// @version         3.0.1
// @license         MIT
// @icon            http://sidelinien.dk/forums/favicon.ico
// @description     Highlighter navn i skakt samt tilføjer autocomplete på navn
// @copyright       2014+, rmjdk
// @include         http://sidelinien.dk/*
// @match           http://sidelinien.dk/*
// @grant none
// @require         http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
// @require         http://code.jquery.com/ui/1.11.4/jquery-ui.min.js
// @require         http://platform.twitter.com/widgets.js
// @grant none
//
// ==/UserScript==

var thingsToMatch = [];
var timer;
var retries = 5;
var spinnerUrl = "https://i0.wp.com/cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif?resize=48%2C48";
var textOpts = [];

function start() {
    console.log('Skakt script: Loading...');

    addJqueryFilters();
    addJQueryUIStyles();
    loadYoutubeApi();
    //disablePost();
    convertTextAreaToInput();
    addStyleButton();
    addAdditionalHighlight();
    addCustomStyles();
    addTmplCompletedEvent();
    addFullscreenButton();
    addImagePreviewEvent();
    setTextFormattingOptions();
    addInputTextFormatting();
    addInputTextFormatListener();
}

function setTextFormattingOptions() {
    var surroundWith = function (styleStart, styleEnd, ignoreSelection) {
        var $input = $("#dbtech_vbshout_editor1");
        var inputEl = $input.get(0);
        var text = $input.val();

        var insertAtPosition = function(input, position, str) {
            return input.substring(0, position) + str + input.substring(position);
        };

        if (!ignoreSelection && inputEl.selectionStart !== inputEl.selectionEnd) {
            text = insertAtPosition(text, inputEl.selectionEnd, styleEnd);
            text = insertAtPosition(text, inputEl.selectionStart, styleStart);

            $input.val(text);

            var selStart = inputEl.selectionStart + styleStart.length;
            var selEnd = inputEl.selectionEnd + styleStart.length;

            inputEl.setSelectionRange(selStart, selEnd);
        } else if (text.length > 0) {
            $input.val(styleStart + text + styleEnd);
            inputEl.selectionStart = text.length + styleStart.length;
            inputEl.selectionEnd = text.length + styleStart.length;
        } else {
            $input.val(styleStart + text + styleEnd);
            inputEl.selectionStart = styleStart.length;
            inputEl.selectionEnd = styleStart.length;
        }
    };

    textOpts = [{
        text: ".oO()",
        shortcut: "t",
        event: function() { surroundWith("/me .oO(", ")", true); }
    }, {
        text: "bold",
        shortcut: "b",
        css: "font-weight: bold;",
        event: function() { surroundWith("[b]", "[/b]"); }
    }, {
        text: "italic",
        shortcut: "i",
        css: "font-style: italic;",
        event: function() { surroundWith("[i]", "[/i]"); }
    }, {
        text: "quote",
        shortcut: "q",
        event: function() { surroundWith("[q]", "[/q]"); }
    }, {
        text: "♫ .. ♫",
        shortcut: "n",
        css: "font-size: 14px;",
        event: function() { surroundWith("♫", "♫"); }
    }];
}

function addInputTextFormatting() {
    $(".dbtech_vbshout_tabcontainer:last-of-type")
        .prepend(`<div id="input-format-btn-container">
						<span id="input-format-btn">
							<img src="/forums/images/buttons/collapse_40b_collapsed.png" style="vertical-align: middle;">
							<p>Genveje</p>
							<div id="input-format-container"><div id="input-format-list"><ul></ul></div></div>
						</span>
					</div>`);

    textOpts.forEach(function(opt) {
        var optHtml = "<li><span>/" + opt.shortcut + "</span><span style='" + opt.css + "'>" + opt.text +"</span></li>";
        var $opt = $("#input-format-list ul").append(optHtml).find("li:last-of-type");

        $opt.click(function () {
            opt.event();

            $("#dbtech_vbshout_editor1").focus();
        });
    });

    var menuHeight = $("#input-format-list").outerHeight(true);
    addDomElement('css', '#input-format-btn-container:hover #input-format-container { height: ' + menuHeight + 'px !important; }');
}

function addInputTextFormatListener() {
    $("#dbtech_vbshout_editor1").bind("input", function(ev) {
        var $input = $(ev.target);

        textOpts.forEach(function(opt) {
            if ($input.val().toLowerCase() === "/" + opt.shortcut) {
                $input.val("");
                opt.event();
            }
        });
    });
}

function addAdditionalHighlight() {
    var addHightlightKey = "additional-hightlight";
    var tooltip = "Tekst der trigger highligt. Flere værdier adskilles med ; (semikolon). Der skelnes ikke mellem store og små bogstaver.";
    var $addHightlight = $(".dbtech_vbshout_tabcontainer:last-of-type").append('<input id="' + addHightlightKey + '" title="' + tooltip + '" />').find("#" + addHightlightKey);

    thingsToMatch.push($('.welcomelink').find('a').text());

    var setAdditionalHightlightValue = function (highlightStr) {
        highlightStr = !highlightStr ? "" : highlightStr;

        thingsToMatch = highlightStr.split(";");
        thingsToMatch = thingsToMatch.map(function (s) { return s.trim(); });
        thingsToMatch = thingsToMatch.filter(function (s) { return s.length > 0; });

        var thingsToMatchStr = thingsToMatch.join("; ");

        localStorage.setItem(addHightlightKey, thingsToMatchStr);
        $addHightlight.val(thingsToMatchStr);
        thingsToMatch.push($('.welcomelink').find('a').text());
    };

    var savedAddHightlight = localStorage.getItem(addHightlightKey);

    setAdditionalHightlightValue(savedAddHightlight);

    $addHightlight.change(function () {
        setAdditionalHightlightValue($addHightlight.val());
    });

    $addHightlight.keyup(function () {
        if (!$addHightlight.val()) {
            setAdditionalHightlightValue("");
        }
    });
}

function addStyleButton() {
    var setSkaktStyle = function() {
        var savedStyle = localStorage.getItem("skakt-style");

        if (!savedStyle || savedStyle === "1") {
            $("#skakt-style").remove();
            $("#skakt-style-btn").val(" Uden linjeskift ").data("skakt-style", 1);
        }

        if (savedStyle === "2") {
            addDomElement("css", "div[name=dbtech_vbshout_content] span:last-of-type { display: inline-block !important; }", "skakt-style");
            $("#skakt-style-btn").val(" Med linjeskift ").data("skakt-style", 2);
        }
    };

    $(".dbtech_vbshout_tabcontainer:last-of-type").append('<input id="skakt-style-btn" type="button" class="button" data-skakt-style="1" />');

    $("#skakt-style-btn").click(function () {
        var style = $(this).data("skakt-style") === 1 ? 2 : 1;

        localStorage.setItem("skakt-style", style);

        setSkaktStyle();
    });

    setSkaktStyle();
}

function addImagePreviewEvent() {
    console.log("Skakt script: Adding image preview events...");

    $("span[name='dbtech_vbshout_shout'] a").each(function (idx, el) {
        $(el).removeAttr("title");
    });

    var setPreviewPosition = function ($previewEl, ev) {
        $previewEl.css("top", ev.pageY + 15).css("left", ev.pageX + 20);

        var retries = 100;

        // element larger than viewport, place on top
        if ($previewEl.outerHeight() > window.innerHeight) {
            $previewEl.css("top", window.scrollY).css("position", "absolute");
        } else {
            while (!$previewEl.inView() && --retries > -1) {
                if (retries > 0 && $previewEl.offset()) {
                    var isBelowView = ($previewEl.outerHeight() + $previewEl.offset().top) > (window.innerHeight + window.scrollY);
                    var isAboveView = window.scrollY > $previewEl.offset().top;

                    if (isAboveView) {
                        $previewEl.css("top", $previewEl.offset().top + 10);
                    }

                    if (isBelowView) {
                        $previewEl.css("top", $previewEl.offset().top - 10);
                    }
                }
            }
        }
    };

    var setMouseEvents = function ($urlEl, $previewEl) {
        var leaveDelay, opacityDelay;

        $urlEl.mouseleave(function () {
            $previewEl.css("opacity", 0);

            leaveDelay = setTimeout(function() {
                $previewEl.remove();
            }, 1000);
        });

        $urlEl.mouseenter(function() {
            clearTimeout(leaveDelay);
            $previewEl.css("opacity", 1);
        });

        $urlEl.mousemove(function (moveEv) {
            setPreviewPosition($previewEl, moveEv);
        });
    };

    var loadImageAsync = function ($previewEl, ev, url) {
        var $asyncImg = $("<img />").load(function () {
            $($previewEl).find("img").prop("src", $(this).prop("src"));

            setPreviewPosition($previewEl, ev);
        });

        setTimeout(function() {
            $asyncImg.prop("src", url);
        }, 200);
    };

    var isImage = function(url) {
        var isImg = false;
        url = url.toString().toLowerCase();

        if (url.indexOf("twimg.com/media") > -1 && url.indexOf(".jpg")) { isImg = true; }
        if (url.indexOf(".jpg") > -1 || url.indexOf(".gif") > -1 || url.indexOf(".png") > -1 || url.indexOf(".jpeg") > -1) { isImg = true; }
        if (url.indexOf("fbcdn.net") > -1 && url.indexOf(".jpg") > -1) { isImg = true; }

        return isImg;
    };

    var isTweet = function(url) {
        url = url.toString().toLowerCase();

        return (url.indexOf("twitter.com") > -1 && url.indexOf("/status/") > -1);
    };

    var isInstagram = function(url) {
        url = url.toString().toLowerCase();

        return (url.indexOf("instagram.com") > -1 && url.indexOf("/p/") > -1);
    };

    var isYoutube = function(url) {
        url = url.toString().toLowerCase();

        return (url.indexOf("youtube.com") > -1 && url.indexOf("v=") > -1) || url.indexOf("youtu.be") > -1;
    };

    // add close handlers for open previews on skakt refresh...
    $(".skaktPreview").each(function (idx, el) {
        var $urlEl = $("div[data-shoutid='" + $(el).data("shoutid") + "'] a[href='" + $(el).data("url") + "']");

        if ($urlEl.length === 0 || $($urlEl.selector + ":hover").length === 0) {
            $(el).remove();
        }

        setMouseEvents($urlEl, $(el));
    });

    var delay;
    $("span[name='dbtech_vbshout_shout'] a").mouseenter(function (ev) {
        delay = setTimeout(function () {
            var $target = $(ev.target);
            var url = $target.prop("href");
            //url = "https://cdn.vox-cdn.com/thumbor/pJgCrRv7_nG0fhd_Ui-934HUi7A=/0x0:1430x808/920x613/filters:focal(574x303:802x531):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/55454065/facebook_fake_news_feed.0.png";
            //url = "https://twitter.com/FCKobenhavn/status/880149023415566336";
            //url = "https://www.instagram.com/p/BV_ww6HnlK2/?taken-by=mzanka";
            //url = "https://www.youtube.com/watch?v=BYD0vxPpEfc";
            //url = "https://scontent-ams3-1.xx.fbcdn.net/v/t1.0-9/21742945_1442006849240144_2710030572559463668_n.jpg?oh=6b5c8d7db2e461c34ba22ab30b2c8ee6&oe=5A15AFED";
            //url = "https://youtu.be/FdeioVndUhs";
            //url = "https://i.redditmedia.com/tMcUoof3tFg5LbTH9aQkPJGuztVMI6ZQx4bUylYB_aI.jpg?w=648&s=ce75331fa619aeccabb10ef3382b3b38";
            //url = "https://twitter.com/prinsnikolaj/status/919203838871121920";

            if (!isImage(url) && !isTweet(url) && !isInstagram(url) && !isYoutube(url)) { return; }

            var elementId = "skaktPreview-" + (new Date().getTime());
            var shoutId = $target.closest("div[data-shoutid]").data("shoutid");

            if ($("span[class='skaktPreview'][data-shoutid='" + shoutId + "'][data-url='" + url +"']").length > 0) {
                return;
            }

            var spinnerElStr = "<img src='" + spinnerUrl + "' />";
            var previewElStr = "<span class='skaktPreview' id='" + elementId +  "' data-shoutid=" + shoutId + " data-url=" + url + ">" + spinnerElStr + "</span>";
            var $previewEl = $("body").append(previewElStr).find("#" + elementId);

            setPreviewPosition($previewEl, ev);

            if (isImage(url)) {
                loadImageAsync($previewEl, ev, url);
            }

            if (isTweet(url)) {
                url = url.substring(url.indexOf("/status/") + 8);
                url = url.substring(0, url.indexOf("/") === -1 ? url.length : url.indexOf("/"));

                var tweetId = url.substring(url.lastIndexOf("/") + 1);

                twttr.widgets
                    .createTweet(tweetId, $previewEl[0], { width: 300 })
                    .then(function(twEl) {
                    $(twEl).siblings().remove(); // spinner
                    setPreviewPosition($previewEl, ev);
                });
            }

            if (isInstagram(url)) {
                $.ajax({
                    url: 'https://api.instagram.com/oembed/?&url=' + url,
                    dataType: "jsonp",
                    success: function(data) {
                        loadImageAsync($previewEl, ev, data.thumbnail_url);
                    },
                });
            }

            if (isYoutube(url)) {
                var videoId;

                if (url.indexOf("v=") > -1) {
                    videoId = url.substring(url.indexOf("v=") + 2);
                } else {
                    videoId = url.substring(url.lastIndexOf("/") + 1);
                }

                var $yEl = $previewEl.append("<div id='ytPlayer' style='display: none;'><div /></div>");

                var player = new YT.Player($previewEl.find("#ytPlayer div")[0], {
                    height: '200',
                    width: '320',
                    videoId: videoId,
                    playerVars: {
                        autoplay: 1,
                        loop: 1
                    },
                    events: {
                        onReady: function (e) {
                            e.target.mute();
                            $previewEl.find("> img").remove();
                            $("#ytPlayer").show();
                            setPreviewPosition($previewEl, ev);
                        },
                        onStateChange: function (e) {
                            setPreviewPosition($previewEl, ev);
                        }
                    }
                });
            }

            setMouseEvents($target, $previewEl);
        }, 300);

    });

    $("span[name='dbtech_vbshout_shout'] a").mouseleave(function (ev) {
        clearTimeout(delay);
    });
}

function addFullscreenButton() {
    $('.blockhead').eq(0).append('<a id="fullscreen">Fuldskærm</a>');

    var button = $('a#fullscreen');

    button.click(function() {
        $('body').toggleClass('fullscreen');
        button.text($('body').hasClass('fullscreen') ? 'Luk fuldskærm' : 'Fuldskærm');

        $(".dbtech_vbshout_frame").height($(window).height() * 0.7);

        $(window).scrollTop($(".body_wrapper").position().top);
    });
}

function addTmplCompletedEvent() {
    var _tmpl = jQueryDupe.fn.tmpl;

    jQueryDupe.fn.tmpl = function() {
        var shoutObj = arguments[0];

        if ((shoutObj.template == "shout" || shoutObj.template == "me" || shoutObj.template == "pm")) {
            shoutObj.time = shoutObj.time.toString().replace('[', '').replace(']', '');

            var tmplResult = _tmpl.apply(this, arguments);
            var $shout = $(tmplResult);

            var toggleHighlight = function () {
                $(thingsToMatch).each(function(idx, val) {
                    val = val.length === 1 ? val : val.toLowerCase();

                    if (shoutObj.message.toLowerCase().indexOf(val) > -1) {
                        $shout.addClass('msg-highlight');

                        return false;
                    }
                });
            };

            var makeUiFit = function () {
                // if /me, move stuff around to fit UI...
                if (shoutObj.template == "me") {
                    $shout.addClass("nobreak");

                    var time = $shout.find('span[name=dbtech_vbshout_shout]:last-of-type');
                    var raw = $shout.find('input[name=dbtech_vbshout_shout_raw]');

                    time.insertAfter(raw);
                }

                if (shoutObj.template == "pm"){
                    $shout.addClass("nobreak");
                }

                if (shoutObj.template == "shout") {
                    $shout.find("[data-userid]").parents("span").contents().filter(function() {
                        return this.nodeType == Node.TEXT_NODE;
                    }).each(function(){
                        this.textContent = this.textContent.replace(':  ', '  ');
                    });
                }

                if ($shout.find("small").length) {
                    $shout.find("span[name='dbtech_vbshout_shout']:last-of-type").css("display", "table-cell");
                }
            };

            toggleHighlight();
            makeUiFit();

            // delay renderAutoSuggest to stop multiple executions
            if (timer !== null) {
                clearTimeout(timer);
                timer = null;

                timer = setTimeout(function () {
                    renderAutoSuggest();
                    addImagePreviewEvent();
                }, 1);
            }

            return tmplResult;
        }

        return _tmpl.apply(this, arguments);
    };
}

function getNames() {
    var names = [];

    $('#dbtech_shoutbox_content1, #wgo_onlineusers_list').find('a[href*="member.php"]').each(function() {
        names.push($(this).text());
    });

    $('a.popupctrl[data-userid]').each(function() {
        names.push($(this).text());
    });

    var distinct = function(arr) {
        var obj = {}, ret = [], i = arr.length;

        while (i--) {
            if (!obj[arr[i]]) {
                obj[arr[i]] = true;
                ret.unshift(arr[i]);
            }
        }

        return ret;
    };

    names = distinct(names).sort();

    var cleanNames = [];
    var nameMap = [];

    names.forEach(function(data) {
        // store both real name and clean name to be able to do startsWith() on names that start with non-alphanum.
        var cleanName = data.replace(/^\W/g, '');
        cleanNames.push(cleanName);

        nameMap[cleanName] = data;
    });

    return { cleanNames: cleanNames, nameMap: nameMap };
}

function disablePost() {
    jQueryDupe.ajaxPrefilter(function(options, originalOptions, jqXHR) {
        if (options.url === "vbshout.php" && options.type === "POST" && options.data.indexOf("message") === 0 ) {
            jqXHR.abort();
            console.log("Stoppede post");
        }
    });

    console.log("Skakt script: Post disabled...");
}

function convertTextAreaToInput() {
    var $textArea = $('textarea#dbtech_vbshout_editor1');
    var $input = $('<input></input>');
    $input.attr('id', $textArea.attr('id'));
    $input.attr('name', $textArea.attr('name'));
    $input.attr('data-instanceid', $textArea.attr('data-instanceid'));

    $textArea.after($input).remove();

    $input.keypress(function (e) {
        if (e.which == 13) {
            $('input[name="dbtech_vbshout_savebutton"]').trigger('click');
        }
    });

    console.log("Skakt script: Converted textarea to input");
}

function renderAutoSuggest() {
    var namesObj = getNames();
    var nameMap = namesObj.nameMap;
    var cleanNames = namesObj.cleanNames;

    console.log('Skakt script: Rendering autosuggest (found ' + cleanNames.length + ' users)');

    $('#dbtech_vbshout_editor1').autocomplete({
        source: function( request, response ) {
            var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( request.term ), "i" );

            // do search in clean names ...
            var items = $.grep( cleanNames, function( item ) {
                return matcher.test( item );
            });

            var newItmes = [];
            // ... but return real names
            items.forEach(function(item) {
                newItmes.push(nameMap[item]);
            });

            response( newItmes );
        },
        autoFocus: true,
        sortResults: false,
        delay: 1000,
        select: function( event, ui ) {
            ui.item.value += ": ";
        },
        open: function( event, ui ) {
            // disable submit in form when selecting user
            $("#dbtech_vbshout_editor1").keypress(function (e) {
                if (e.which == 13) {
                    e.stopPropagation();
                }
            });
        },
        close: function( event, ui ) {
            // restore submit...
            $("#dbtech_vbshout_editor1").keypress(function (e) {
                if (e.which == 13) {
                    $('input[name="dbtech_vbshout_savebutton"]').trigger('click');
                }
            });
        }
    });
}

function addJQueryUIStyles() {
    $.ajax({
        url: "http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/themes/smoothness/jquery-ui.css",
        async: true,
        success: function(data) {
            addDomElement('css', data);
        },
        error: function(err) {
            console.log(err);
        }
    });
}

function addCustomStyles() {
    var borderColor = '#E3E3E3';
    var backgroundColor = '#F2F2F2';

    addDomElement('css', '.ui-state-focus-custom { background-color: #eee !important; font-weight: bold; }');
    addDomElement('css', '.ui-menu-item { color: #417394; font-size: 11px; background-color: ' + backgroundColor + '; }');
    addDomElement('css', '.ui-autocomplete { border: 1px solid ' + '#6B91AB' + '; }');
    addDomElement('css', 'ul.dbtech_shoutarea_left { width: 100% !important; margin-bottom: 2px; }');
    addDomElement('css', 'input#dbtech_vbshout_editor1 { height: 25px; width: 100%; border: 1px solid #6B91AB; font-size: 12px; margin-bottom: 3px; padding-left: 3px; }');
    addDomElement('css', 'div[name=dbtech_vbshout_content] { padding-left: 0px; }');
    addDomElement('css', 'div[name=dbtech_vbshout_content] span:last-of-type { display: block; }');
    addDomElement('css', 'div[name=dbtech_vbshout_content] .nobreak span:last-of-type { display: inline-block; }');
    addDomElement('css', 'div.dbtech_vbshout_shout.alt1 { margin-bottom: 2px !important; padding: 3px 3px 1px 6px; margin: 3px 0 3px 0 }');
    addDomElement('css', 'div.dbtech_vbshout_shout.alt1 a.popupctrl{ font-weight: bold; }');
    addDomElement('css', '.msg-highlight { background-color: ' + backgroundColor + ' !important; border: 1px solid ' + borderColor + '; border-radius: 3px; padding: 3px 3px 3px 6px; margin: 3px 0 3px 0 }');
    addDomElement('css', 'ul.dbtech_shouts { width: 100% !important; }');
    addDomElement('css', '.fullscreen div#sidebar_container { display: none; }');
    addDomElement('css', '.fullscreen div#pagetitle { display: none; }');
    addDomElement('css', '.fullscreen div#breadcrumb { display: none; }');
    addDomElement('css', '.fullscreen div#content { margin-left: 0 !important; }');
    addDomElement('css', '.fullscreen ol#forums { display: none; }');
    addDomElement('css', 'a#fullscreen { float: left; cursor: pointer; }');
    addDomElement('css', '.skaktPreview { position: absolute; background-color: white; border: 1px solid #6B91AB; border-radius: 4px; overflow: hidden; transition: opacity 1s linear; }');
    addDomElement('css', '.skaktPreview img { max-height: 300px; }');
    addDomElement('css', '#skakt-style-btn { float: right; }');
    addDomElement('css', 'twitterwidget { margin: 0 !important; }');
    addDomElement('css', 'input#additional-hightlight { float: right; margin-left: 5px; margin-right: 5px; padding: 2px; font-size: 11px; height: 19px; width: 100px; }');
    addDomElement('css', 'ul.dbtech_shouts li:first-of-type { overflow-x: hidden; }');
    addDomElement('css', '#input-format-btn-container { display: inline-block; font-size: 11px; cursor: default; margin-left: 15px; }');
    addDomElement('css', '#input-format-btn-container p { display: inline; }');
    addDomElement('css', '#input-format-btn img { vertical-align: middle; margin-right: 1px; margin-bottom: 2px; }');
    addDomElement('css', '#input-format-container { position: absolute; overflow: hidden; z-index: 99999; transition: all 150ms linear; height: 0; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);}');
    addDomElement('css', '#input-format-list { font-size: 12px; background-color: white; padding: 3px 15px 10px 15px; }');
    addDomElement('css', '#input-format-list li { cursor: pointer; height: 18px; box-sizing: border-box; overflow: hidden; }');
    addDomElement('css', '#input-format-list li:hover { border-bottom: 1px dotted grey; }');
    addDomElement('css', '#input-format-list li span:first-of-type { display: inline-block; width: 22px; opacity: 0.6; }');
    addDomElement('css', '#input-format-list li span:last-of-type { display: inline-block; }');
}

function addDomElement(type, content, id) {
    var head = document.getElementsByTagName('head')[0];

    var element = document.createElement(type === "css" ? "style" : "script");
    element.type = type === "css" ? "text/css" : "text/javascript";
    element.innerHTML = content;
    if (id) { element.id = id; }

    head.appendChild(element);
}

function addJqueryFilters() {
    $.fn.inView = function(){
        if(!this.length) return false;
        var rect = this.get(0).getBoundingClientRect();

        return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
            rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        );
    };
}

function loadYoutubeApi() {
    var tag = document.createElement('script');

    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}

// account for vBShout delay
function runIfVBShout() {
    if (retries === 0) {
        return;
    }

    if (typeof window.vBShout === "undefined") {
        --retries;
        window.setTimeout(runIfVBShout, 1000);
    } else {
        start();
    }
}

runIfVBShout();