Browse files

Updated version.

  • Loading branch information...
2 parents 6b22201 + 7bd944b commit 17ca6f1cdffb3bf289a783be73ae5621d46b0d59 @jbfink committed Dec 19, 2011
View
32 CHANGELOG
@@ -0,0 +1,32 @@
+0.4.1 (12/13/2012)
+
+ * Adds a basic search to web front-end
+ * Beginning of the work towards cross-platform notifications
+
+0.4.2 (12/13/2012)
+
+ * Fixes search on blank results case
+
+0.4.3 (12/13/2012)
+
+ * Javascript fixes for front-end
+
+0.4.4 (12/14/2012)
+
+ * Lock to eventmachine 1.0
+ * Use mime-types over file for better platform support
+ * Remove signal handling and just leverage EM start/stop
+ * Adds table sorting and file+folder icons in web front-end
+
+0.4.5, 0.4.6 (12/14/2012)
+
+ * Enables logs in ~/.gitdocs/log for easier debugging
+ * Fix issue with default sort order on web interface
+ * Fixes major mime type detection issues
+ * Fixes rendering issues
+
+0.4.7 (12/15/2012)
+
+ * Re-throw exceptions from reactor loop
+ * Socket testing moved to defer block
+
View
10 README.md
@@ -67,7 +67,7 @@ You need to start gitdocs in order for the monitoring to work:
gitdocs start
```
-If the start command fails, you can run again with a debug flag:
+If the start command fails, you can check the logs in `~/.gitdocs/log` or run again with the debug flag:
```
gitdocs start -D
@@ -110,7 +110,7 @@ gitdocs clear
### Web Front-end
-Gitdocs come with a handy web front-end that is available.
+Gitdocs come with a handy web front-end that is available.
<a href="http://i.imgur.com/IMwqN.png">
<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
In most cases, git does a good job of handling file merges for you. Still, what about cases where the conflict cannot be
resolved automatically?
-Don't worry, gitdocs makes handling this simple. In the event of a conflict,
-**all the different versions of a document are stored** in the repo tagged with the **git sha** for each
+Don't worry, gitdocs makes handling this simple. In the event of a conflict,
+**all the different versions of a document are stored** in the repo tagged with the **git sha** for each
committed version. The members of the repo can then compare all versions and resolve the conflict.
## Planned Features
@@ -158,7 +158,7 @@ Gitdocs is a young project but we have big plans for it including:
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,
but provide the features that makes dropbox great. If you are interested in other Dropbox alternatives, be sure to checkout our notes below:
- * [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).
+ * [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).
More mature but has a lot of dependencies, and lacks some of the features planned in Gitdocs.
* [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.
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).
View
14 gitdocs.gemspec
@@ -19,19 +19,21 @@ Gem::Specification.new do |s|
s.require_paths = ["lib"]
s.add_dependency 'joshbuddy-guard', '~> 0.10.0'
- s.add_dependency 'thin'
+ s.add_dependency 'thin', '~> 1.3.1'
s.add_dependency 'renee', '~> 0.3.7'
- s.add_dependency 'redcarpet'
- s.add_dependency 'thor'
- s.add_dependency 'coderay'
+ s.add_dependency 'redcarpet', '~> 2.0.0'
+ s.add_dependency 'thor', '~> 0.14.6'
+ s.add_dependency 'coderay', '~> 1.0.4'
s.add_dependency 'dante', '~> 0.1.2'
s.add_dependency 'growl', '~> 1.0.3'
- s.add_dependency 'yajl-ruby'
- s.add_dependency 'haml'
+ s.add_dependency 'yajl-ruby', '~> 1.1.0'
+ s.add_dependency 'haml', '~> 3.1.4'
s.add_dependency 'sqlite3', "~> 1.3.4"
s.add_dependency 'activerecord', "~> 3.1.0"
s.add_dependency 'grit', "~> 2.4.1"
s.add_dependency 'shell_tools', "~> 0.1.0"
+ s.add_dependency 'mimetype-fu', "~> 0.1.2"
+ s.add_dependency 'eventmachine', '>= 1.0.0.beta.3'
s.add_development_dependency 'minitest', "~> 2.6.1"
s.add_development_dependency 'rake'
View
11 lib/gitdocs.rb
@@ -1,5 +1,4 @@
require 'thread'
-require 'growl'
require 'yajl'
require 'dante'
require 'socket'
@@ -13,17 +12,23 @@
require 'gitdocs/cli'
require 'gitdocs/manager'
require 'gitdocs/docfile'
+require 'gitdocs/rendering'
module Gitdocs
DEBUG = ENV['DEBUG']
- def self.run(config_root = nil, debug = DEBUG, &blk)
+ def self.start(config_root = nil, debug = DEBUG, &blk)
+ @manager.stop if @manager
@manager = Manager.new(config_root, debug, &blk)
- @manager.run
+ @manager.start
end
def self.restart
@manager.restart
end
+
+ def self.stop
+ @manager.stop
+ end
end
View
4 lib/gitdocs/cli.rb
@@ -10,11 +10,11 @@ def self.source_root; File.expand_path('../../', __FILE__); end
method_option :debug, :type => :boolean, :aliases => "-D"
def start
if self.stopped? && !options[:debug]
- self.runner.execute { Gitdocs.run }
+ self.runner.execute { Gitdocs.start }
self.running? ? say("Started gitdocs", :green) : say("Failed to start gitdocs", :red)
elsif self.stopped? && options[:debug]
say "Starting in debug mode", :yellow
- Gitdocs.run(nil, true)
+ Gitdocs.start(nil, true)
else # already running
say "Gitdocs is already running, please use restart", :red
end
View
22 lib/gitdocs/docfile.rb
@@ -1,6 +1,7 @@
module Gitdocs
class Docfile
- attr_reader :parent, :path, :name, :author, :modified
+ attr_accessor :parent
+ attr_reader :path, :name
def initialize(path)
@path = path
@@ -14,24 +15,9 @@ def within?(dir, root)
File.expand_path(@parent, root) == expanded_root ||
File.expand_path(@path, root).include?(expanded_root)
end
- end
-
- class Docdir < Docfile
- attr_accessor :subdirs
- attr_accessor :files
-
- def initialize(path)
- super
- @subdirs = []
- @files = []
- end
-
- def items
- subdirs + files
- end
- def parent=(dir)
- dir.subdirs.push(self) if dir
+ def dir?
+ File.directory?(@path)
end
end
end
View
97 lib/gitdocs/manager.rb
@@ -6,45 +6,90 @@ class Manager
def initialize(config_root, debug)
@config = Configuration.new(config_root)
+ @logger = Logger.new(File.expand_path('log', @config.config_root))
@debug = debug
yield @config if block_given?
end
- def run
- run = true
- trap('USR1') { run = true; EM.stop }
- while run
- run = false
- puts "Gitdocs v#{VERSION}" if self.debug
- puts "Using configuration root: '#{self.config.config_root}'" if self.debug
- puts "Shares: #{config.shares.map(&:inspect).join(", ")}" if self.debug
- # Start the repo watchers
- runners = nil
+ RepoDescriptor = Struct.new(:name, :index)
+
+ def search(term)
+ results = {}
+ @runners.each_with_index do |runner, index|
+ descriptor = RepoDescriptor.new(runner.root, index)
+ repo_results = runner.search(term)
+ results[descriptor] = repo_results unless repo_results.empty?
+ end
+ results
+ end
+
+ def start
+ self.log "Starting Gitdocs v#{VERSION}..."
+ self.log "Using configuration root: '#{self.config.config_root}'"
+ self.log "Shares: #{config.shares.map(&:inspect).join(", ")}"
+ # Start the repo watchers
+ runners = nil
+ retrying = false
+ begin
EM.run do
- runners = config.shares.map { |share| Runner.new(share) }
- runners.each(&:run)
+ self.log "Starting EM loop..."
+ @runners = config.shares.map { |share|
+ self.log "Starting #{share}"
+ Runner.new(share)
+ }
+ self.log "Running runners... #{@runners.size}"
+ @runners.each(&:run)
# Start the web front-end
if self.config.global.start_web_frontend
- Server.new(self, *runners).start
- i = 0
- web_started = false
- begin
- TCPSocket.open('127.0.0.1', 8888).close
- web_started = true
- rescue Errno::ECONNREFUSED
- sleep 0.2
- i += 1
- retry if i <= 20
- end
- system("open http://localhost:8888/") if self.config.global.load_browser_on_startup && web_started
+ Server.new(self, *@runners).start
+ EM.defer( proc {
+ i = 0
+ web_started = false
+ begin
+ TCPSocket.open('127.0.0.1', 8888).close
+ web_started = true
+ rescue Errno::ECONNREFUSED
+ self.log "Retrying server loop..."
+ sleep 0.2
+ i += 1
+ retry if i <= 20
+ end
+ system("open http://localhost:8888/") if !retrying && self.config.global.load_browser_on_startup && web_started
+ }, proc {
+ self.log "Web server running!"
+ })
end
end
- sleep(10) if runners && runners.empty?
+ rescue Restart
+ retrying = true
+ retry
end
+ rescue Exception => e # Report all errors in log
+ self.log(e.class.inspect + " - " + e.inspect + " - " + e.message.inspect, :error)
+ self.log(e.backtrace.join("\n"), :error)
+ raise
+ ensure
+ self.log("Gitdocs is terminating...goodbye\n\n")
end
def restart
- Process.kill("USR1", Process.pid)
+ Thread.new do
+ Thread.main.raise Restart, "restarting ... "
+ sleep 0.1 while EM.reactor_running?
+ start
+ end
+ end
+
+ def stop
+ EM.stop
+ end
+
+ protected
+
+ # Logs and outputs to file or stdout based on debugging state
+ # log("message")
+ def log(msg, level=:info)
+ @debug ? puts(msg) : @logger.send(level, msg)
end
end
end
View
8 lib/gitdocs/public/css/app.css
@@ -2,6 +2,8 @@ body {
padding-top: 50px;
}
+.contents a { color: inherit; }
+
.inline-file {
width:100%;
min-height: 600px;
@@ -26,9 +28,9 @@ form.upload p, form.add p {
}
/* Dir Listing */
-table td.author, td.modified {
- width: 30%;
-}
+table td img { vertical-align: middle; }
+table td.author, td.modified { width: 30%; }
+table td.size { width: 15%; }
/* Settings */
div.share {
View
BIN lib/gitdocs/public/img/error.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN lib/gitdocs/public/img/file.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN lib/gitdocs/public/img/folder.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN lib/gitdocs/public/img/info.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN lib/gitdocs/public/img/ok.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN lib/gitdocs/public/img/warning.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
64 lib/gitdocs/public/js/app.js
@@ -1,10 +1,11 @@
GitDocs = {
// Links all breadcrumb options in the explored path
linkBreadcrumbs : function() {
- var fullPath = $('span.path').text();
- var paths = fullPath.split("/");
- var docIdx = window.location.pathname.match(/(\d+)\//);
+ var fullPath = $('span.path').text().replace(/\/+/g, '/').replace(/\/$/, '');
+ if (fullPath.length == 0) { return; }
+ var docIdx = window.location.pathname.match(/\/(\d+)/);
if (!docIdx) return false;
+ var paths = fullPath.split("/");
$(paths).each(function(idx, subpath) {
var relPath = paths.slice(0, idx+1).join("/");
var link = "<a href='/" + docIdx[1] + relPath + "'>" + subpath + "/</a>";
@@ -14,25 +15,72 @@ GitDocs = {
},
// fills in directory meta author and modified for every file
fillDirMeta : function(){
- $('table.listing tbody tr').each(function(i, e) {
+ $('table#fileListing tbody tr').each(function(i, e) {
var file = $(e).find('a').attr('href');
+ var fileListingBody = $('table#fileListing tbody')
$.getJSON(file + "?mode=meta", function(data) {
- $(e).find('td.author').html(data.author);
+ $(e).addClass('loaded').find('td.author').html(data.author);
$(e).find('td.modified').html(RelativeDate.time_ago_in_words(data.modified));
+ $(e).find('td.size').html(Utils.humanizeBytes(data.size)).data("val", data.size);
+ if ($(fileListingBody).find('tr').length == $(fileListingBody).find('tr.loaded').length) {
+ GitDocs.pageLoaded(); // Fire on completion
+ }
});
});
+ },
+ // Fire when the page is finished loading
+ pageLoaded : function() {
+ // Enable table sorter
+ var extractor = function(e) { return $(e).data('val') || $(e).text() }
+ $("table#fileListing").tablesorter({ textExtraction : extractor, sortList: [[0,0]] });
+ },
+ // To make the settings form ajax-y
+ observeSettingsForm : function() {
+ $('#settings').submit(function(e) {
+ e.preventDefault();
+ $.ajax({
+ type: 'POST',
+ url: this.action,
+ data: $(this).serialize(),
+ success: function() {
+ var el = $('.content').prepend('<div class="alert-message success"><a class="close" href="#">×</a>' +
+ '<p><strong>Settings saved!</strong> Gitdocs has been restarted with your new settings.</p>' +
+ '</div>');
+ $('div.alert-message').alert();
+ }
+ });
+ return false;
+ });
}
};
$(document).ready(function() {
GitDocs.linkBreadcrumbs();
GitDocs.fillDirMeta();
+ StringFormatter.autoLink();
+ GitDocs.observeSettingsForm();
});
// Redirect to edit page for new file when new file form is submitted
$('form.add').live('submit', function(e){
- var docIdx = window.location.pathname.match(/(\d+)\//);
+ var docIdx = window.location.pathname.match(/\/(\d+)/);
+ if (!docIdx) return false;
var fullPath = $('span.path').text();
- window.location = "/" + docIdx[1] + fullPath + "/" + $(this).find('input.edit').val();
- e.preventDefault();
+ var newPath = "/" + docIdx[1] + (fullPath == "/" ? "/" : fullPath + "/") + $(this).find('input.edit').val();
+ window.location = newPath;
+ e.preventDefault(); return false;
});
+
+// Link method redirection
+$('a[data-method]').live('click', function(e) {
+ e.preventDefault();
+ var link = $(this);
+ var href = link.attr('href'),
+ method = link.data('method'),
+ target = link.attr('target'),
+ form = $('<form method="post" action="' + href + '"></form>'),
+ metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
+ if (target) { form.attr('target', target); }
+ form.hide().append(metadata_input).appendTo('body');
+ form.submit();
+});
View
113 lib/gitdocs/public/js/bootstrap-alerts.js
@@ -0,0 +1,113 @@
+/* ==========================================================
+ * bootstrap-alerts.js v1.4.0
+ * http://twitter.github.com/bootstrap/javascript.html#alerts
+ * ==========================================================
+ * Copyright 2011 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function( $ ){
+
+ "use strict"
+
+ /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
+ * ======================================================= */
+
+ var transitionEnd
+
+ $(document).ready(function () {
+
+ $.support.transition = (function () {
+ var thisBody = document.body || document.documentElement
+ , thisStyle = thisBody.style
+ , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
+ return support
+ })()
+
+ // set CSS transition event type
+ if ( $.support.transition ) {
+ transitionEnd = "TransitionEnd"
+ if ( $.browser.webkit ) {
+ transitionEnd = "webkitTransitionEnd"
+ } else if ( $.browser.mozilla ) {
+ transitionEnd = "transitionend"
+ } else if ( $.browser.opera ) {
+ transitionEnd = "oTransitionEnd"
+ }
+ }
+
+ })
+
+ /* ALERT CLASS DEFINITION
+ * ====================== */
+
+ var Alert = function ( content, options ) {
+ this.settings = $.extend({}, $.fn.alert.defaults, options)
+ this.$element = $(content)
+ .delegate(this.settings.selector, 'click', this.close)
+ }
+
+ Alert.prototype = {
+
+ close: function (e) {
+ var $element = $(this).parent('.alert-message')
+
+ e && e.preventDefault()
+ $element.removeClass('in')
+
+ function removeElement () {
+ $element.remove()
+ }
+
+ $.support.transition && $element.hasClass('fade') ?
+ $element.bind(transitionEnd, removeElement) :
+ removeElement()
+ }
+
+ }
+
+
+ /* ALERT PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.alert = function ( options ) {
+
+ if ( options === true ) {
+ return this.data('alert')
+ }
+
+ return this.each(function () {
+ var $this = $(this)
+
+ if ( typeof options == 'string' ) {
+ return $this.data('alert')[options]()
+ }
+
+ $(this).data('alert', new Alert( this, options ))
+
+ })
+ }
+
+ $.fn.alert.defaults = {
+ selector: '.close'
+ }
+
+ $(document).ready(function () {
+ new Alert($('body'), {
+ selector: '.alert-message[data-alert] .close'
+ })
+ })
+
+}( window.jQuery || window.ender );
View
4 lib/gitdocs/public/js/jquery.tablesorter.js
@@ -0,0 +1,4 @@
+
+(function($){$.extend({tablesorter:new
+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
+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);
View
13 lib/gitdocs/public/js/search.js
@@ -0,0 +1,13 @@
+GitDocs.search = {
+ highlight : function(query) {
+ $('.results dl dd').each(function(idx, el) {
+ var result = $(el).text().replace(new RegExp(query, 'ig'), function($0) { return"<strong>" + $0 + "</strong>"; });
+ $(el).html(result);
+ });
+ }
+};
+
+$(document).ready(function() {
+ var query = $('.results').data("query");
+ GitDocs.search.highlight(query);
+});
View
42 lib/gitdocs/public/js/util.js
@@ -13,6 +13,48 @@ Utils = {
values.push(hash[i]);
}
return values;
+ },
+ // humanizeBytes(1234)
+ humanizeBytes : function(filesize) {
+ if (filesize == null || filesize <= 0 || filesize == "") { return "&mdash;" }
+ if (filesize >= 1073741824) {
+ filesize = Utils.number_format(filesize / 1073741824, 2, '.', '') + ' Gb';
+ } else {
+ if (filesize >= 1048576) {
+ filesize = Utils.number_format(filesize / 1048576, 2, '.', '') + ' Mb';
+ } else {
+ if (filesize >= 1024) {
+ filesize = Utils.number_format(filesize / 1024, 0) + ' Kb';
+ } else {
+ filesize = Utils.number_format(filesize, 0) + ' bytes';
+ };
+ };
+ };
+ return filesize;
+ },
+ number_format : function( number, decimals, dec_point, thousands_sep ) {
+ var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
+ var d = dec_point == undefined ? "," : dec_point;
+ var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : "";
+ var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
+ 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) : "");
+ }
+};
+
+// Strings
+StringFormatter = {
+ // autolink text within a plain text file
+ // apply to the wrapper around any text (.autolink)
+ autoLink : function() {
+ $('.autolink:not(.linked)').each(function(index, item) {
+ var result = $(item).html().toString() + ' ';
+ $(result.match(/(https?.*?)[^<\s]*/gm)).each(function(index, linkString) {
+ var link = "<a href='" + linkString + "' target='_blank'>" + linkString + "</a>";
+ result = result.replace(linkString, link);
+ $(item).addClass('linked');
+ });
+ $(item).html(result.slice(0, -1));
+ });
}
};
View
22 lib/gitdocs/rendering.rb
@@ -0,0 +1,22 @@
+# This shouldn't exist but I can't find any other way to prevent redcarpet from complaining
+# WARN: tilt autoloading 'redcarpet' in a non thread-safe way; explicit require 'redcarpet' suggested.
+# !! Unexpected error while processing request: Input must be UTF-8 or US-ASCII, ASCII-8BIT given
+# Input must be UTF-8 or US-ASCII, ASCII-8BIT given
+# gems/redcarpet-2.0.1/lib/redcarpet.rb:70:in `render'
+# gems/redcarpet-2.0.1/lib/redcarpet.rb:70:in `to_html'
+# gems/tilt-1.3.3/lib/tilt/markdown.rb:38:in `evaluate'
+# gems/tilt-1.3.3/lib/tilt/markdown.rb:61:in `evaluate'
+# gems/tilt-1.3.3/lib/tilt/template.rb:76:in `render'
+
+require 'redcarpet'
+
+# Compatibility class;
+# Creates a instance of Redcarpet with the RedCloth
+# API. This instance has no extensions enabled whatsoever,
+# and no accessors to change this. 100% pure, standard
+# Markdown.
+class RedcarpetCompat
+ def to_html(*_dummy)
+ @markdown.render(@text.encode('utf-8'))
+ end
+end
View
59 lib/gitdocs/runner.rb
@@ -6,18 +6,28 @@ class Runner
def initialize(share)
@share = share
- @root = share.path
+ @root = share.path.sub(%r{/+$},'') if share.path
@polling_interval = share.polling_interval
@icon = File.expand_path("../../img/icon.png", __FILE__)
end
+ SearchResult = Struct.new(:file, :context)
+ def search(term)
+ results = []
+ if result_test = sh_string("git grep -i #{ShellTools.escape(term)}")
+ result_test.scan(/(.*?):([^\n]*)/) { |(file, context)| results << SearchResult.new(file, context) }
+ end
+ results
+ end
+
def run
- return false unless self.valid?
- out, status = sh_with_code "which growlnotify"
- @use_growl = @share.notification && status.success?
- @current_remote = @share.remote_name
- @current_branch = @share.branch_name
- @current_revision = sh_string("git rev-parse HEAD")
+ return false unless self.valid? && !self.root.empty?
+ @show_notifications = @share.notification
+ @current_remote = @share.remote_name
+ @current_branch = @share.branch_name
+ @current_revision = sh_string("git rev-parse HEAD")
+ Guard::Notifier.turn_on if @show_notifications
+
mutex = Mutex.new
info("Running gitdocs!", "Running gitdocs in `#{@root}'")
@@ -134,24 +144,21 @@ def get_latest_changes
IGNORED_FILES = ['.gitignore']
# dir_files("some/dir") => [<Docfile>, <Docfile>]
- def dir_files(dir)
- dir_path = File.expand_path(dir, @root)
- files = {}
- ls_files = sh_string("git ls-files").split("\n").map { |f| Docfile.new(f) }
- ls_files.select { |f| f.within?(dir, @root) }.each do |f|
- path = File.expand_path(f.parent, root)
- files[path] ||= Docdir.new(path)
- files[path].files << f unless IGNORED_FILES.include?(f.name)
- end
- files.keys.each { |f| files[f].parent = files[File.dirname(f)] }
- files[dir_path]
+ def dir_files(dir_path)
+ Dir[File.join(dir_path, "*")].to_a.map { |path| Docfile.new(path) }
end
def file_meta(file)
+ result = {}
file = file.gsub(%r{^/}, '')
- author, modified = sh_string("git log --format='%aN|%ai' -n1 #{ShellTools.escape(file)}").split("|")
+ full_path = File.expand_path(file, @root)
+ log_result = sh_string("git log --format='%aN|%ai' -n1 #{ShellTools.escape(file)}")
+ result = {} unless File.exist?(full_path) && log_result
+ author, modified = log_result.split("|")
modified = Time.parse(modified.sub(' ', 'T')).utc.iso8601
- { :author => author, :modified => modified }
+ size = (File.symlink?(full_path) || File.directory?(full_path)) ? -1 : File.size(full_path)
+ result = { :author => author, :size => size, :modified => modified }
+ result
end
def valid?
@@ -160,24 +167,24 @@ def valid?
end
def warn(title, msg)
- if @use_growl
- Growl.notify_warning(msg, :title => title)
+ if @show_notifications
+ Guard::Notifier.notify(msg, :title => title)
else
Kernel.warn("#{title}: #{msg}")
end
end
def info(title, msg)
- if @use_growl
- Growl.notify_ok(msg, :title => title, :icon => @icon)
+ if @show_notifications
+ Guard::Notifier.notify(msg, :title => title, :image => @icon)
else
puts("#{title}: #{msg}")
end
end
def error(title, msg)
- if @use_growl
- Growl.notify_error(msg, :title => title)
+ if @show_notifications
+ Guard::Notifier.notify(msg, :title => title, :image => :failure)
else
Kernel.warn("#{title}: #{msg}")
end
View
21 lib/gitdocs/server.rb
@@ -3,6 +3,7 @@
require 'coderay'
require 'uri'
require 'haml'
+require 'mimetype_fu'
module Gitdocs
class Server
@@ -14,6 +15,7 @@ def initialize(manager, *gitdocs)
def start(port = 8888)
gds = @gitdocs
manager = @manager
+ Thin::Logging.debug = @manager.debug
Thin::Server.start('127.0.0.1', port) do
use Rack::Static, :urls => ['/css', '/js', '/img', '/doc'], :root => File.expand_path("../public", __FILE__)
run Renee {
@@ -31,11 +33,19 @@ def start(port = 8888)
end
shares[Integer(idx)].update_attributes(share)
end
- manager.restart
+ EM.add_timer(0.1) { manager.restart }
redirect! '/settings'
end
end
+ path('search').get do
+ render! "search", :layout => 'app', :locals => {:conf => manager.config, :results => manager.search(request.GET['q']), :nav_state => nil}
+ end
+
+ path('shares').post do
+ Configuration::Share.create
+ end
+
var :int do |idx|
gd = gds[idx]
halt 404 if gd.nil?
@@ -47,9 +57,10 @@ def start(port = 8888)
parent = '' if parent == '/'
parent = nil if parent == '.'
locals = {:idx => idx, :parent => parent, :root => gd.root, :file_path => expanded_path, :nav_state => nil }
- mode, mime = request.params['mode'], `file -I #{ShellTools.escape(expanded_path)}`.strip
+ mime = File.mime_type?(File.open(expanded_path)) if File.file?(expanded_path)
+ mode = request.params['mode']
if mode == 'meta' # Meta
- halt 200, { 'Content-Type' => 'application/json' }, gd.file_meta(file_path).to_json
+ halt 200, { 'Content-Type' => 'application/json' }, [gd.file_meta(file_path).to_json]
elsif mode == 'save' # Saving
File.open(expanded_path, 'w') { |f| f.print request.params['data'] }
redirect! "/" + idx.to_s + file_path
@@ -61,7 +72,7 @@ def start(port = 8888)
elsif !File.exist?(expanded_path) # edit for non-existent file
render! "edit", :layout => 'app', :locals => locals.merge(:contents => "")
elsif File.directory?(expanded_path) # list directory
- contents = gd.dir_files(expanded_path)
+ contents = gd.dir_files(expanded_path)
render! "dir", :layout => 'app', :locals => locals.merge(:contents => contents)
elsif mode == 'delete' # delete file
FileUtils.rm(expanded_path)
@@ -71,7 +82,7 @@ def start(port = 8888)
render! "edit", :layout => 'app', :locals => locals.merge(:contents => contents)
elsif mode != 'raw' # render file
begin # attempting to render file
- contents = '<div class="tilt">' + Tilt.new(expanded_path).render + '</div>'
+ contents = '<div class="tilt">' + render(expanded_path) + '</div>'
rescue RuntimeError => e # not tilt supported
contents = if mime.match(%r{text/})
'<pre class="CodeRay">' + CodeRay.scan_file(expanded_path).encode(:html) + '</pre>'
View
2 lib/gitdocs/version.rb
@@ -1,3 +1,3 @@
module Gitdocs
- VERSION = "0.4.1-jbfink"
+ VERSION = "0.4.7-jbfink"
end
View
4 lib/gitdocs/views/app.haml
@@ -8,6 +8,8 @@
%link{ :href => "/css/coderay.css", :rel => "stylesheet" }
%script{ :src => "/js/util.js", :type => "text/javascript", :charset => "utf-8" }
%script{ :src => "/js/jquery.js", :type => "text/javascript", :charset => "utf-8" }
+ %script{ :src => "/js/jquery.tablesorter.js", :type => "text/javascript", :charset => "utf-8" }
+ %script{ :src => "/js/bootstrap-alerts.js", :type => "text/javascript", :charset => "utf-8" }
%script{ :src => "/js/app.js", :type => "text/javascript", :charset => "utf-8" }
%body
#nav.topbar
@@ -19,6 +21,8 @@
%a(href = "/") Home
%li{ :class => ("active" if nav_state == "settings") }
%a(href = "/settings") Settings
+ %form{:class => "pull-left", :action => "/search", :method => 'GET'}
+ %input{:type => "text", :placeholder => "Search", :name => 'q'}
#main.container
.content
View
35 lib/gitdocs/views/dir.haml
@@ -2,21 +2,28 @@
= partial("header", :locals => { :parent => parent, :file => false, :idx => idx })
-%table.condensed-table.zebra-striped.listing
- %thead
- %tr
- %th File
- %th Author
- %th Last Modified
-
- %tbody
- - contents.items.each_with_index do |f, i|
+- if contents && contents.any?
+ %table#fileListing.condensed-table.zebra-striped
+ %thead
%tr
- %td
- %a{ :href => "/#{idx}#{request.path_info}/#{f.name}" }
- = f.name
- %td.author
- %td.modified
+ %th File
+ %th Author
+ %th Last Modified
+ %th Size
+
+ %tbody
+ - contents.each_with_index do |f, i|
+ %tr
+ %td
+ %img{ :src => "/img/#{f.dir? ? 'folder' : 'file'}.png", :width => 16, :height => 16 }
+ %a{ :href => "/#{idx}#{request.path_info}/#{f.name}" }
+ = f.name
+ %td.author
+ %td.modified
+ %td.size
+
+- if contents.empty?
+ %p No files were found in this directory.
.row
.span6
View
16 lib/gitdocs/views/search.haml
@@ -0,0 +1,16 @@
+- @title = "Matches for #{request.params['q'].inspect}"
+%script{ :src => "/js/search.js", :type => "text/javascript", :charset => "utf-8" }
+
+.results{ "data-query" => request.params['q'] }
+ -if results.empty?
+ %h2 No results
+ -else
+ - results.each do |repo, search_results|
+ %h2= repo.name
+ %dl
+ - search_results.each do |res|
+ %dt
+ %a{:href => "/#{repo.index}/#{res.file}"}
+ = "/#{res.file}"
+ %dd
+ = res.context
View
7 lib/gitdocs/views/settings.haml
@@ -1,6 +1,6 @@
- @title = "Settings"
-%form{:method => 'POST', :action => '/settings'}
+%form#settings{:method => 'POST', :action => '/settings'}
%h2 Gitdocs
#config.field.config
%input{:type =>'hidden', :value => '0', :name=>"config[load_browser_on_startup]"}
@@ -41,8 +41,9 @@
%input{:type =>'hidden', :value => '0', :name=>"share[#{idx}][notification]"}
%input{:type =>'checkbox', :value => '1', :name=>"share[#{idx}][notification]", :checked => share.notification ? 'checked' : nil}
%span Notifications?
- .delete
+ .delete{ :style => "display: none;" }
%input{:type =>'button', :value => "Delete", :class => "btn danger"}
%input{:type =>'hidden', :name=>"share[#{idx}][delete]"}
- %input{:value => 'Save', :type => 'submit', :class => "btn primary" }
+ %input{:value => 'Save', :type => 'submit', :class => "btn primary" }
+ %a{ :class => "btn secondary new-share", :href => "/shares", :"data-method" => "post" } Add Share
View
2 test/test_helper.rb
@@ -31,7 +31,7 @@ def with_clones(count = 3)
end
begin
puts "RUNNING!"
- Gitdocs.run(conf_path) do |conf|
+ Gitdocs.start(conf_path) do |conf|
conf.global.update_attributes(:load_browser_on_startup => false, :start_web_frontend => false)
conf.add_path(path, :polling_interval => 0.1, :notification => false)
end

0 comments on commit 17ca6f1

Please sign in to comment.