httpit 0.4 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +7 -0
- data/Gemfile.lock +30 -0
- data/README.md +1 -1
- data/bin/httpit +111 -90
- data/example/index.haml +9 -6
- data/example/testing_design/_header.haml +3 -0
- data/example/testing_design/index.haml +50 -0
- data/example/testing_design/layout.sass +56 -0
- data/httpit.gemspec +6 -6
- data/views/listing.haml +55 -86
- data/views/utils.js +13 -0
- metadata +47 -26
- data/lib/search.rb +0 -34
- data/lib/search/dystopia.rb +0 -65
- data/lib/search/find.rb +0 -16
- data/views/search.haml +0 -12
- data/views/search.js +0 -59
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
httpit (0.4.2)
|
|
5
|
+
RedCloth (~> 4.2.9)
|
|
6
|
+
haml (~> 4.0.2)
|
|
7
|
+
sass (~> 3.2.8)
|
|
8
|
+
sinatra (~> 1.4.2)
|
|
9
|
+
|
|
10
|
+
GEM
|
|
11
|
+
remote: https://rubygems.org/
|
|
12
|
+
specs:
|
|
13
|
+
RedCloth (4.2.9)
|
|
14
|
+
haml (4.0.2)
|
|
15
|
+
tilt
|
|
16
|
+
rack (1.5.2)
|
|
17
|
+
rack-protection (1.5.0)
|
|
18
|
+
rack
|
|
19
|
+
sass (3.2.8)
|
|
20
|
+
sinatra (1.4.2)
|
|
21
|
+
rack (~> 1.5, >= 1.5.2)
|
|
22
|
+
rack-protection (~> 1.4)
|
|
23
|
+
tilt (~> 1.3, >= 1.3.4)
|
|
24
|
+
tilt (1.3.7)
|
|
25
|
+
|
|
26
|
+
PLATFORMS
|
|
27
|
+
ruby
|
|
28
|
+
|
|
29
|
+
DEPENDENCIES
|
|
30
|
+
httpit!
|
data/README.md
CHANGED
data/bin/httpit
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
require "rubygems"
|
|
3
|
+
require "bundler/setup"
|
|
2
4
|
|
|
3
|
-
require '
|
|
4
|
-
require 'sinatra'
|
|
5
|
+
require 'sinatra/base'
|
|
5
6
|
require 'haml'
|
|
7
|
+
require 'sass'
|
|
6
8
|
require 'redcloth'
|
|
7
9
|
require "pathname"
|
|
10
|
+
require "fileutils"
|
|
8
11
|
|
|
9
12
|
GEM_ROOT = Pathname.new(__FILE__).dirname.join("..").expand_path
|
|
10
|
-
|
|
11
|
-
require GEM_ROOT + "lib/search"
|
|
12
|
-
|
|
13
13
|
ROOT = Pathname.pwd
|
|
14
14
|
|
|
15
|
+
TMP_DIR = File.expand_path("#{Dir.tmpdir}/#{Time.now.to_i}#{rand(1000)}/")
|
|
16
|
+
FileUtils.mkdir_p(TMP_DIR)
|
|
17
|
+
|
|
15
18
|
# this hack allow
|
|
16
19
|
# /some/folder/with/content + /subfolder => /some/folder/with/content/subfolder
|
|
17
|
-
|
|
18
|
-
class <<ROOT
|
|
20
|
+
class << ROOT
|
|
19
21
|
def join(*args)
|
|
20
22
|
super *args.map{|a| a.is_a?(String) && a[0] == ?/ ? a[1 .. a.size] : a }
|
|
21
23
|
end
|
|
@@ -25,105 +27,124 @@ class <<ROOT
|
|
|
25
27
|
end
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
set :views, ROOT
|
|
29
|
-
set :public_folder, ROOT
|
|
30
|
-
set :app_file, $0
|
|
31
|
-
|
|
32
|
-
set :static, false
|
|
33
|
-
|
|
34
30
|
# only one parametr - port number
|
|
35
31
|
if ARGV.size > 0
|
|
36
32
|
set :port, ARGV.first.to_i
|
|
37
33
|
end
|
|
38
34
|
|
|
35
|
+
class HttpIt < Sinatra::Base
|
|
36
|
+
set :views, ROOT
|
|
37
|
+
set :public_folder, ROOT
|
|
38
|
+
set :app_file, $0
|
|
39
|
+
set :static, false
|
|
40
|
+
set :sass, :cache => true, :cache_location => File.join(TMP_DIR, 'sass-cache')
|
|
39
41
|
|
|
40
|
-
# build array of contents and theris type for specified folder
|
|
41
|
-
def get_content(folder = nil, sort = nil)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# build array if pairs: [filename, :type, is_textfile]
|
|
45
|
-
sorter = case sort
|
|
46
|
-
when 'name' then '| sort -f'
|
|
47
|
-
when 'ctime' then '-c'
|
|
48
|
-
when 'mtime' then '--sort=time'
|
|
49
|
-
when 'size' then '--sort=size'
|
|
50
|
-
else ''
|
|
51
|
-
end
|
|
42
|
+
# build array of contents and theris type for specified folder
|
|
43
|
+
def get_content(folder = nil, sort = nil)
|
|
44
|
+
abs_path = folder ? ROOT + folder : ROOT
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
# build array if pairs: [filename, :type, is_textfile]
|
|
47
|
+
sorter = case sort
|
|
48
|
+
when 'name' then '| sort -f'
|
|
49
|
+
when 'ctime' then '-c'
|
|
50
|
+
when 'mtime' then '--sort=time'
|
|
51
|
+
when 'size' then '--sort=size'
|
|
52
|
+
else ''
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
`cd "#{abs_path.to_s.gsub(?", '\"')}" && ls -A #{sorter}`.split("\n").map do |obj|
|
|
56
|
+
file_path = abs_path + obj
|
|
57
|
+
[obj,
|
|
58
|
+
if file_path.file?; :file
|
|
59
|
+
elsif file_path.directory?; :dir
|
|
60
|
+
elsif file_path.symlink?; :link
|
|
61
|
+
end,
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
!!`file "#{file_path.to_s.gsub(?", '\"')}"`.sub(file_path.to_s, '').index(/text/i)
|
|
64
|
+
]
|
|
65
|
+
end
|
|
63
66
|
end
|
|
64
|
-
end
|
|
65
67
|
|
|
66
|
-
def view(tpl)
|
|
67
|
-
|
|
68
|
-
end
|
|
68
|
+
def view(tpl)
|
|
69
|
+
Pathname.new(__FILE__).dirname.join("../views/#{tpl}.haml").expand_path.read
|
|
70
|
+
end
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
elsif File.file?('./index.haml')
|
|
77
|
-
haml view(:index)
|
|
78
|
-
else
|
|
79
|
-
@path = ''
|
|
80
|
-
@files = get_content(nil, params[:sort]).select {|f| f[0] != '.' && f[0] != '..' }
|
|
81
|
-
haml view(:listing)
|
|
72
|
+
def folder_view(tpl, format)
|
|
73
|
+
if ROOT.join("#{tpl}.#{format}").file?
|
|
74
|
+
tpl.to_sym
|
|
75
|
+
else
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
82
78
|
end
|
|
83
|
-
end
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
80
|
+
# shows index.html or folder contents if index.html does not exists
|
|
81
|
+
get '/' do
|
|
82
|
+
if ROOT.join('./index.html').file?
|
|
83
|
+
ROOT.join('./index.html').read
|
|
84
|
+
elsif folder_view(:index, :haml)
|
|
85
|
+
haml folder_view(:index, :haml)
|
|
86
|
+
else
|
|
87
|
+
@path = ''
|
|
88
|
+
@files = get_content(nil, params[:sort]).select {|f| f[0] != '.' && f[0] != '..' }
|
|
89
|
+
haml view(:listing)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
94
92
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
content_type :css
|
|
105
|
-
sass @path.chomp('.sass.css').to_sym
|
|
106
|
-
|
|
107
|
-
elsif !abs_path.file? && !abs_path.directory?
|
|
108
|
-
halt 404
|
|
93
|
+
get '/__img_preview' do
|
|
94
|
+
params[:file] = params[:file].gsub(' ', '\ ')
|
|
95
|
+
tmppath = "/tmp/httpit_preview_#{Time.now.to_i}.jpeg"
|
|
96
|
+
`convert #{ROOT + params[:file]} -resize 1024 -quality 100% #{tmppath}`
|
|
97
|
+
content_type "image/jpeg"
|
|
98
|
+
content = File.open(tmppath, 'rb') { |f| f.read }
|
|
99
|
+
File.delete(tmppath)
|
|
100
|
+
content
|
|
101
|
+
end
|
|
109
102
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
103
|
+
# shows folder contents
|
|
104
|
+
get %r{.+} do
|
|
105
|
+
return nil if request.path == '/favicon.ico'
|
|
106
|
+
@path = URI.unescape(request.path)
|
|
107
|
+
|
|
108
|
+
abs_path = ROOT + @path
|
|
109
|
+
@dirname = abs_path.file? ? File.dirname(@path) : @path.chomp('/')
|
|
110
|
+
|
|
111
|
+
if @path =~ /.+\.sass\.css/
|
|
112
|
+
return halt(404) unless File.file?(abs_path.to_s.chomp('.css'))
|
|
113
|
+
content_type :css
|
|
114
|
+
sass @path.chomp('.sass.css').to_sym
|
|
115
|
+
|
|
116
|
+
elsif !abs_path.file? && !abs_path.directory?
|
|
117
|
+
halt 404
|
|
118
|
+
|
|
119
|
+
elsif @path =~ /.+\.md/
|
|
120
|
+
content_type :html
|
|
121
|
+
return RedCloth.new(abs_path.read).to_html
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
elsif @path =~ /.+\.haml/
|
|
124
|
+
haml @path.chomp('.haml').to_sym
|
|
125
|
+
|
|
126
|
+
elsif File.file?(abs_path)
|
|
127
|
+
send_file(abs_path)
|
|
128
|
+
|
|
129
|
+
elsif abs_path.join('./index.html').file?
|
|
130
|
+
content_type :html
|
|
131
|
+
abs_path.join('./index.html').read
|
|
132
|
+
|
|
133
|
+
elsif abs_path.join('./index.haml').file?
|
|
134
|
+
content_type :html
|
|
135
|
+
haml File.join(@path, 'index').to_sym
|
|
136
|
+
|
|
137
|
+
else
|
|
138
|
+
@files = get_content(@path, params[:sort])
|
|
139
|
+
haml view(:listing)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
error 404 do
|
|
144
|
+
@path = request.path
|
|
145
|
+
haml view(:not_found)
|
|
123
146
|
end
|
|
124
147
|
end
|
|
125
148
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
haml view(:not_found)
|
|
129
|
-
end
|
|
149
|
+
HttpIt.run!
|
|
150
|
+
at_exit { FileUtils.rm_rf(TMP_DIR) if File.exists?(TMP_DIR) }
|
data/example/index.haml
CHANGED
|
@@ -10,20 +10,23 @@
|
|
|
10
10
|
%body
|
|
11
11
|
#wrap
|
|
12
12
|
%h1 HttpIt is awesome
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
%pre.code.usage
|
|
15
15
|
:preserve
|
|
16
|
-
$
|
|
16
|
+
$ gem install httpit
|
|
17
17
|
$ cd /folder/for/server
|
|
18
18
|
$ httpit
|
|
19
19
|
# or you can set port
|
|
20
|
-
$ httpit
|
|
20
|
+
$ httpit 8889
|
|
21
21
|
|
|
22
22
|
%p
|
|
23
23
|
%a{ :href => "images/bird.jpg" } Static file
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
%p
|
|
26
26
|
%a{ :href => "readme.md" } MarkDown example
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
%p
|
|
29
|
+
%a{ :href => "images" } Gallery folder
|
|
30
|
+
|
|
28
31
|
%p
|
|
29
|
-
%a{ :href => "
|
|
32
|
+
%a{ :href => "testing_design" } Full-stack layout example
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
!!! 5
|
|
2
|
+
%html{:lang => "en"}
|
|
3
|
+
%head
|
|
4
|
+
%meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}/
|
|
5
|
+
%title Testing layout
|
|
6
|
+
%link{:rel => "stylesheet", :type => "text/css", :href => "#{@dirname}/layout.sass.css"}
|
|
7
|
+
|
|
8
|
+
%body
|
|
9
|
+
#wrapper
|
|
10
|
+
= haml :"#{@dirname}/_header"
|
|
11
|
+
.content
|
|
12
|
+
.top-line
|
|
13
|
+
This is example layout to show how gem "#{"<b>httpit</b>"}" in action
|
|
14
|
+
|
|
15
|
+
%aside>
|
|
16
|
+
%h4 For example:
|
|
17
|
+
%ul
|
|
18
|
+
%li 2/1 = 2.0
|
|
19
|
+
%li 3/2 = 1.5
|
|
20
|
+
%li 5/3 = 1.67
|
|
21
|
+
%li 8/5 = 1.6
|
|
22
|
+
%li 13/8 = 1.625
|
|
23
|
+
%li 21/13 = 1.615
|
|
24
|
+
%li 34/21 = 1.619
|
|
25
|
+
%li 55/34 = 1.6176
|
|
26
|
+
|
|
27
|
+
.main-column>
|
|
28
|
+
%h3 The Fibonacci Sequence
|
|
29
|
+
%p
|
|
30
|
+
The mathematics behind the golden ratio is heavily connected to the
|
|
31
|
+
= succeed "." do
|
|
32
|
+
%a{:href => "http://en.wikipedia.org/wiki/Fibonacci_sequence"} Fibonacci Sequence
|
|
33
|
+
If you’re unfamiliar with the fibonacci sequence,
|
|
34
|
+
it begins by definition with the numbers 0, 1 and then each successive number
|
|
35
|
+
in the sequence is the sum of the previous two numbers.
|
|
36
|
+
|
|
37
|
+
%p 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55…
|
|
38
|
+
|
|
39
|
+
%p
|
|
40
|
+
I’ll spare you the deep mathematics talk (we’ll just do a bit of division),
|
|
41
|
+
since what we’re mainly interested in is how the sequence relates to the
|
|
42
|
+
= succeed "." do
|
|
43
|
+
%a{:href => "http://freemasonry.bcy.ca/symbolism/golden_ratio/index.html"} golden section
|
|
44
|
+
|
|
45
|
+
%p
|
|
46
|
+
If you take any number in the sequence and divide it by the previous
|
|
47
|
+
number the result approximates Phi or the golden ratio.
|
|
48
|
+
%p
|
|
49
|
+
With early numbers in the sequence this may not appear to be true,
|
|
50
|
+
but as we continue along the sequence the division approaches 1.618 rather quickly.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
$line: 20px
|
|
2
|
+
$pageWidth: 800px
|
|
3
|
+
$asideRation: 5 / 21
|
|
4
|
+
body
|
|
5
|
+
:margin 0
|
|
6
|
+
:padding 0
|
|
7
|
+
:font
|
|
8
|
+
:size 14px
|
|
9
|
+
:family Arial, sans-serif
|
|
10
|
+
a
|
|
11
|
+
:color #2468a5
|
|
12
|
+
> #wrapper
|
|
13
|
+
:width $pageWidth
|
|
14
|
+
:margin $line auto
|
|
15
|
+
> header
|
|
16
|
+
:border-bottom 1px solid #ccc
|
|
17
|
+
:margin-bottom $line
|
|
18
|
+
h1
|
|
19
|
+
:color #36333e
|
|
20
|
+
:margin-bottom $line / -2
|
|
21
|
+
:font-family serif
|
|
22
|
+
h2
|
|
23
|
+
:color #393e4d
|
|
24
|
+
:font-size 1.3em
|
|
25
|
+
|
|
26
|
+
> .content
|
|
27
|
+
.top-line
|
|
28
|
+
:text-align center
|
|
29
|
+
:font-style italic
|
|
30
|
+
:background #f6e5e4
|
|
31
|
+
:padding $line / 2 0
|
|
32
|
+
:margin-bottom $line
|
|
33
|
+
> aside, > .main-column
|
|
34
|
+
:display inline-block
|
|
35
|
+
:vertical-align top
|
|
36
|
+
:line-height 23px
|
|
37
|
+
h3
|
|
38
|
+
:margin
|
|
39
|
+
:bottom 23px
|
|
40
|
+
:top 0
|
|
41
|
+
p
|
|
42
|
+
:margin-bottom 23px
|
|
43
|
+
> aside
|
|
44
|
+
:margin-top 46px
|
|
45
|
+
:width $asideRation * $pageWidth
|
|
46
|
+
:line-height 20px
|
|
47
|
+
h4
|
|
48
|
+
:margin
|
|
49
|
+
:bottom 11px
|
|
50
|
+
:top 0px
|
|
51
|
+
:color #393e4d
|
|
52
|
+
ul
|
|
53
|
+
:margin 0
|
|
54
|
+
:padding-left 17px
|
|
55
|
+
> .main-column
|
|
56
|
+
:width $pageWidth - $asideRation * $pageWidth
|
data/httpit.gemspec
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "httpit"
|
|
3
|
-
s.version = "0.4"
|
|
3
|
+
s.version = "0.4.2"
|
|
4
4
|
s.summary = "Web server for static files"
|
|
5
|
-
s.description = "Just go to folder and run `httpit`"
|
|
5
|
+
s.description = "Just go to folder and run `httpit` to make it avaliable as web-server"
|
|
6
6
|
s.author = "Pavel Evstigneev"
|
|
7
7
|
s.email = "pavel.evst@gmail.com"
|
|
8
8
|
s.homepage = "http://github.com/Paxa/httpit"
|
|
@@ -11,8 +11,8 @@ Gem::Specification.new do |s|
|
|
|
11
11
|
s.rubyforge_project = "httpit"
|
|
12
12
|
s.files = `git ls-files`.split("\n")
|
|
13
13
|
|
|
14
|
-
s.add_runtime_dependency 'sinatra', "
|
|
15
|
-
s.add_runtime_dependency 'haml', '
|
|
16
|
-
s.add_runtime_dependency 'RedCloth', "
|
|
17
|
-
s.add_runtime_dependency '
|
|
14
|
+
s.add_runtime_dependency 'sinatra', "~> 1.4.2"
|
|
15
|
+
s.add_runtime_dependency 'haml', '~> 4.0.2'
|
|
16
|
+
s.add_runtime_dependency 'RedCloth', "~> 4.2.9"
|
|
17
|
+
s.add_runtime_dependency 'sass', "~> 3.2.8"
|
|
18
18
|
end
|
data/views/listing.haml
CHANGED
|
@@ -1,93 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
ul, li
|
|
11
|
-
:list-style-type none
|
|
12
|
-
|
|
13
|
-
ul > header
|
|
14
|
-
:font-style italic
|
|
15
|
-
:margin-bottom 11px
|
|
16
|
-
|
|
17
|
-
.preview_link
|
|
18
|
-
:color #55a075
|
|
19
|
-
:margin-left 20px
|
|
20
|
-
|
|
21
|
-
input[type=search]
|
|
22
|
-
:width 300px
|
|
23
|
-
input[type=submit]
|
|
24
|
-
:display none
|
|
25
|
-
|
|
26
|
-
= box-shadow($value)
|
|
27
|
-
:box-shadow $value
|
|
28
|
-
:-moz-box-shadow $value
|
|
29
|
-
:-webkit-box-shadow $value
|
|
30
|
-
|
|
31
|
-
#files_search
|
|
32
|
-
> .results
|
|
33
|
-
:display none
|
|
34
|
-
:background #fff
|
|
35
|
-
+box-shadow(1px 1px 5px rgba(125, 125, 125, 0.7))
|
|
36
|
-
:width 620px
|
|
37
|
-
:position relative
|
|
38
|
-
:top 7px
|
|
39
|
-
:padding 5px 15px
|
|
40
|
-
em
|
|
41
|
-
:background-color yellow
|
|
42
|
-
ul
|
|
43
|
-
:padding 0
|
|
44
|
-
:margin 0
|
|
45
|
-
li
|
|
46
|
-
:margin 3px 0
|
|
47
|
-
:font-size 13px
|
|
48
|
-
&:hover
|
|
49
|
-
:background-color #def
|
|
50
|
-
&.loading
|
|
51
|
-
> .results
|
|
52
|
-
:display block
|
|
53
|
-
</style>
|
|
1
|
+
!!! 5
|
|
2
|
+
%html(lang="en")
|
|
3
|
+
%head
|
|
4
|
+
<style type="text/css">
|
|
5
|
+
:sass
|
|
6
|
+
#wrap
|
|
7
|
+
:width 800px
|
|
8
|
+
:margin 30 auto 50px
|
|
54
9
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
10
|
+
span
|
|
11
|
+
:color #777
|
|
12
|
+
|
|
13
|
+
ul, li
|
|
14
|
+
:list-style-type none
|
|
15
|
+
|
|
16
|
+
ul > header
|
|
17
|
+
:font-style italic
|
|
18
|
+
:margin-bottom 11px
|
|
19
|
+
|
|
20
|
+
.preview_link
|
|
21
|
+
:color #55a075
|
|
22
|
+
:margin-left 20px
|
|
23
|
+
|
|
24
|
+
input[type=search]
|
|
25
|
+
:width 300px
|
|
26
|
+
input[type=submit]
|
|
27
|
+
:display none
|
|
28
|
+
</style>
|
|
29
|
+
|
|
30
|
+
%body
|
|
31
|
+
#wrap
|
|
32
|
+
%h1
|
|
33
|
+
%span Folder:
|
|
34
|
+
= @path == '' ? '/' : @path
|
|
35
|
+
- if @path != ''
|
|
36
|
+
%a{:href => @path.gsub(%r{/[^/]+/?$}, '') + '/'} ←
|
|
68
37
|
|
|
69
38
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
39
|
+
%ul
|
|
40
|
+
%header
|
|
41
|
+
Sort
|
|
42
|
+
%select.sorting
|
|
43
|
+
- for key in %w{name size ctime mtime}
|
|
44
|
+
%option{params[:sort] == key ? {:selected => "selected"} : {}}= key
|
|
76
45
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
46
|
+
- for file in @files
|
|
47
|
+
%li
|
|
48
|
+
- if file[1] == :file
|
|
49
|
+
%span= "–"
|
|
50
|
+
- elsif file[1] == :dir
|
|
51
|
+
%span= "+"
|
|
52
|
+
- elsif file[1] == :link
|
|
53
|
+
%span= "→"
|
|
85
54
|
|
|
86
|
-
|
|
55
|
+
%a{:href => "#{@path}/#{file[0]}"}= file[0]
|
|
87
56
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
57
|
+
- if %w{.jpg .jpeg .png .gif}.include?(File.extname(file[0]).downcase)
|
|
58
|
+
%a{:href => "/__img_preview?file=#{@path}/#{file[0]}", :class => "preview_link"} #1024
|
|
59
|
+
- if file[2]
|
|
60
|
+
%a{:href => "/__view?file=#{@path}/#{file[0]}", :class => "preview_link"} view
|
|
92
61
|
|
|
93
|
-
%script{:type => "text/javascript"}= GEM_ROOT.join('views/
|
|
62
|
+
%script{:type => "text/javascript"}= GEM_ROOT.join('views/utils.js').read
|
data/views/utils.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function $ (selector) {
|
|
2
|
+
return document.querySelector(selector);
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
function $$ (selector) {
|
|
6
|
+
return document.querySelectorAll(selector);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
$('.sorting').addEventListener('change', function(e) {
|
|
10
|
+
var clean_path = (window.location + '').replace(/(\?|&)sort=[^=]*/, '');
|
|
11
|
+
clean_path += (clean_path.indexOf('?') == -1 ? '?' : '&') + 'sort=' + e.target.value
|
|
12
|
+
window.location = clean_path;
|
|
13
|
+
}, false);
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: httpit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 0.4.2
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,53 +9,73 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2013-04-27 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: sinatra
|
|
16
|
-
requirement:
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
|
-
- -
|
|
19
|
+
- - ~>
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version:
|
|
21
|
+
version: 1.4.2
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements:
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 1.4.2
|
|
25
30
|
- !ruby/object:Gem::Dependency
|
|
26
31
|
name: haml
|
|
27
|
-
requirement:
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
28
33
|
none: false
|
|
29
34
|
requirements:
|
|
30
|
-
- -
|
|
35
|
+
- - ~>
|
|
31
36
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
37
|
+
version: 4.0.2
|
|
33
38
|
type: :runtime
|
|
34
39
|
prerelease: false
|
|
35
|
-
version_requirements:
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ~>
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 4.0.2
|
|
36
46
|
- !ruby/object:Gem::Dependency
|
|
37
47
|
name: RedCloth
|
|
38
|
-
requirement:
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
39
49
|
none: false
|
|
40
50
|
requirements:
|
|
41
|
-
- -
|
|
51
|
+
- - ~>
|
|
42
52
|
- !ruby/object:Gem::Version
|
|
43
|
-
version: 4.2.
|
|
53
|
+
version: 4.2.9
|
|
44
54
|
type: :runtime
|
|
45
55
|
prerelease: false
|
|
46
|
-
version_requirements:
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 4.2.9
|
|
47
62
|
- !ruby/object:Gem::Dependency
|
|
48
|
-
name:
|
|
49
|
-
requirement:
|
|
63
|
+
name: sass
|
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
65
|
none: false
|
|
51
66
|
requirements:
|
|
52
|
-
- -
|
|
67
|
+
- - ~>
|
|
53
68
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
69
|
+
version: 3.2.8
|
|
55
70
|
type: :runtime
|
|
56
71
|
prerelease: false
|
|
57
|
-
version_requirements:
|
|
58
|
-
|
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ~>
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: 3.2.8
|
|
78
|
+
description: Just go to folder and run `httpit` to make it avaliable as web-server
|
|
59
79
|
email: pavel.evst@gmail.com
|
|
60
80
|
executables:
|
|
61
81
|
- httpit
|
|
@@ -63,20 +83,21 @@ extensions: []
|
|
|
63
83
|
extra_rdoc_files: []
|
|
64
84
|
files:
|
|
65
85
|
- .gitignore
|
|
86
|
+
- Gemfile
|
|
87
|
+
- Gemfile.lock
|
|
66
88
|
- README.md
|
|
67
89
|
- bin/httpit
|
|
68
90
|
- example/images/bird.jpg
|
|
69
91
|
- example/index.haml
|
|
70
92
|
- example/main.sass
|
|
71
93
|
- example/readme.md
|
|
94
|
+
- example/testing_design/_header.haml
|
|
95
|
+
- example/testing_design/index.haml
|
|
96
|
+
- example/testing_design/layout.sass
|
|
72
97
|
- httpit.gemspec
|
|
73
|
-
- lib/search.rb
|
|
74
|
-
- lib/search/dystopia.rb
|
|
75
|
-
- lib/search/find.rb
|
|
76
98
|
- views/listing.haml
|
|
77
99
|
- views/not_found.haml
|
|
78
|
-
- views/
|
|
79
|
-
- views/search.js
|
|
100
|
+
- views/utils.js
|
|
80
101
|
- views/view.haml
|
|
81
102
|
homepage: http://github.com/Paxa/httpit
|
|
82
103
|
licenses: []
|
|
@@ -98,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
98
119
|
version: '0'
|
|
99
120
|
requirements: []
|
|
100
121
|
rubyforge_project: httpit
|
|
101
|
-
rubygems_version: 1.8.
|
|
122
|
+
rubygems_version: 1.8.24
|
|
102
123
|
signing_key:
|
|
103
124
|
specification_version: 3
|
|
104
125
|
summary: Web server for static files
|
data/lib/search.rb
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
module Search
|
|
2
|
-
def engine=(value)
|
|
3
|
-
@engine = value
|
|
4
|
-
end
|
|
5
|
-
|
|
6
|
-
def engine; @engine; end
|
|
7
|
-
|
|
8
|
-
def self.included(base)
|
|
9
|
-
base.class_eval do
|
|
10
|
-
Search.engine = Search::Find #Dystopia
|
|
11
|
-
|
|
12
|
-
get '/__search' do
|
|
13
|
-
@path = ROOT + params[:path].to_s
|
|
14
|
-
s_time = Time.now.to_f
|
|
15
|
-
@found = Search.engine.find_in_folder(ROOT, @path, params[:q])
|
|
16
|
-
@total_time = Time.now.to_f - s_time
|
|
17
|
-
|
|
18
|
-
haml view(:search)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
get '/__view' do
|
|
22
|
-
if !`file "#{ROOT.join(params[:file])}"`.index('text')
|
|
23
|
-
redirect params[:file]
|
|
24
|
-
return
|
|
25
|
-
end
|
|
26
|
-
@content = ROOT.join(params[:file]).read
|
|
27
|
-
haml view(:view)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
require GEM_ROOT + "lib/search/find"
|
|
34
|
-
require GEM_ROOT + "lib/search/dystopia"
|
data/lib/search/dystopia.rb
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
|
|
3
|
-
require 'rufus/tokyo/dystopia'
|
|
4
|
-
require 'digest'
|
|
5
|
-
|
|
6
|
-
=begin
|
|
7
|
-
A B : searches for records including the two tokens.
|
|
8
|
-
A && B : searches for records including the two tokens.
|
|
9
|
-
A || B : searches for records including the one or both of the two tokens.
|
|
10
|
-
"A B..." : searches for records including the phrase.
|
|
11
|
-
[[A]] : searches for records including words exactly matching the token.
|
|
12
|
-
[[A*]] : searches for records including words beginning with the token.
|
|
13
|
-
[[*A]] : searches for records including words ending with the token.
|
|
14
|
-
[[[[A : searches for records beginning with the token.
|
|
15
|
-
A]]]] : searches for records ending with the token.
|
|
16
|
-
Note that the priority of "||" is higher than the one of "&&".
|
|
17
|
-
=end
|
|
18
|
-
|
|
19
|
-
module Search::Dystopia
|
|
20
|
-
extend self
|
|
21
|
-
|
|
22
|
-
CMD = "find . -type d \\( -name '*.git*' \\) -prune -o -print"
|
|
23
|
-
|
|
24
|
-
def find_in_folder(root, path, needle)
|
|
25
|
-
files = inst.search(needle).map do |id|
|
|
26
|
-
doc = inst.fetch(id)
|
|
27
|
-
doc[0, doc.index("\n")]
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
if path != root
|
|
31
|
-
subdir = path.to_s.sub(root.to_s + '/', '')
|
|
32
|
-
files.select {|f| f[0, subdir.size] == subdir }.map {|f| f.sub(subdir + '/', '') }
|
|
33
|
-
else
|
|
34
|
-
files
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# return instance of searcher. and make indexing on first search
|
|
39
|
-
def inst
|
|
40
|
-
return @inst if @inst
|
|
41
|
-
db_file = "/tmp/httpit_index_#{Digest::MD5.hexdigest ROOT.to_s}"
|
|
42
|
-
@inst ||= Rufus::Tokyo::Dystopia::Core.new(db_file)
|
|
43
|
-
at_exit{ puts("removing #{db_file}"); `rm -r "#{db_file}"` }
|
|
44
|
-
index_dir()
|
|
45
|
-
@inst
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def index_dir()
|
|
49
|
-
files = `cd #{ROOT.to_s} && #{CMD}`.split("\n")
|
|
50
|
-
inst.clear
|
|
51
|
-
|
|
52
|
-
puts "Indexing #{files.size} files"
|
|
53
|
-
files.each_with_index do |file, i|
|
|
54
|
-
next if file[0, 3] == './.'
|
|
55
|
-
|
|
56
|
-
# if its a text file store its content
|
|
57
|
-
content = if `file "#{ROOT.join(file).to_s}"`.index('text')
|
|
58
|
-
(ROOT + file).read
|
|
59
|
-
else ""; end
|
|
60
|
-
inst.store(i, file.sub('./', '') + "\n" + content)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
inst
|
|
64
|
-
end
|
|
65
|
-
end
|
data/lib/search/find.rb
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
module Search::Find
|
|
2
|
-
extend self
|
|
3
|
-
CMD = "find . -type d \\( -name '*.git*' \\) -prune -o -exec file -F '&' {} \\; | grep text | cut -d\\& -f1 | sed 's/./\\\\&/g' | xargs -n 1 grep -i -l '###'"
|
|
4
|
-
CMD_LIST = "find . -type d \\( -name '*.git*' \\) -prune -o -print | grep -i '###'"
|
|
5
|
-
|
|
6
|
-
def find_in_folder(root, path, needle)
|
|
7
|
-
in_filenames = `cd #{path} && #{CMD_LIST.sub('###', needle)}`.split("\n")
|
|
8
|
-
in_text_files = `cd #{path} && #{CMD.sub('###', needle)}`.split("\n")
|
|
9
|
-
|
|
10
|
-
(in_filenames + in_text_files.select {|f| !in_filenames.include?(f) }).map {|f| f.sub('./', '') }
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
#def find_in_file(file, needle)
|
|
14
|
-
# `cat #{file} | grep -n -B 1 -A 1 '#{needle}'`
|
|
15
|
-
#end
|
|
16
|
-
end
|
data/views/search.haml
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
%ul
|
|
2
|
-
- for file in @found
|
|
3
|
-
%li<>
|
|
4
|
-
- if @path.join(file).file?
|
|
5
|
-
%a{:href => "/__view?file=#{File.join(params[:path], file)}" }= file.gsub(/#{params[:q]}/i) {|m| "<em>#{m}</em>" }
|
|
6
|
-
- else
|
|
7
|
-
%a{:href => File.join(params[:path], file)}= file.gsub(/#{params[:q]}/i) {|m| "<em>#{m}</em>" }
|
|
8
|
-
|
|
9
|
-
- if @found.size == 0
|
|
10
|
-
%i Nothing
|
|
11
|
-
|
|
12
|
-
%footer= "Complete in #{"%4.3fs" % @total_time}"
|
data/views/search.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
Xhr = function (url, data, callback) {
|
|
2
|
-
var xhr = new XMLHttpRequest();
|
|
3
|
-
|
|
4
|
-
xhr.open("GET", url + '?' + data, true);
|
|
5
|
-
|
|
6
|
-
xhr.onreadystatechange = function() {
|
|
7
|
-
if (xhr.readyState == 4 && xhr.status == 200) {
|
|
8
|
-
callback(xhr.responseText);
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
xhr.send();
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// get forms data for xhr request
|
|
16
|
-
Xhr.formData = function (form) {
|
|
17
|
-
var results = [];
|
|
18
|
-
var elements = form.querySelectorAll('input, select, textarea');
|
|
19
|
-
|
|
20
|
-
for(k in elements)
|
|
21
|
-
if (elements.hasOwnProperty(k) && elements[k].name)
|
|
22
|
-
results.push(elements[k].name + '=' + encodeURIComponent(elements[k].value))
|
|
23
|
-
|
|
24
|
-
return results.join('&')
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function $ (selector) {
|
|
28
|
-
return document.querySelector(selector);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
function $$ (selector) {
|
|
32
|
-
return document.querySelectorAll(selector);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
$('#files_search').addEventListener('submit', function (e) {
|
|
36
|
-
var form = e.target;
|
|
37
|
-
e.preventDefault();
|
|
38
|
-
|
|
39
|
-
form.querySelector('.results').innerHTML = 'Finding...';
|
|
40
|
-
form.className = 'loading';
|
|
41
|
-
|
|
42
|
-
new Xhr(form.action, Xhr.formData(form), function(data) {
|
|
43
|
-
form.querySelector('.results').innerHTML = data;
|
|
44
|
-
});
|
|
45
|
-
}, true);
|
|
46
|
-
|
|
47
|
-
// hide results when erase search field
|
|
48
|
-
function hideResults (e) {
|
|
49
|
-
if (e.target.value == '') $('#files_search').className = '';
|
|
50
|
-
}
|
|
51
|
-
$('input[type=search]').addEventListener('change', hideResults, false);
|
|
52
|
-
$('input[type=search]').addEventListener('keyup', hideResults, false);
|
|
53
|
-
$('input[type=search]').addEventListener('click', hideResults, false);
|
|
54
|
-
|
|
55
|
-
$('.sorting').addEventListener('change', function(e) {
|
|
56
|
-
var clean_path = (window.location + '').replace(/(\?|&)sort=[^=]*/, '');
|
|
57
|
-
clean_path += (clean_path.indexOf('?') == -1 ? '?' : '&') + 'sort=' + e.target.value
|
|
58
|
-
window.location = clean_path;
|
|
59
|
-
}, false);
|