cSvn-UI for SVN Repositories

cGit-UI – is a web interface for Subversion (SVN) Repositories. cSvn CGI script is writen in C and therefore it's fast enough

15 Commits   0 Branches   1 Tag

function get_query_parameter( name, url = window.location.href )
{
  name = name.replace( /[\[\]]/g, '\\$&' );
  var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), results = regex.exec(url);
  if( !results ) return null;
  if( !results[2] ) return '';
  return decodeURIComponent( results[2].replace(/\+/g, ' ') );
}

function removeParam( query_string, param ) {
  if( query_string !== "" )
  {
    var query = "";
    var params = decodeURIComponent(query_string).split('&');
    var i, name;
    for( i = 0; i < params.length; ++i ) {
      name = params[i].split('=');
      if( name[0] !== param ) {
        query = query + name[0] + '=' + name[1] + '&';
      }
    }
    if( query !== "" ) {
      query = query.substring( 0, query.length - 1 );
    }
  }
  return query;
}

function search() {

  var text = document.querySelector( '.search-form input' ).value;

  if( page_type === "repo" && text )
  {
    var path         = window.location.pathname;
    var query_string = window.location.search;
    var revision     = encodeURIComponent( text );

    if( query_string ) {
      query_string = query_string.substring( 1, query_string.length );
      query_string = removeParam( query_string, "rev" );
      query_string = removeParam( query_string, "op" );
    }

    revision = revision.toLowerCase();
    if( revision === "head" ) {
      revision = "0";
    }

    document.querySelector( '.search-form input' ).value = "";

    window.location.pathname = path;
    if( query_string ) {
      window.location.search = "?rev=" + revision + "&" + query_string;
    } else {
      window.location.search = "?rev=" + revision;
    }
  }
  if( page_type === "repolist" && text )
  {
    var path         = window.location.pathname;
    var query_string = window.location.search;
    var repo_path    = encodeURIComponent( text );

    if( query_string ) {
      query_string = query_string.substring( 1, query_string.length );
      query_string = removeParam( query_string, "search" );
    }

    document.querySelector( '.search-form input' ).value = "";

    window.location.pathname = path;
    if( query_string ) {
      window.location.search = "?search=" + repo_path + "&" + query_string;
    } else {
      window.location.search = "?search=" + repo_path;
    }
  }
}

function show_hide_direction() {
  var direction = document.querySelector( '.direction' );
  var prev = document.querySelector( '.prev-direction' );
  var next = document.querySelector( '.next-direction' );
  var ofs = get_query_parameter( 'ofs' );

  if( direction )
  {
    if( num_of_repos < page_size ) {
      direction.style.display = 'none';
    }
    else {
      direction.style.display = 'block';

      if( (num_of_repos - ofs) < page_size ) { next.style.display = 'none';  }
      else                                   { next.style.display = 'block'; }

      if( ofs > 0 ) { prev.style.display = 'block'; }
      else          { prev.style.display = 'none';  }
    }
  }
}

/***************************************************************
  We dont want to use tooltips from any foreign engine.
  NOTE:
    All tooltips we attached to the '.pusher' element.
 */
function fade_out( element ) {
  /* постепенно исчезать */
  var op = 1;  /* initial opacity */
  var timer = setInterval( function() {
    if( op <= 0.1 ) {
      clearInterval( timer );
      element.style.display = 'none';
    }
    element.style.opacity = op;
    element.style.filter = 'alpha(opacity=' + op * 100 + ")";
    op -= op * 0.1;
  }, 10);
}

function fade_in( element ) {
  /* постепенно появляться */
  var op = 0.1;  /* initial opacity */
  element.style.display = 'block';
  var timer = setInterval( function() {
    if( op >= 1 ) {
      clearInterval( timer );
    }
    element.style.opacity = op;
    element.style.filter = 'alpha(opacity=' + op * 100 + ")";
    op += op * 0.1;
  }, 10);
}

function simple_tooltip( target_items, name ) {
  var pusher = document.querySelector( '.pusher' );
  var items = document.querySelectorAll( target_items );

  items.forEach( function( item, i, array ) {
    var html = "<div class='" + name +
               "' id='" + name + "-" + i +
               "'><p>" + item.getAttribute('title') +
               "</p></div>";
    pusher.insertAdjacentHTML( 'beforeend', html );

    var tooltip = document.querySelector( "#" + name + "-" + i );

    item.removeAttribute( 'title' );
    item.addEventListener( "mouseover", function( event ) {
      fade_in( tooltip );
    });
    item.addEventListener( "mousemove", function( kmouse ) {
      /***********************************************
         c - client is a browser window;
         o - offset from page (document) coordinate.
         t - tooltip;
       ***********************************************/
      var cW  = window.innerWidth;
      var cH  = window.innerHeight;
      var cX  = kmouse.clientX;
      var cY  = kmouse.clientY;
      var tW  = tooltip.offsetWidth;
      var tH  = tooltip.offsetHeight;
      var oX;
      var oY;
      if( ( cW - cX ) < ( tW + 15) ) { oX = - 15 - tW; } else { oX = 15; }
      if( ( cH - cY ) < ( tH + 15) ) { oY = - 15 - tH; } else { oY = 15; }
      tooltip.style.left = kmouse.pageX + oX + 'px';
      tooltip.style.top  = kmouse.pageY + oY - pusher.offsetTop + 'px';
    });
    item.addEventListener( "mouseout", function( event ) {
      fade_out( tooltip );
    });
  });
}

