Newer
Older
GitBucket / src / main / twirl / gitbucket / core / repo / blob.scala.html
@(branch: String,
  repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
  pathList: List[String],
  content: gitbucket.core.util.JGitUtil.ContentInfo,
  latestCommit: gitbucket.core.util.JGitUtil.CommitInfo,
  hasWritePermission: Boolean,
  isBlame: Boolean)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@gitbucket.core.html.main(s"${(repository.name :: pathList).mkString("/")} at ${helpers.encodeRefName(branch)} - ${repository.owner}/${repository.name}", Some(repository)) {
  @gitbucket.core.html.menu("files", repository){
    <div class="head">
      <div class="pull-right hide-if-blame"><div class="btn-group">
        <a href="@helpers.url(repository)/find/@helpers.encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t">Find file</a>
      </div></div>
      <div class="line-age-legend">
        <span>Newer</span>
        <ol>
            <li class="heat1"></li>
            <li class="heat2"></li>
            <li class="heat3"></li>
            <li class="heat4"></li>
            <li class="heat5"></li>
            <li class="heat6"></li>
            <li class="heat7"></li>
            <li class="heat8"></li>
            <li class="heat9"></li>
            <li class="heat10"></li>
        </ol>
        <span>Older</span>
      </div>
      @gitbucket.core.helper.html.branchcontrol(
        branch,
        repository,
        hasWritePermission
      ){
        @repository.branchList.map { x =>
          <li><a href="@helpers.url(repository)/blob/@helpers.encodeRefName(x)/@pathList.mkString("/")">@gitbucket.core.helper.html.checkicon(x == branch) @x</a></li>
        }
      }
      <a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)">@repository.name</a> /
      @pathList.zipWithIndex.map { case (section, i) =>
        @if(i == pathList.length - 1){
          @section
        } else {
          <a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
        }
      }
    </div>
    <div class="box-header">
      @helpers.avatar(latestCommit, 28)
      @helpers.user(latestCommit.authorName, latestCommit.authorEmailAddress, "username strong")
      <span class="muted">@gitbucket.core.helper.html.datetimeago(latestCommit.commitTime)</span>
      <a href="@helpers.url(repository)/commit/@latestCommit.id" class="commit-message">@helpers.link(latestCommit.summary, repository)</a>
      <div class="btn-group pull-right">
        @if(hasWritePermission && content.viewType == "text" && repository.branchList.contains(branch)){
          <a class="btn btn-sm btn-default" href="@helpers.url(repository)/edit/@helpers.encodeRefName(branch)/@pathList.mkString("/")">Edit</a>
        }
        <a class="btn btn-sm btn-default" href="@helpers.url(repository)/raw/@latestCommit.id/@pathList.mkString("/")">Raw</a>
        @if(content.viewType == "text"){
          <a class="btn btn-sm btn-default blame-action" href="@helpers.url(repository)/blame/@latestCommit.id/@pathList.mkString("/")" data-url="@helpers.url(repository)/get-blame/@latestCommit.id/@pathList.mkString("/")" data-repository="@helpers.url(repository)">Blame</a>
        }
        <a class="btn btn-sm btn-default" href="@helpers.url(repository)/commits/@helpers.encodeRefName(branch)/@pathList.mkString("/")">History</a>
        @if(hasWritePermission && repository.branchList.contains(branch)){
          <a class="btn btn-sm btn-danger" href="@helpers.url(repository)/remove/@helpers.encodeRefName(branch)/@pathList.mkString("/")">Delete</a>
        }
      </div>
    </div>
      @if(content.viewType == "text"){
        @defining(helpers.isRenderable(pathList.reverse.head)){ isRenderable =>
          @if(!isBlame && isRenderable) {
            <div class="box-content-bottom markdown-body" style="padding-left: 20px; padding-right: 20px;">
              @helpers.renderMarkup(pathList, content.content.get, branch, repository, false, false, true)
            </div>
          } else {
            <div class="box-content-bottom">
              <pre class="prettyprint linenums blob @if(!isRenderable){ no-renderable } ">@content.content.map(_.replaceAll("^(\r?\n)", "$1$1"))</pre>
            </div>
          }
        }
      }
      @if(content.viewType == "image"){
        <div class="box-content-bottom">
          <img src="@helpers.url(repository)/raw/@helpers.encodeRefName(branch)/@pathList.mkString("/")"/>
        </div>
      }
      @if(content.viewType == "large" || content.viewType == "binary"){
        <div class="box-content-bottom" style="text-align: center; padding-top: 20px; padding-bottom: 20px;">
          <a href="@helpers.url(repository)/raw/@helpers.encodeRefName(branch)/@pathList.mkString("/")">View Raw</a><br>
          <br>
          (Sorry about that, but we can't show files that are this big right now)
        </div>
      }
  }
}
<script src="@helpers.assets/vendors/jquery/jquery.ba-hashchange.js"></script>
<script>
$(window).load(function(){
  $(window).hashchange(function(){
    updateHighlighting();
  }).hashchange();

  var pre = $('pre.prettyprint');
  function updateSourceLineNum(){
    $('.source-line-num').remove();
    var pos = pre.find('ol.linenums').position();
    if(pos){
      $('<div class="source-line-num">').css({
        height  : pre.height(),
        width   : '48px',
        cursor  : 'pointer',
        position: 'absolute',
        top     : pos.top + 'px',
        left    : pos.left + 'px'
      }).click(function(e){
        $(window).hashchange(function(){})
        var pos = $(this).data("pos");
        if(!pos){
          pos = $('ol.linenums li').map(function(){ return { id: $(this).attr("id"), top: $(this).position().top} }).toArray();
          $(this).data("pos",pos);
        }
        for(var i = 0; i < pos.length-1; i++){
          if(pos[i + 1].top > e.pageY){
            break;
          }
        }
        var line = pos[i].id.replace(/^L/,'');
        var hash = location.hash;
        if(e.shiftKey == true && hash.match(/#L\d+(-L\d+)?/)){
          var lines = hash.split('-');
          location.hash = lines[0] + '-L' + line;
        } else {
          var p = $("#L"+line).attr('id',"");
          location.hash = '#L' + line;
          p.attr('id','L'+line);
        }
      }).appendTo(pre);
    }
  }
  var repository = $('.blame-action').data('repository');
  $('.blame-action').click(function(e){
    if(history.pushState && $('pre.prettyprint.no-renderable').length){
      e.preventDefault();
      history.pushState(null, null, this.href);
      updateBlame();
    }
  });

  function updateBlame(){
    var m = /\/(blame|blob)(\/.*)$/.exec(location.href);
    var mode = m[1];
    $('.blame-action').toggleClass("active", mode=='blame').attr('href', repository + (m[1] == 'blame' ? '/blob' : '/blame') + m[2]);
    if(pre.parents("div.box-content-bottom").find(".blame").length){
      pre.parent().toggleClass("blame-container", mode == 'blame');
      updateSourceLineNum();
      return;
    }
    if(mode=='blob'){
      updateSourceLineNum();
      return;
    }
    $(document.body).toggleClass('no-box-shadow', document.body.style.boxShadow===undefined);
    $('.blame-action').addClass("active");
    var base = $('<div class="blame">').css({height: pre.height()}).prependTo(pre.parents("div.box-content-bottom"));
    base.parent().addClass("blame-container");
    updateSourceLineNum();
    $.get($('.blame-action').data('url')).done(function(data){
      var blame = data.blame;
      var index = [];
      for(var i = 0; i < blame.length; i++){
        for(var j = 0; j < blame[i].lines.length; j++){
          index[blame[i].lines[j]] = blame[i];
        }
      }
      var blame, lastDiv, now = new Date().getTime();

      $('pre.prettyprint ol.linenums li').each(function(i, e){
        var p = $(e).position();
        var h = $(e).height();
        if(blame == index[i]){
          lastDiv.css("min-height",(p.top + h + 1) - lastDiv.position().top);
        }else{
          $(e).addClass('blame-sep')
          blame = index[i];
          var sha = $('<div class="blame-sha">')
             .append($('<a>').attr("href", data.root + '/commit/' + blame.id).text(blame.id.substr(0,7)));
          if(blame.prev){
             sha.append($('<br />'))
             .append($('<a class="muted-link">').text('prev').attr("href", data.root + '/blame/' + blame.prev + '/' + (blame.prevPath || data.path)));
          }
          lastDiv = $('<div class="blame-info">')
           .addClass('heat' + Math.min(10, Math.max(1, Math.ceil((now - blame.commited) / (24 * 3600 * 1000 * 70)))))
           .toggleClass('blame-last', blame.id == data.last)
           .data('line', (i + 1))
           .css({
             "top"        : p.top + 'px',
             "min-height" : h + 'px'
           })
           .append(sha)
           .append($(blame.avatar).addClass('avatar').css({"float": "left"}))
           .append($('<div class="blame-commit-title">').text(blame.message))
           .append($('<div class="muted">').html(blame.author + " authed " + blame.authed))
           .appendTo(base);
        }
      });
    });
    return false;
  };
  updateBlame();
});

var scrolling = false;

/**
 * Hightlight lines which are specified by URL hash.
 */
function updateHighlighting(){
  var hash = location.hash;
  if(hash.match(/#L\d+(-L\d+)?/)){
    $('li.highlight').removeClass('highlight');
    var lines = hash.substr(1).split('-');
    if(lines.length == 1){
      $('#' + lines[0]).addClass('highlight');
      if(!scrolling){
        $(window).scrollTop($('#' + lines[0]).offset().top - 40);
      }
    } else if(lines.length > 1){
      var start = parseInt(lines[0].substr(1));
      var end   = parseInt(lines[1].substr(1));
      for(var i = start; i <= end; i++){
        $('#L' + i).addClass('highlight');
      }
      if(!scrolling){
        $(window).scrollTop($('#L' + start).offset().top - 40);
      }
    }
    scrolling = true;
  }
}
</script>