Commit 1a2bacfb4b4b8f4d79df0335b4daf1d2cfa16d88

Authored by randx
1 parent b8425cf1

Feature: ajax load for tree commit log

app/assets/javascripts/application.js
... ... @@ -12,6 +12,7 @@
12 12 //= require jquery.cookie
13 13 //= require jquery.endless-scroll
14 14 //= require jquery.highlight
  15 +//= require jquery.waitforimages
15 16 //= require bootstrap-modal
16 17 //= require modernizr
17 18 //= require chosen-jquery
... ...
app/controllers/refs_controller.rb
... ... @@ -9,7 +9,7 @@ class RefsController < ApplicationController
9 9 before_filter :require_non_empty_project
10 10  
11 11 before_filter :ref
12   - before_filter :define_tree_vars, :only => [:tree, :blob, :blame]
  12 + before_filter :define_tree_vars, :only => [:tree, :blob, :blame, :logs_tree]
13 13 before_filter :render_full_content
14 14  
15 15 layout "project"
... ... @@ -46,6 +46,18 @@ class RefsController < ApplicationController
46 46 end
47 47 end
48 48  
  49 + def logs_tree
  50 + contents = @tree.contents
  51 + @logs = contents.map do |content|
  52 + file = params[:path] ? File.join(params[:path], content.name) : content.name
  53 + last_commit = @project.commits(@commit.id, file, 1).last
  54 + {
  55 + :file_name => content.name,
  56 + :commit => last_commit
  57 + }
  58 + end
  59 + end
  60 +
49 61 def blob
50 62 if @tree.is_blob?
51 63 if @tree.text?
... ... @@ -79,6 +91,15 @@ class RefsController < ApplicationController
79 91 @commit = project.commit(@ref)
80 92 @tree = Tree.new(@commit.tree, project, @ref, params[:path])
81 93 @tree = TreeDecorator.new(@tree)
  94 + @hex_path = Digest::SHA1.hexdigest(params[:path] || "/")
  95 +
  96 + if params[:path]
  97 + @history_path = tree_file_project_ref_path(@project, @ref, params[:path])
  98 + @logs_path = logs_file_project_ref_path(@project, @ref, params[:path])
  99 + else
  100 + @history_path = tree_project_ref_path(@project, @ref)
  101 + @logs_path = logs_tree_project_ref_path(@project, @ref)
  102 + end
82 103 rescue
83 104 return render_404
84 105 end
... ...
app/views/refs/_tree.html.haml
... ... @@ -13,7 +13,7 @@
13 13 = render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree }
14 14 - else
15 15 - contents = tree.contents
16   - %table#tree-slider.bordered-table.table
  16 + %table#tree-slider.bordered-table.table{:class => "table_#{@hex_path}" }
17 17 %thead
18 18 %th Name
19 19 %th Last Update
... ... @@ -48,17 +48,18 @@
48 48 - else
49 49 = simple_format(content.data)
50 50  
51   -- if params[:path]
52   - - history_path = tree_file_project_ref_path(@project, @ref, params[:path])
53   -- else
54   - - history_path = tree_project_ref_path(@project, @ref)
55 51 :javascript
56 52 $(function(){
57 53 $('select#branch').selectmenu({style:'popup', width:200});
58 54 $('select#tag').selectmenu({style:'popup', width:200});
59 55 $('.project-refs-select').chosen();
60 56  
61   - history.pushState({ path: this.path }, '', "#{history_path}")
  57 + history.pushState({ path: this.path }, '', "#{@history_path}");
  58 +
  59 + });
  60 +
  61 + $(window).load(function(){
  62 + $.ajax({type: "GET", url: '#{@logs_path}', dataType: "script"});
62 63 });
63 64  
64 65  
... ...
app/views/refs/_tree_commit.html.haml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +- if tm
  2 + %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
  3 += link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