/*******************************************
  Truncate descriptions or commit messages:
 */
function trunc(el) {
  if( el.classList.contains('trunc') )
  {
    el.classList.remove('trunc');
  }
  else
  {
    el.classList.add('trunc');
  }
}

/*****************************
  Show/Hide right popup menu:
 */
function show_sidebar(event) {
  var sidebar = document.querySelector('.csvn-popup-menu-bars');
  sidebar.classList.add("visible");

  /* change icon bars->times */
  var icon = document.querySelector('.csvn-main-menu-bars .las');
  icon.classList.remove("la-bars");
  icon.classList.add("la-times");

  var sb = document.querySelector('.csvn-main-menu-bars');
  sb.onclick = hide_sidebar;

  if( !event )
    event = window.event;
  event.stopPropagation();
}

function hide_sidebar(event) {
  var sidebar = document.querySelector('.csvn-popup-menu-bars');
  sidebar.classList.remove("visible");

  /* change icon times->bars */
  var icon = document.querySelector('.csvn-main-menu-bars .las');
  icon.classList.remove("la-times");
  icon.classList.add("la-bars");

  var sb = document.querySelector('.csvn-main-menu-bars');
  sb.onclick = show_sidebar;

  if( !event )
    event = window.event;
  event.stopPropagation();
}

/********************
  on Document Ready:
 */
document.addEventListener('DOMContentLoaded', function() {

  /* initialy show main popup menu on click */
  var sb = document.querySelector('.csvn-main-menu-bars');
  sb.onclick = show_sidebar;

  var items = document.querySelectorAll('.csvn-popup-menu-bars > .item');
  items.forEach( function(item) {
    /* each item should hide main popup menu */
    item.onclick = hide_sidebar;
  });

  window.addEventListener('resize', function(event) {
    var width = window.innerWidth;
    if( width > 768 ) {
      /* change icon times->bars */
      var icon = document.querySelector('.csvn-main-menu-bars .las');
      icon.classList.remove("la-times");
      icon.classList.add("la-bars");

      /* hide main popup menu */
      var sidebar = document.querySelector('.csvn-popup-menu-bars');
      sidebar.classList.remove("visible");
    }
    else {
      /* show main popup menu on click after resize */
      var sb = document.querySelector('.csvn-main-menu-bars');
      sb.onclick = show_sidebar;
    }
  });


  /*************************************************
    Skip line numbering in the Markdown content:
   */
  var mdcode = document.querySelectorAll('.markdown-content code');
  mdcode.forEach( function( block ) {
    block.classList.add('nohljsln');
  });

  /*************************************************
    Skip line numbering in the Blame content:
   */
  var blame = document.querySelectorAll('.blame code');
  blame.forEach( function( block ) {
    block.classList.add('nohljsln');
  });

  /*************************************************
    Initialize highlight.js with hljs line numbers:
   */
  hljs.configure({
    useBR: false,
    /* languages: 'Makefile Bash C Perl',*/
    tabReplace: '<span class="tab">&lt;------&gt;</span>'
  });

  var preformated = document.querySelectorAll('pre code');
  preformated.forEach( function( block ) {
    hljs.highlightBlock( block ); /* init hljs */
    if( false === block.classList.contains('nohljsln') )
    {
      hljs.lineNumbersBlock( block, { /* init hljs line numbers with options: */
        startFrom: 0,
        singleLine: false
      });
    }
  });


  /***************************************************************
    Pseudo paragraphs inserted before headers to skip the stiky
    Main Menu Bar height when we going to anchor in a Markdown
    document.
   */
  var mdheaders = document.querySelectorAll('.markdown-content h1,h2,h3,h4,h5,h6');
  mdheaders.forEach( function( header ) {
    var content = header.textContent;
    var id = content.toLowerCase().replaceAll( ' ', '-' ).replaceAll( ':', '' );

    var ph = document.createElement( 'p' );
    ph.setAttribute( 'class', 'headers-anchor' )
    ph.setAttribute( 'id', id );
    header.parentNode.insertBefore( ph, header );
  });

  var input = document.querySelector( '.search-form input' );
  input.addEventListener( "keyup", function( event ) {
    if( event.key === "Enter" ) {
      search();
    }
  });

  simple_tooltip( "[title]", "tooltip" );
  show_hide_direction();
});