Subversion Repositories public iLand

Rev

Rev 1221 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* global $:true */
$(function() {
    'use strict';

    // ************************************************************************* //
    //  HTML templates
    // ************************************************************************* //

    // (None)


    // ************************************************************************* //
    //  Functions
    // ************************************************************************* //

    function setUpActiveTab() {
        if(localStorage.getItem('main-nav')){
            $('a[href="'+ localStorage['main-nav'] + '"]').tab('show');
        }
    }

    function setUpOptionsCheckboxes() {
        if(localStorage.getItem('options')){
            var optionsArr = JSON.parse(localStorage.options),
                optionsForm = $('#options-form');

            for(var i=0;i<optionsArr.length;i++){
                var box = optionsForm.find('input:checkbox').eq(i);
                box.prop('checked', optionsArr[i]);
                setOptionDisplayState(box);
            }
        }
    }

    function setUpWidgets() {
        var sideSource = [], navbarSource = [], sidebarSearch, navbarSearch;

        $('#sidebar .tab-pane.active li a').each(function(index, elem) {
            sideSource.push($(elem).text());
        });
        sidebarSearch = $('#sidebar input[type="search"]');
        sidebarSearch.typeahead({
            source: sideSource,
            updater : function(item) {
                $('#sidebar .tab-pane.active a:contains(' + item + ')')[0].click();
                return item;
            }
        });

        $('#sidebar .tab-pane li a').each(function(index, elem) {
            var $el = $(elem),
                type = $el.parents('.tab-pane').is('#classes') ? 'classes/' : 'modules/';
            navbarSource.push(type + $el.text());
        });
        navbarSearch = $('.navbar input');
        navbarSearch.typeahead({
            source : navbarSource,
            updater : function(item) {
                var type = item.split('/')[0], name = item.split('/')[1],
                    $parent = $('#sidebar .tab-pane#' + type);
                $parent.find('a:contains(' + name + ')')[0].click();
                return item;
            }
        });
    }

    function setOptionDisplayState(box) {
        var cssName = $.trim(box.parent('label').text()).toLowerCase();
        if(box.is(':checked')){
            $('div.'+cssName).css('display', 'block');
            $('li.'+cssName).css('display', 'block');
            $('span.'+cssName).css('display', 'inline');
        }else{
            $('.'+cssName).css('display', 'none');
        }
    }

    function scrollToAnchor($anchor) {
        var navbarHeight = $('.navbar-fixed-top').outerHeight(true);
        $(document).scrollTop( $anchor.offset().top - navbarHeight);
    }

    $('[data-toggle=tab]').on('click', function (event, extraArgs) {
        // Why?  If responding to the click of a link or hashchange already, we really
        //  don't want to change window hash
        if (extraArgs && extraArgs.ignore === true) {
            return;
        }
        window.location.hash = $(this).attr('href');
    });

    $('[data-tabid]').on('click', function(event) {
        var tabToActivate = $(this).attr('data-tabid'),
        anchor = $(this).attr('data-anchor');
        event.preventDefault();

        $('[data-toggle=tab][href="'+ tabToActivate + '"]').click();
        // ...huh?  http://stackoverflow.com/a/9930611
        // otherwise, can't select an element by ID if the ID has a '.' in it
        var scrollAnchor = anchor.replace(/\./g, '\\.');
        scrollToAnchor($(scrollAnchor));
        window.location.hash = anchor;
    });

    function moveToWindowHash() {
        var hash = window.location.hash,
            tabToActivate = hash,
            $tabToActivate = false,
            $scroll = $(hash);

        if (!hash) {
            return;
        }

        if (hash.match(/^#method_/)) {
            tabToActivate = '#methods';
        }
        else if (hash.match(/^#property_/)) {
            tabToActivate = '#properties';
        }
        else if (hash.match(/^#event_/)) {
            tabToActivate = '#event';
        }
        else if (hash.match(/#l\d+/)) {
            var lineNumber = /#l(\d+)/.exec(hash)[1];
            var whichLi = parseInt(lineNumber, 10) - 1; //e.g. line 1 is 0th element
            $scroll = $('ol.linenums > li').eq(whichLi);
        }

        $tabToActivate = $('[data-toggle=tab][href="'+ tabToActivate + '"]');
        if ($tabToActivate.length) {
            $tabToActivate.trigger('click', { ignore: true });
        }

        if ($scroll.length) {
            scrollToAnchor($scroll);
        }
    }

    // ************************************************************************* //
    //  Initializations + Event listeners
    // ************************************************************************* //

    //
    // Store last clicked tab in local storage
    //
    $('#main-nav li').on('click', function(e) {
        e.preventDefault();
        localStorage['main-nav'] = $(this).find('a').attr('href');
    });

    //
    // Refresh typeahead source arrays when user changes tabs
    //
    $('#main-nav li').on('shown', function(e) {
        e.preventDefault();
        setUpWidgets();
    });

    //
    // Bind change events for options form checkboxes
    //
    $('#options-form input:checkbox').on('change', function(){
        setOptionDisplayState($(this));

        // Update localstorage
        var optionsArr = [];
        $('#options-form input:checkbox').each(function(i,el) {
            optionsArr.push($(el).is(':checked'));
        });
        localStorage.options = JSON.stringify(optionsArr);
    });

    //
    // Keyboard shortcut - 's' key
    // This brings the api search input into focus.
    //
    $(window).keyup(function(e) {
        // Listen for 's' key and focus search input if pressed
        if(e.keyCode === 83){ // 's'
            $('#api-tabview-filter input').focus();
        }
    });

    //
    // Keyboard shortcut - 'ctrl+left', 'ctrl+right', 'up', 'down'
    // These shortcuts offer sidebar navigation via keyboard,
    // *only* when sidebar or any element within has focus.
    //
    var nextIndex;
    $('#sidebar').keydown(function(e) {
        var $this = $(this);

        // Determine if the control/command key was pressed
        if(e.ctrlKey){
            if(e.keyCode === 37){ // left arrow
                $('#main-nav li a:first').tab('show');
            } else if(e.keyCode === 39){ // right arrow
                $('#main-nav li a:last').tab('show');
            }
        } else {
            if(e.keyCode === 40){ // down arrow
                if ($('#api-tabview-filter input').is(':focus')) {
                    // Scenario 1: We're focused on the search input; move down to the first li
                    $this.find('.tab-content .tab-pane.active li:first a').focus();
                } else if ($this.find('.tab-content .tab-pane.active li:last a').is(':focus')) {
                    // Scenario 2: We're focused on the last li; move up to search input
                    $('#api-tabview-filter input').focus();
                } else {
                    // Scenario 3: We're in the list but not on the last element, simply move down
                    nextIndex = $this
                    .find('.tab-content .tab-pane.active li')
                    .find('a:focus')
                    .parent('li').index() + 1;
                    $this.find('.tab-content .tab-pane.active li:eq('+nextIndex+') a').focus();
                }
                e.preventDefault(); // Stop page from scrolling
            } else if (e.keyCode === 38){ // up arrow
                if($('#api-tabview-filter input').is(':focus')) {
                    // Scenario 1: We're focused on the search input; move down to the last li
                    $this.find('.tab-content .tab-pane.active li:last a').focus();
                }else if($this.find('.tab-content .tab-pane.active li:first a').is(':focus')){
                    // Scenario 2: We're focused on the first li; move up to search input
                    $('#api-tabview-filter input').focus();
                }else{
                    // Scenario 3: We're in the list but not on the first element, simply move up
                    nextIndex = $this
                    .find('.tab-content .tab-pane.active li')
                    .find('a:focus')
                    .parent('li').index() - 1;
                    $this.find('.tab-content .tab-pane.active li:eq('+nextIndex+') a').focus();
                }
                e.preventDefault(); // Stop page from scrolling
            }
        }
    });

    function setUpHashChange() {
        $(window).on('hashchange', moveToWindowHash);
    }


    // ************************************************************************* //
    //  Immediate function calls
    // ************************************************************************* //

    setUpActiveTab();
    setUpOptionsCheckboxes();
    setUpWidgets();
    setUpHashChange();
    if (window.location.hash) {
        moveToWindowHash();
    }

});