rycus86/demo-site

View on GitHub
src/assets/app.js

Summary

Maintainability
D
2 days
Test Coverage
window.cApp = (function () {
    'use strict';

    $(window).on('load', function () {
        cApp.LazyLoad.css();
        cApp.LazyLoad.images($('body'));

        cApp.Startup.initTabs();
        cApp.Startup.initTabLinks();

        cApp.Startup.initTasks_.forEach(function (task) {
            task.call();
        });

        cApp.Scroll.initialize();
        cApp.Scroll.registerListeners();

        cApp.Navigation.ensureActiveTabIsVisible();
        cApp.StickyProgress.initialize();
        cApp.Markdown.initialize();
    });

    return {
        Startup: {
            initTasks_: [],

            initTabs: function () {
                $('.mdl-layout__tab-bar').children().each(function () {
                    $(this).on('click', function (evt) {
                        evt.preventDefault();

                        var tab = $(this).attr('id').replace('tab-', '');

                        cApp.Navigation.goToTab(tab);
                        $('main').scrollTop(0);
                    });
                });
            },

            initTabLinks: function () {
                $('a[data-target-tab]').each(function () {
                    var target_tab = $(this).data('target-tab');

                    $(this).click(function (evt) {
                        evt.preventDefault();
                        cApp.Navigation.goToTab(target_tab);
                    });
                });
            },

            addInitTask: function (task) {
                cApp.Startup.initTasks_.push(task);
            }
        },

        LazyLoad: {
            css: function () {
                $('meta[name=custom-fetch-css]').each(function () {
                    var $placeholder = $(this);
                    var href = $placeholder.attr('content');

                    $placeholder.replaceWith(
                        $('<link>').attr('rel', 'stylesheet')
                                   .attr('href', href)
                                   .attr('type', 'text/css'));
                });
            },

            images: function (container) {
                container.find('img[data-src]').each(function () {
                    var $img = $(this);
                    var source = $img.data('src');

                    if (source.indexOf('http://') !== 0) {
                        // do not load images on HTTP
                        $img.attr('src', source);
                    }

                    $img.removeAttr('data-src');
                });

                container.find('*[data-background-image]').each(function () {
                    var $item = $(this);

                    var position = $item.data('background-position');
                    if (!!position) {
                        $item.css('background-position', position);
                        $item.removeAttr('data-background-position');
                    }

                    var size = $item.data('background-size');
                    if (!!size) {
                        $item.css('background-size', size);
                        $item.removeAttr('data-background-size');
                    }

                    $item.css('background-image', 'url(' + $item.data('background-image') + ')');
                    $item.removeAttr('data-background-image');
                });
            },
        },

        Navigation: {
            onChangeListeners_: [],

            getSelectedTab: function () {
                var $currentLink = $('a.mdl-layout__tab.is-active');
                return $currentLink.attr('id').replace('tab-', '');
            },

            isTabSelected: function (tabId) {
                return this.getSelectedTab() === tabId;
            },

            goToTab: function (tabId) {
                var $currentLink = $('a.mdl-layout__tab.is-active');
                var $targetLink = $('#tab-' + tabId);

                var currentId = $currentLink.attr('id').replace('tab-', '');
                var targetId = tabId;

                if (currentId === targetId) {
                    return;
                }

                if (!document.getElementById(targetId)) {
                    return;
                }

                cApp.StickyProgress.hide(currentId);

                var $current = $('#' + currentId);
                var $target = $('#' + targetId);

                $current.fadeOut(150, function () {
                    history.pushState({}, window.title, '/page/' + tabId);

                    if (!!window.TabTitles && targetId in window.TabTitles) {
                        document.title = window.TabTitles[targetId];
                    }

                    ga('set', 'page', '/page/' + tabId);
                    ga('send', 'pageview');

                    $currentLink.toggleClass('is-active');
                    $targetLink.toggleClass('is-active');

                    $current.toggleClass('is-active');
                    $target.toggleClass('is-active');

                    $('main').scrollTop(0);

                    cApp.Navigation.fireTabChange(targetId);
                    cApp.Navigation.ensureActiveTabIsVisible();

                    $target.fadeIn(150);
                });
            },

            ensureActiveTabIsVisible: function () {
                var $target = $('a.mdl-layout__tab.is-active');
                var bounds = $target.get(0).getBoundingClientRect();

                if (bounds.left < 0) {
                    var offset = bounds.left - 4;
                    $target.parent().scrollLeft($target.parent().scrollLeft() + offset);
                } else if (bounds.right > $(window).width()) {
                    var offset = bounds.right - $(window).width() + 4;
                    $target.parent().scrollLeft($target.parent().scrollLeft() + offset);
                }
            },

            onTabChange: function (targetTab, callback) {
                cApp.Navigation.onChangeListeners_.push({'target': targetTab, 'callback': callback});

                if (cApp.Navigation.getSelectedTab() === targetTab) {
                    callback();
                }
            },

            fireTabChange: function (targetTab) {
                cApp.Navigation.onChangeListeners_.forEach(function (listener) {
                    if (listener.target === targetTab) {
                        listener.callback();
                    }
                });
            }
        },

        Scroll: {
            listeners_: [],

            pending_: false,
            endScrollHandle_: null,

            initialize: function () {
                var _this = cApp.Scroll;

                $('main').scroll(function () {
                    if (_this.pending_) {
                        return;
                    }

                    _this.pending_ = true;

                    clearTimeout(_this.endScrollHandle_);

                    _this.onScroll();

                    setTimeout(function () {
                        _this.pending_ = false;
                    }, 300);

                    _this.endScrollHandle_ = setTimeout(function () {
                        _this.onScroll();
                    }, 500);
                });
            },

            registerListeners: function () {
                cApp.Scroll.setupFab();
            },

            onScroll: function () {
                cApp.Scroll.listeners_.forEach(function (listener) {
                    listener();
                });
            },

            setupFab: function () {
                var $button = $('.specs-fab-menu');
                var $footer = $('footer');

                cApp.Scroll.listeners_.push(function () {
                    var margin = $button.css('margin-bottom');
                    if (!!margin) {
                        margin = parseInt(margin.substring(0, margin.length - 2));
                    }

                    var threshold = $button.offset().top + $button.outerHeight() / 2 + margin;
                    var offset = $footer.offset().top;

                    if (offset < threshold) {
                        $button.stop().animate({'margin-bottom': '-100px'}, 'fast').addClass('off-screen');
                    } else if (offset > threshold) {
                        $button.removeClass('off-screen').stop().animate({'margin-bottom': '0'}, 'fast');
                    }
                });

                var $header = $('header');
                var $main = $('main');

                $button.find('a').each(function (index, item) {
                    var $item = $(item);
                    var $target = $($item.attr('href'));

                    $item.click(function (event) {
                        event.preventDefault();

                        $main.animate({
                            scrollTop: $target.offset().top + $main.scrollTop() - $header.outerHeight()
                        }, 1000);
                    });
                });
            }
        },

        StickyProgress: {
            progressbar_: $('.sticky-progress'),

            initialize: function () {
                this.progressbar_.hide(); 
            },

            set: function (tabId, value) {
                if (!cApp.Navigation.isTabSelected(tabId)) {
                    return;
                }

                if (!!this.progressbar_.get(0).MaterialProgress) {
                    if (!this.progressbar_.is(':visible')) {
                        this.progressbar_.fadeIn('fast');
                    }

                    this.progressbar_.get(0).MaterialProgress.setProgress(value);
                }
            },

            hide: function (tabId) {
                if (!cApp.Navigation.isTabSelected(tabId)) {
                    return;
                }
                
                this.progressbar_.fadeOut();
            }
        },

        Markdown: {
            initialize: function () {
                if (!!hljs) {
                    hljs.configure({
                        languages: ['java', 'python', 'dockerfile', 'yaml', 'xml', 'html', 'shell', 'bash']
                    });

                    $('pre code').each(function (i, block) {
                        hljs.highlightBlock(block);
                    });
                }
            },

            processCodeBlocks: function (container) {
                var codeBlocks = $(container).find('pre code');
                for (var idx = 0; idx < codeBlocks.length; idx++) {
                    window.hljs.highlightBlock(codeBlocks[idx]);
                }
            },

            processTables: function (container) {
                var tables = $(container).find('table');
                tables.each(function (i, table) {
                    $(table).wrap($('<div>').addClass('table-wrapper'))
                });
            }
        },

        Tracking: {
            start: function (title, label) {
                var startTime = new Date();

                return {
                    done: function () {
                        var endTime = new Date();

                        ga('send', 'timing', title, label, endTime - startTime);
                    }
                };
            }
        }
    };

})();