gitdocs 0.4.3 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -11,4 +11,18 @@
11
11
 
12
12
  * Javascript fixes for front-end
13
13
 
14
- 0.4.4 (Not Yet Released)
14
+ 0.4.4 (12/14/2012)
15
+
16
+ * Lock to eventmachine 1.0
17
+ * Use mime-types over file for better platform support
18
+ * Remove signal handling and just leverage EM start/stop
19
+ * Adds table sorting and file+folder icons in web front-end
20
+
21
+ 0.4.5 (12/14/2012)
22
+
23
+ * Enables logs in ~/.gitdocs/log for easier debugging
24
+ * Fix issue with default sort order on web interface
25
+ * Fixes major mime type detection issues
26
+ * Fixes rendering issues
27
+
28
+ 0.4.6 (Not yet released)
data/README.md CHANGED
@@ -67,7 +67,7 @@ You need to start gitdocs in order for the monitoring to work:
67
67
  gitdocs start
68
68
  ```
69
69
 
70
- If the start command fails, you can run again with a debug flag:
70
+ If the start command fails, you can check the logs in `~/.gitdocs/log` or run again with the debug flag:
71
71
 
72
72
  ```
73
73
  gitdocs start -D
@@ -110,7 +110,7 @@ gitdocs clear
110
110
 
111
111
  ### Web Front-end
112
112
 
113
- Gitdocs come with a handy web front-end that is available.
113
+ Gitdocs come with a handy web front-end that is available.
114
114
 
115
115
  <a href="http://i.imgur.com/IMwqN.png">
116
116
  <img src="http://i.imgur.com/IMwqN.png" width="250" />
@@ -140,8 +140,8 @@ Proper conflict resolution is an important part of any good doc and file collabo
140
140
  In most cases, git does a good job of handling file merges for you. Still, what about cases where the conflict cannot be
141
141
  resolved automatically?
142
142
 
143
- Don't worry, gitdocs makes handling this simple. In the event of a conflict,
144
- **all the different versions of a document are stored** in the repo tagged with the **git sha** for each
143
+ Don't worry, gitdocs makes handling this simple. In the event of a conflict,
144
+ **all the different versions of a document are stored** in the repo tagged with the **git sha** for each
145
145
  committed version. The members of the repo can then compare all versions and resolve the conflict.
146
146
 
147
147
  ## Planned Features
@@ -158,7 +158,7 @@ Gitdocs is a young project but we have big plans for it including:
158
158
  Gitdocs is a fresh project that we spiked on in a few days time. Our primary goals are to keep the code as simple as possible,
159
159
  but provide the features that makes dropbox great. If you are interested in other Dropbox alternatives, be sure to checkout our notes below:
160
160
 