... ...
app/views/refs/_tree_item.html.haml
1 1 - file = params[:path] ? File.join(params[:path], content.name) : content.name
2   -- content_commit = @project.commits(@commit.id, file, 1).last
3   -- return unless content_commit
4   -%tr{ :class => "tree-item", :url => tree_file_project_ref_path(@project, @ref, file) }
  2 +%tr{ :class => "tree-item file_#{Digest::SHA1.hexdigest(content.name)}", :url => tree_file_project_ref_path(@project, @ref, file) }
5 3 %td.tree-item-file-name
6 4 - if content.is_a?(Grit::Blob)
7 5 - if content.text?
8   - = image_tag "file_txt.png"
  6 + = image_tag "file_txt.png", :class => "tree-ico"
9 7 - elsif content.image?
10   - = image_tag "file_img.png"
  8 + = image_tag "file_img.png", :class => "tree-ico"
11 9 - else
12   - = image_tag "file_bin.png"
  10 + = image_tag "file_bin.png", :class => "tree-ico"
13 11 - else
14   - = image_tag "file_dir.png"
  12 + = image_tag "file_dir.png", :class => "tree-ico"
15 13 = link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true
16 14 %td.tree_time_ago.cgray
17   - = time_ago_in_words(content_commit.committed_date)
18   - ago
19 15 %td.tree_commit
20   - - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name)
21   - - if tm
22   - %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
23   - = link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
... ...
app/views/refs/logs_tree.js.haml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +- @logs.each do |content_data|
  2 + - file_name = content_data[:file_name]
  3 + - content_commit = content_data[:commit]
  4 + - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name)
  5 +
  6 + :plain
  7 + var row = $("table.table_#{@hex_path} tr.file_#{Digest::SHA1.hexdigest(file_name)}");
  8 + row.find("td.tree_time_ago").html('#{escape_javascript(time_ago_in_words(content_commit.committed_date))} ago');
  9 + row.find("td.tree_commit").html('#{escape_javascript(render("tree_commit", :tm => tm, :content_commit => content_commit))}');
... ...
app/views/refs/tree.js.haml
... ... @@ -2,3 +2,8 @@
2 2 $("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}");
3 3 $("#tree-content-holder").show("slide", { direction: "right" }, 150);
4 4 $('.project-refs-form #path').val("#{params[:path]}");
  5 +
  6 +
  7 + $('#tree-slider').waitForImages(function() {
  8 + $.ajax({type: "GET", url: '#{@logs_path}', dataType: "script"});
  9 + });
... ...
config/routes.rb
... ... @@ -117,6 +117,8 @@ Gitlab::Application.routes.draw do
117 117  
118 118 member do
119 119 get "tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ }
  120 + get "logs_tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ }
  121 +
120 122 get "blob",
121 123 :constraints => {
122 124 :id => /[a-zA-Z.0-9\/_\-]+/,
... ... @@ -132,6 +134,14 @@ Gitlab::Application.routes.draw do
132 134 :path => /.*/
133 135 }
134 136  
  137 + # tree viewer
  138 + get "logs_tree/:path" => "refs#logs_tree",
  139 + :as => :logs_file,
  140 + :constraints => {
  141 + :id => /[a-zA-Z.0-9\/_\-]+/,
  142 + :path => /.*/
  143 + }
  144 +
