/**
* @file * SiteDiff report behaviors. */
'use strict';
/* global $ */ /**
* SiteDiff namespace. */
var SiteDiff
= SiteDiff
|| {};
/**
* SiteDiff global map of diffs. */
SiteDiff.diffs = SiteDiff.diffs || {};
SiteDiff.currentDiff = -1;
/**
* Scrolls the document to the said position. * * @param options * Object specifying various options. * * x: X position. * y: Y position. * animate: Whether to animate. * callback: A function to call after scrolling. */
SiteDiff.scrollToPosition = function (options) {
// Compute vertical and horizontal adjustments, if any. // Example: Fixed elements, etc. var xFix = 0; var yFix = 0 - 100; // Determine final x and y offsets. var x = parseInt(options.x) + xFix; x = Math.max(x, 0); var y = parseInt(options.y) + yFix; y = Math.max(y, 0); // Perform the scroll with or without animation. window.scrollTo(x, y); // Trigger a callback, if any. if (options.callback) { options.callback(); }
};
/**
* Scrolls to a DOM element on the page. * * @param el * The DOM element. * * @param options * Object specifying various options. * * "callback" to trigger after scrolling. */
SiteDiff.scrollToElement = function (el, options) {
options = options || {}; var callback = options.callback || function () {}; // See if the element exists. var $el = $(el).first(); if ($el.length == 1) { // Inject callback to focus on the element we scroll to. options.x = 0; options.y = $el.offset().top; options.callback = function () { $el.focus(); callback.call(el); }; SiteDiff.scrollToPosition(options); }
};
/**
* Initialize behaviors. */
SiteDiff.init = function () {
// On the overview page. switch ($(document.body).data('page')) { case 'overview': SiteDiff.initFilterForm(); break; case 'diff': SiteDiff.jumpToFirstDiff(); break; }
};
/**
* Initializes report filters and overlay. */
SiteDiff.initFilterForm = function () {
SiteDiff.initDiffArray(); SiteDiff.initStatusFilter(); SiteDiff.initSearchFilter(); SiteDiff.initOverlay(); SiteDiff.initClickHandlers();
};
/**
* Initialize global diff array * */
SiteDiff.initDiffArray = function() {
SiteDiff.diffs = $('.button-diff').map(function (i, element) { var $el = $(element); $el.data('diffindex', i); return { diff: $el.attr('href'), element: $el, index: i, path: $el.parents('.description').find('.path').text() }; });
};
/**
* Initializes the "status" filter. */
SiteDiff.initStatusFilter = function () {
$('.form-item--status input') .on('change', function () { // Get a list of applied filters. var appliedFilters = $('.form-item--status input:checked') .map(function () { return this.getAttribute('value'); // applied.push(this.getAttribute('value')); }); // Show only matching results, hide the rest. $('#sitediff-report') .find('.sitediff-result') .each(function () { var $row = $(this); var status = $row.data('status'); if ( // Row matches applied filters. $.inArray(status, appliedFilters) > -1 || // No filters are applied. appliedFilters.length === 0 ) { $row.removeAttr('hidden'); } else { $row.attr('hidden', 'hidden'); } }); });
};
/**
* Initializes the "search" filter. */
SiteDiff.initSearchFilter = function () {
$('#input-search') .on('change keyup', function () { var keyword = $(this).val().toLowerCase(); // Filter the records. // TODO: Trigger one event per 250ms. $('#sitediff-report') .find('.sitediff-result') .each(function () { var $row = $(this); var path = $row.find('.path').text(); // If keyword matches, keep the row visible. if (path.toLowerCase().indexOf(keyword) > -1) { $row.attr('hidden', null); } else { $row.attr('hidden', 'hidden'); } }); });
};
/**
* Set up the diff overlay to be displayed. */
SiteDiff.initOverlay = function () {
if (SiteDiff.diffs.length <= 0) return; // add overlay $('body').append($( '<div class="overlay" style="display: none;"><div class="overlay__inner"><header>' + '<div class="path"></div>' + '<div class="prev"><a href="#" title="Previous diff (left arrow)">< Prev</a></div>' + '<div class="next"><a href="#" title="Next diff (right arrow)">Next ></a></div>' + '<div class="exit"><a href="#" title="Close diff display (Esc)">Close</a></div>' + '</header><article></article></div></div>')); // add header click handlers $('.overlay header .exit').click(function (event) { event.preventDefault(); SiteDiff.destroyOverlay(); }); $('.overlay header .prev').click(function (event) { event.preventDefault(); SiteDiff.prevDiff(); }); $('.overlay header .next').click(function (event) { event.preventDefault(); SiteDiff.nextDiff(); });
};
/**
* Set up click handlers for all diff buttons */
SiteDiff.initClickHandlers = function () {
SiteDiff.diffs.each( function (i, diff) { diff.element.click({index: i}, function (event) { event.preventDefault(); SiteDiff.openOverlay(event.data.index); }); });
};
/**
* Set up key handlers for overlay. */
SiteDiff.initKeyHandlers = function () {
$(document).keyup(function (event) { switch (event.which) { case 37: SiteDiff.prevDiff(); break; case 39: SiteDiff.nextDiff(); break; case 27: SiteDiff.destroyOverlay(); break; } });
};
/**
* Remove overlay key handlers. */
SiteDiff.removeKeyHandlers = function () {
$(document).off('keyup');
};
/**
* Open overlay for the diff identified by the `index`. * * @param integer index * The index of the diff to be viewed. */
SiteDiff.openOverlay = function (index) {
SiteDiff.currentDiff = index; SiteDiff.showDiff(); SiteDiff.initKeyHandlers(); $('.overlay').fadeIn(300);
};
/**
* Create the iframe to display the current diff. */
SiteDiff.showDiff = function () {
var diff = SiteDiff.diffs[SiteDiff.currentDiff]; var iframe = '<iframe src="' + diff.diff + '"></iframe>'; $('.overlay header .path').text(diff.path); SiteDiff.setPrevNext(); $('.overlay article').html(iframe);
};
/**
* Hide the overlay and clean up. */
SiteDiff.destroyOverlay = function () {
$('.overlay article').empty(); SiteDiff.removeKeyHandlers(); $('.overlay').fadeOut(300, SiteDiff.scrollToButton);
};
/**
* Display the previous diff. */
SiteDiff.prevDiff = function () {
if (SiteDiff.currentDiff > 0) { SiteDiff.currentDiff--; SiteDiff.showDiff(); }
};
/**
* Display the next diff. */
SiteDiff.nextDiff = function () {
if (SiteDiff.currentDiff < SiteDiff.diffs.length - 1) { SiteDiff.currentDiff++; SiteDiff.showDiff(); }
};
/**
* Enable or disable prev and next buttons based on current diff. */
SiteDiff.setPrevNext = function () {
if (SiteDiff.currentDiff <= 0) { // set prev disabled $('.overlay header .prev').addClass('disabled'); } else { $('.overlay header .prev.disabled').removeClass('disabled'); } if (SiteDiff.currentDiff >= SiteDiff.diffs.length - 1) { // set next disabled $('.overlay header .next').addClass('disabled'); } else { $('.overlay header .next.disabled').removeClass('disabled'); }
};
/**
* Scroll to the button associated with the current diff. */
SiteDiff.scrollToButton = function () {
var $diffButton = SiteDiff.diffs[SiteDiff.currentDiff].element; if (! SiteDiff.isElementVisible($diffButton)) { SiteDiff.scrollToElement($diffButton); }
};
/**
* Check if an element is at least partly visible. * @param element */
SiteDiff.isElementVisible = function (element) {
var topVisible = $(window).scrollTop(); var bottomVisible = topVisible + $(window).height(); var elemTop = $(element).offset().top; var elemBottom = elemTop + $(element).height(); return ((elemBottom <= bottomVisible) && (elemTop >= topVisible));
};
/**
* Jumps to the first diff on the page. */
SiteDiff.jumpToFirstDiff = function () {
// Get the first diff hunk. var $diff = $('#diff-container') .find('.del, .ins') .first(); if ($diff.length === 0) { return; } // Scroll the window to it! setTimeout(function () { SiteDiff.scrollToElement($diff[0]); }, 250);
};
$(document).ready(SiteDiff.init);