161
- * [SparkleShare](http://sparkleshare.org/) is an open source, self-hosted Dropbox alternative written using C# and the [Mono Project](http://www.mono-project.com/Main_Page).
161
+ * [SparkleShare](http://sparkleshare.org/) is an open source, self-hosted Dropbox alternative written using C# and the [Mono Project](http://www.mono-project.com/Main_Page).
162
162
  More mature but has a lot of dependencies, and lacks some of the features planned in Gitdocs.
163
163
  * [DVCS-Autosync](http://mayrhofer.eu.org/dvcs-autosync) is a project to create an open source replacement for Dropbox based on distributed version control systems.
164
164
  Very similar project but again we have features planned that are out of scope (local tunnel file sharing, complete web ui for browsing, uploading and editing).
data/gitdocs.gemspec CHANGED
@@ -32,6 +32,8 @@ Gem::Specification.new do |s|
32
32
  s.add_dependency 'activerecord', "~> 3.1.0"
33
33
  s.add_dependency 'grit', "~> 2.4.1"
34
34
  s.add_dependency 'shell_tools', "~> 0.1.0"
35
+ s.add_dependency 'mimetype-fu', "~> 0.1.2"
36
+ s.add_dependency 'eventmachine', '>= 1.0.0.beta.3'
35
37
 
36
38
  s.add_development_dependency 'minitest', "~> 2.6.1"
37
39
  s.add_development_dependency 'rake'
data/lib/gitdocs.rb CHANGED
@@ -12,17 +12,23 @@ require 'gitdocs/server'
12
12
  require 'gitdocs/cli'
13
13
  require 'gitdocs/manager'
14
14
  require 'gitdocs/docfile'
15
+ require 'gitdocs/rendering'
15
16
 
16
17
  module Gitdocs
17
18
 
18
19
  DEBUG = ENV['DEBUG']
19
20
 
20
- def self.run(config_root = nil, debug = DEBUG, &blk)
21
+ def self.start(config_root = nil, debug = DEBUG, &blk)
22
+ @manager.stop if @manager
21
23
  @manager = Manager.new(config_root, debug, &blk)
22
- @manager.run
24
+ @manager.start
23
25
  end
24
26
 
25
27
  def self.restart
26
28
  @manager.restart
27
29
  end
30
+
31
+ def self.stop
32
+ @manager.stop
33
+ end
28
34
  end
data/lib/gitdocs/cli.rb CHANGED
@@ -10,11 +10,11 @@ module Gitdocs
10
10
  method_option :debug, :type => :boolean, :aliases => "-D"
11
11
  def start
12
12
  if self.stopped? && !options[:debug]
13
- self.runner.execute { Gitdocs.run }
13
+ self.runner.execute { Gitdocs.start }
14
14
  self.running? ? say("Started gitdocs", :green) : say("Failed to start gitdocs", :red)
15
15
  elsif self.stopped? && options[:debug]
16
16
  say "Starting in debug mode", :yellow
17
- Gitdocs.run(nil, true)
17
+ Gitdocs.start(nil, true)
18
18
  else # already running
19
19
  say "Gitdocs is already running, please use restart", :red
20
20
  end
@@ -14,6 +14,10 @@ module Gitdocs
14
14
  File.expand_path(@parent, root) == expanded_root ||
15
15
  File.expand_path(@path, root).include?(expanded_root)
16
16
  end
17
+
18
+ def file?
19
+ true
20
+ end
17
21
  end
18
22
 
19
23
  class Docdir < Docfile
@@ -27,11 +31,15 @@ module Gitdocs
27
31
  end
28
32
 
29
33
  def items
30
- subdirs + files
34
+ (subdirs + files).sort { |a,b| a.name.downcase <=> b.name.downcase }
31
35
  end
32
36
 
33
37
  def parent=(dir)
34
38
  dir.subdirs.push(self) if dir
35
39
  end
40
+
41
+ def file?
42
+ false
43
+ end
36
44
  end
37
45
  end
@@ -6,6 +6,7 @@ module Gitdocs
6
6
 
7
7
  def initialize(config_root, debug)
8
8
  @config = Configuration.new(config_root)
9
+ @logger = Logger.new(File.expand_path('log', @config.config_root))
9
10
  @debug = debug
10
11
  yield @config if block_given?
11
12
  end
@@ -22,41 +23,56 @@ module Gitdocs
22
23
  results
23
24
  end
24
25
 
25
- def run
26
- run = true
27
- trap('USR1') { run = true; EM.stop }
28
- while run
29
- run = false
30
- puts "Gitdocs v#{VERSION}" if self.debug
31
- puts "Using configuration root: '#{self.config.config_root}'" if self.debug
32
- puts "Shares: #{config.shares.map(&:inspect).join(", ")}" if self.debug
33
- # Start the repo watchers
34
- runners = nil
35
- EM.run do
36
- @runners = config.shares.map { |share| Runner.new(share) }
37
- @runners.each(&:run)
38
- # Start the web front-end
39
- if self.config.global.start_web_frontend
40
- Server.new(self, *@runners).start
41
- i = 0
42
- web_started = false
43
- begin
44
- TCPSocket.open('127.0.0.1', 8888).close
45
- web_started = true
46
- rescue Errno::ECONNREFUSED
47
- sleep 0.2
48
- i += 1
49
- retry if i <= 20
50
- end
51
- system("open http://localhost:8888/") if self.config.global.load_browser_on_startup && web_started
26
+ def start
27
+ self.log "Starting Gitdocs v#{VERSION}..."
28
+ self.log "Using configuration root: '#{self.config.config_root}'"
29
+ self.log "Shares: #{config.shares.map(&:inspect).join(", ")}"
30
+ # Start the repo watchers
31
+ runners = nil
32
+ EM.run do
33
+ self.log "Starting EM loop..."
34
+ @runners = config.shares.map { |share| Runner.new(share) }
35
+ @runners.each(&:run)
36
+ # Start the web front-end
37
+ if self.config.global.start_web_frontend
38
+ Server.new(self, *@runners).start
39
+ i = 0
40
+ web_started = false
41
+ begin
42
+ TCPSocket.open('127.0.0.1', 8888).close
43
+ web_started = true
44
+ rescue Errno::ECONNREFUSED
45
+ self.log "Retrying server loop..."
46
+ sleep 0.2
47
+ i += 1
48
+ retry if i <= 20
52
49
  end
50
+ self.log "Web server running: #{web_started}"
51
+ system("open http://localhost:8888/") if self.config.global.load_browser_on_startup && web_started
53
52
  end
54
- sleep(10) if @runners && @runners.empty?
55
53
  end
54
+ rescue Exception => e # Report all errors in log
55
+ self.log(e.class.inspect + " - " + e.inspect + " - " + e.message.inspect, :error)
56
+ self.log(e.backtrace.join("\n"), :error)
57
+ ensure
58
+ self.log("Gitdocs is terminating...goodbye\n\n")
56
59
  end
57
60
 
58
61
  def restart
59
- Process.kill("USR1", Process.pid)
62
+ stop
63
+ start
64
+ end
65
+
66
+ def stop
67
+ EM.stop
68
+ end
69
+
70
+ protected
71
+
72
+ # Logs and outputs to file or stdout based on debugging state
73
+ # log("message")
74
+ def log(msg, level=:info)
75
+ @debug ? puts(msg) : @logger.send(level, msg)
60
76
  end
61
77
  end
62
78
  end
@@ -28,9 +28,9 @@ form.upload p, form.add p {
28
28
  }
29
29
 
30
30
  /* Dir Listing */
31
- table td.author, td.modified {
32
- width: 30%;
33
- }
31
+ table td img { vertical-align: middle; }
32
+ table td.author, td.modified { width: 30%; }
33
+ table td.size { width: 15%; }
34
34
 
35
35
  /* Settings */
36
36
  div.share {
Binary file
Binary file
@@ -15,13 +15,24 @@ GitDocs = {
15
15
  },
16
16
  // fills in directory meta author and modified for every file
17
17
  fillDirMeta : function(){
18
- $('table.listing tbody tr').each(function(i, e) {
18
+ $('table#fileListing tbody tr').each(function(i, e) {
19
19
  var file = $(e).find('a').attr('href');
20
+ var fileListingBody = $('table#fileListing tbody')
20
21
  $.getJSON(file + "?mode=meta", function(data) {
21
- $(e).find('td.author').html(data.author);
22
+ $(e).addClass('loaded').find('td.author').html(data.author);
22
23
  $(e).find('td.modified').html(RelativeDate.time_ago_in_words(data.modified));
24
+ $(e).find('td.size').html(Utils.humanizeBytes(data.size)).data("val", data.size);
25
+ if ($(fileListingBody).find('tr').length == $(fileListingBody).find('tr.loaded').length) {
26
+ GitDocs.pageLoaded(); // Fire on completion
27
+ }
23
28
  });
24
29
  });
30
+ },
31
+ // Fire when the page is finished loading
32
+ pageLoaded : function() {
33
+ // Enable table sorter
34
+ var extractor = function(e) { return $(e).data('val') || $(e).text() }
35
+ $("table#fileListing").tablesorter({ textExtraction : extractor, sortList: [[0,0]] });
25
36
  }
26
37
  };
27
38
 
@@ -0,0 +1,4 @@
1
+
2
+ (function($){$.extend({tablesorter:new
3
+ function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
4
+ var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
@@ -13,6 +13,31 @@ Utils = {
13
13
  values.push(hash[i]);
14
14
  }
15
15
  return values;
16
+ },
17
+ // humanizeBytes(1234)
18
+ humanizeBytes : function(filesize) {
19
+ if (filesize == null || filesize <= 0 || filesize == "") { return "&mdash;" }
20
+ if (filesize >= 1073741824) {
21
+ filesize = Utils.number_format(filesize / 1073741824, 2, '.', '') + ' Gb';
22
+ } else {
23
+ if (filesize >= 1048576) {
24
+ filesize = Utils.number_format(filesize / 1048576, 2, '.', '') + ' Mb';
25
+ } else {
26
+ if (filesize >= 1024) {
27
+ filesize = Utils.number_format(filesize / 1024, 0) + ' Kb';
28
+ } else {
29
+ filesize = Utils.number_format(filesize, 0) + ' bytes';
30
+ };
31
+ };
32
+ };
33
+ return filesize;
34
+ },
35
+ number_format : function( number, decimals, dec_point, thousands_sep ) {
36
+ var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
37
+ var d = dec_point == undefined ? "," : dec_point;
38
+ var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : "";
39
+ var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
40
+ return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
16
41
  }
17
42
  };
18
43
 
@@ -0,0 +1,8 @@
1
+ # This shouldn't exist but I can't find any other way to prevent redcarpet from complaining
2
+
3
+ require 'redcarpet'
4
+ class RedcarpetCompat
5
+ def to_html(*_dummy)
6
+ @markdown.render(@text.force_encoding('utf-8'))
7
+ end
8
+ end
@@ -155,9 +155,11 @@ module Gitdocs
155
155
 
156
156
  def file_meta(file)
157
157
  file = file.gsub(%r{^/}, '')
158
+ full_path = File.expand_path(file, @root)
158
159
  author, modified = sh_string("git log --format='%aN|%ai' -n1 #{ShellTools.escape(file)}").split("|")
159
160
  modified = Time.parse(modified.sub(' ', 'T')).utc.iso8601
160
- { :author => author, :modified => modified }
161
+ size = (File.symlink?(full_path) || File.directory?(full_path)) ? -1 : File.size(full_path)
162
+ { :author => author, :size => size, :modified => modified }
161
163
  end
162
164
 
163
165
  def valid?
@@ -3,6 +3,7 @@ require 'renee'
3
3
  require 'coderay'
4
4
  require 'uri'
5
5
  require 'haml'
6
+ require 'mimetype_fu'
6
7
 
7
8
  module Gitdocs
8
9
  class Server
@@ -14,6 +15,7 @@ module Gitdocs
14
15
  def start(port = 8888)
15
16
  gds = @gitdocs
16
17
  manager = @manager
18
+ Thin::Logging.debug = @manager.debug
17
19
  Thin::Server.start('127.0.0.1', port) do
18
20
  use Rack::Static, :urls => ['/css', '/js', '/img', '/doc'], :root => File.expand_path("../public", __FILE__)
19
21
  run Renee {
@@ -51,9 +53,10 @@ module Gitdocs
51
53
  parent = '' if parent == '/'
52
54
  parent = nil if parent == '.'
53
55
  locals = {:idx => idx, :parent => parent, :root => gd.root, :file_path => expanded_path, :nav_state => nil }
54
- mode, mime = request.params['mode'], `file -I #{ShellTools.escape(expanded_path)}`.strip
56
+ mime = File.mime_type?(File.open(expanded_path)) if File.file?(expanded_path)
57
+ mode = request.params['mode']
55
58
  if mode == 'meta' # Meta
56
- halt 200, { 'Content-Type' => 'application/json' }, gd.file_meta(file_path).to_json
59
+ halt 200, { 'Content-Type' => 'application/json' }, [gd.file_meta(file_path).to_json]
57
60
  elsif mode == 'save' # Saving
58
61
  File.open(expanded_path, 'w') { |f| f.print request.params['data'] }
59
62
  redirect! "/" + idx.to_s + file_path
@@ -75,7 +78,7 @@ module Gitdocs
75
78
  render! "edit", :layout => 'app', :locals => locals.merge(:contents => contents)
76
79
  elsif mode != 'raw' # render file
77
80
  begin # attempting to render file
78
- contents = '<div class="tilt">' + Tilt.new(expanded_path).render + '</div>'
81
+ contents = '<div class="tilt">' + render(expanded_path) + '</div>'
79
82
  rescue RuntimeError => e # not tilt supported
80
83
  contents = if mime.match(%r{text/})
81
84
  '<pre class="CodeRay">' + CodeRay.scan_file(expanded_path).encode(:html) + '</pre>'
@@ -1,3 +1,3 @@
1
1
  module Gitdocs
2
- VERSION = "0.4.3"
2
+ VERSION = "0.4.5"
3
3
  end
@@ -8,6 +8,7 @@
8
8
  %link{ :href => "/css/coderay.css", :rel => "stylesheet" }
9
9
  %script{ :src => "/js/util.js", :type => "text/javascript", :charset => "utf-8" }
10
10
  %script{ :src => "/js/jquery.js", :type => "text/javascript", :charset => "utf-8" }
11
+ %script{ :src => "/js/jquery.tablesorter.js", :type => "text/javascript", :charset => "utf-8" }
11
12
  %script{ :src => "/js/app.js", :type => "text/javascript", :charset => "utf-8" }
12
13
  %body
13
14
  #nav.topbar
@@ -2,21 +2,24 @@
2
2
 
3
3
  = partial("header", :locals => { :parent => parent, :file => false, :idx => idx })
4
4
 
5
- %table.condensed-table.zebra-striped.listing
5
+ %table#fileListing.condensed-table.zebra-striped
6
6
  %thead
7
7
  %tr
8
8
  %th File
9
9
  %th Author
10
10
  %th Last Modified
11
+ %th Size
11
12
 
12
13
  %tbody
13
14
  - contents.items.each_with_index do |f, i|
14
15
  %tr
15
16
  %td
17
+ %img{ :src => "/img/#{f.file? ? 'file' : 'folder'}.png", :width => 16, :height => 16 }
16
18
  %a{ :href => "/#{idx}#{request.path_info}/#{f.name}" }
17
19
  = f.name
18
20
  %td.author
19
21
  %td.modified
22
+ %td.size
20
23
 
21
24
  .row
22
25
  .span6
data/test/test_helper.rb CHANGED
@@ -31,7 +31,7 @@ class MiniTest::Spec
31
31
  end
32
32
  begin
33
33
  puts "RUNNING!"
34
- Gitdocs.run(conf_path) do |conf|
34
+ Gitdocs.start(conf_path) do |conf|
35
35
  conf.global.update_attributes(:load_browser_on_startup => false, :start_web_frontend => false)
36
36
  conf.add_path(path, :polling_interval => 0.1, :notification => false)
37
37
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: gitdocs
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.3
5
+ version: 0.4.5
6
6
  platform: ruby
7
7
  authors:
8
8
  - Josh Hull
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2011-12-14 00:00:00 Z
14
+ date: 2011-12-15 00:00:00 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: joshbuddy-guard
@@ -168,49 +168,71 @@ dependencies:
168
168
  type: :runtime
169
169
  version_requirements: *id014
170
170
  - !ruby/object:Gem::Dependency
171
- name: minitest
171
+ name: mimetype-fu
172
172
  prerelease: false
173
173
  requirement: &id015 !ruby/object:Gem::Requirement
174
+ none: false
175
+ requirements:
176
+ - - ~>
177
+ - !ruby/object:Gem::Version
178
+ version: 0.1.2
179
+ type: :runtime
180
+ version_requirements: *id015
181
+ - !ruby/object:Gem::Dependency
182
+ name: eventmachine
183
+ prerelease: false
184
+ requirement: &id016 !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: 1.0.0.beta.3
190
+ type: :runtime
191
+ version_requirements: *id016
192
+ - !ruby/object:Gem::Dependency
193
+ name: minitest
194
+ prerelease: false
195
+ requirement: &id017 !ruby/object:Gem::Requirement
174
196
  none: false
175
197
  requirements:
176
198
  - - ~>
177
199
  - !ruby/object:Gem::Version
178
200
  version: 2.6.1
179
201
  type: :development
180
- version_requirements: *id015
202
+ version_requirements: *id017
181
203
  - !ruby/object:Gem::Dependency
182
204
  name: rake
183
205
  prerelease: false
184
- requirement: &id016 !ruby/object:Gem::Requirement
206
+ requirement: &id018 !ruby/object:Gem::Requirement
185
207
  none: false
186
208
  requirements:
187
209
  - - ">="
188
210
  - !ruby/object:Gem::Version
189
211
  version: "0"
190
212
  type: :development
191
- version_requirements: *id016
213
+ version_requirements: *id018
192
214
  - !ruby/object:Gem::Dependency
193
215
  name: mocha
194
216
  prerelease: false
195
- requirement: &id017 !ruby/object:Gem::Requirement
217
+ requirement: &id019 !ruby/object:Gem::Requirement
196
218
  none: false
197
219
  requirements:
198
220
  - - ">="
199
221
  - !ruby/object:Gem::Version
200
222
  version: "0"
201
223
  type: :development
202
- version_requirements: *id017
224
+ version_requirements: *id019
203
225
  - !ruby/object:Gem::Dependency
204
226
  name: fakeweb
205
227
  prerelease: false
206
- requirement: &id018 !ruby/object:Gem::Requirement
228
+ requirement: &id020 !ruby/object:Gem::Requirement
207
229
  none: false
208
230
  requirements:
209
231
  - - ">="
210
232
  - !ruby/object:Gem::Version
211
233
  version: "0"
212
234
  type: :development
213
- version_requirements: *id018
235
+ version_requirements: *id020
214
236
  description: Open-source Dropbox using Ruby and Git.
215
237
  email:
216
238
  - joshbuddy@gmail.com
@@ -245,6 +267,8 @@ files:
245
267
  - lib/gitdocs/public/css/coderay.css
246
268
  - lib/gitdocs/public/css/tilt.css
247
269
  - lib/gitdocs/public/img/error.png
270
+ - lib/gitdocs/public/img/file.png
271
+ - lib/gitdocs/public/img/folder.png
248
272
  - lib/gitdocs/public/img/git_logo.png
249
273
  - lib/gitdocs/public/img/info.png
250
274
  - lib/gitdocs/public/img/ok.png
@@ -281,8 +305,10 @@ files:
281
305
  - lib/gitdocs/public/js/app.js
282
306
  - lib/gitdocs/public/js/edit.js
283
307
  - lib/gitdocs/public/js/jquery.js
308
+ - lib/gitdocs/public/js/jquery.tablesorter.js
284
309
  - lib/gitdocs/public/js/search.js
285
310
  - lib/gitdocs/public/js/util.js
311
+ - lib/gitdocs/rendering.rb
286
312
  - lib/gitdocs/runner.rb
287
313
  - lib/gitdocs/server.rb
288
314
  - lib/gitdocs/version.rb