135 145 # blame
136 146 get "blame/:path" => "refs#blame",
137 147 :as => :blame_file,
... ...
vendor/assets/javascripts/jquery.waitforimages.js 0 → 100644
... ... @@ -0,0 +1,144 @@
  1 +/*
  2 + * waitForImages 1.4
  3 + * -----------------
  4 + * Provides a callback when all images have loaded in your given selector.
  5 + * http://www.alexanderdickson.com/
  6 + *
  7 + *
  8 + * Copyright (c) 2011 Alex Dickson
  9 + * Licensed under the MIT licenses.
  10 + * See website for more info.
  11 + *
  12 + */
  13 +
  14 +;(function($) {
  15 + // Namespace all events.
  16 + var eventNamespace = 'waitForImages';
  17 +
  18 + // CSS properties which contain references to images.
  19 + $.waitForImages = {
  20 + hasImageProperties: [
  21 + 'backgroundImage',
  22 + 'listStyleImage',
  23 + 'borderImage',
  24 + 'borderCornerImage'
  25 + ]
  26 + };
  27 +
  28 + // Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded.
  29 + $.expr[':'].uncached = function(obj) {
  30 + // Ensure we are dealing with an `img` element with a valid `src` attribute.
  31 + if ( ! $(obj).is('img[src!=""]')) {
  32 + return false;
  33 + }
  34 +
  35 + // Firefox's `complete` property will always be`true` even if the image has not been downloaded.
  36 + // Doing it this way works in Firefox.
  37 + var img = document.createElement('img');
  38 + img.src = obj.src;
  39 + return ! img.complete;
  40 + };
  41 +
  42 + $.fn.waitForImages = function(finishedCallback, eachCallback, waitForAll) {
  43 +
  44 + // Handle options object.
  45 + if ($.isPlainObject(arguments[0])) {
  46 + eachCallback = finishedCallback.each;
  47 + waitForAll = finishedCallback.waitForAll;
  48 + finishedCallback = finishedCallback.finished;
  49 + }
  50 +
  51 + // Handle missing callbacks.
  52 + finishedCallback = finishedCallback || $.noop;
  53 + eachCallback = eachCallback || $.noop;
  54 +
  55 + // Convert waitForAll to Boolean
  56 + waitForAll = !! waitForAll;
  57 +
  58 + // Ensure callbacks are functions.
  59 + if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
  60 + throw new TypeError('An invalid callback was supplied.');
  61 + };
  62 +
  63 + return this.each(function() {
  64 + // Build a list of all imgs, dependent on what images will be considered.
  65 + var obj = $(this),
  66 + allImgs = [];
  67 +
  68 + if (waitForAll) {
  69 + // CSS properties which may contain an image.
  70 + var hasImgProperties = $.waitForImages.hasImageProperties || [],
  71 + matchUrl = /url\((['"]?)(.*?)\1\)/g;
  72 +
  73 + // Get all elements, as any one of them could have a background image.
  74 + obj.find('*').each(function() {
  75 + var element = $(this);
  76 +
  77 + // If an `img` element, add it. But keep iterating in case it has a background image too.
  78 + if (element.is('img:uncached')) {
  79 + allImgs.push({
  80 + src: element.attr('src'),
  81 + element: element[0]
  82 + });
  83 + }
  84 +
  85 + $.each(hasImgProperties, function(i, property) {
  86 + var propertyValue = element.css(property);
  87 + // If it doesn't contain this property, skip.
  88 + if ( ! propertyValue) {
  89 + return true;
  90 + }
  91 +
  92 + // Get all url() of this element.
  93 + var match;
  94 + while (match = matchUrl.exec(propertyValue)) {
  95 + allImgs.push({
  96 + src: match[2],
  97 + element: element[0]
  98 + });
  99 + };
  100 + });
  101 + });
  102 + } else {
  103 + // For images only, the task is simpler.
  104 + obj
  105 + .find('img:uncached')
  106 + .each(function() {
  107 + allImgs.push({
  108 + src: this.src,
  109 + element: this
  110 + });
  111 + });
  112 + };
  113 +
  114 + var allImgsLength = allImgs.length,
  115 + allImgsLoaded = 0;
  116 +
  117 + // If no images found, don't bother.
  118 + if (allImgsLength == 0) {
  119 + finishedCallback.call(obj[0]);
  120 + };
  121 +
  122 + $.each(allImgs, function(i, img) {
  123 +
  124 + var image = new Image;
  125 +
  126 + // Handle the image loading and error with the same callback.
  127 + $(image).bind('load.' + eventNamespace + ' error.' + eventNamespace, function(event) {
  128 + allImgsLoaded++;
  129 +
  130 + // If an error occurred with loading the image, set the third argument accordingly.
  131 + eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load');
  132 +
  133 + if (allImgsLoaded == allImgsLength) {
  134 + finishedCallback.call(obj[0]);
  135 + return false;
  136 + };
  137 +
  138 + });
  139 +
  140 + image.src = img.src;
  141 + });
  142 + });
  143 + };
  144 +})(jQuery);
... ...