├── .gems ├── README.md ├── bin ├── pngout └── yuicompressor-2.4.2.jar ├── build ├── config.ru ├── githubfinder.rb ├── ie9build ├── index.app.html ├── index.ie9.html ├── index.min.html ├── info.php ├── js2png ├── min └── bootstrap.js ├── optimize ├── proxy.php └── public ├── css └── code_highlighter.css ├── github_readme.css ├── img ├── dir.gif ├── dir.png ├── folder_explore.png ├── githubfinder.png ├── la.gif ├── la.png ├── la_h.gif ├── la_h.png ├── txt.gif └── txt.png ├── index.html ├── javascripts ├── .DS_Store ├── app.js ├── bootstrap.js ├── diff.js ├── f.js ├── gh.js ├── keyboard.js ├── p.js ├── plugins │ ├── base.js │ ├── bookmarklet.js │ ├── code_highlighter.js │ ├── github_readme.js │ ├── permalink.js │ ├── prefetch_tree.js │ └── resizeable_panel.js ├── prototype.js ├── recentlyChangedFiles.js ├── userscript.js ├── userscript.user.js ├── util.js └── vendors │ ├── code_highlighter.js │ ├── difflib.js │ ├── diffview.js │ ├── jsdiff.js │ ├── jsonp.js │ └── prettyDate.js └── styles.css /.gems: -------------------------------------------------------------------------------- 1 | sinatra 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Github Finder 2 | GithubFinder is a fast Github repository browser. The browser is inspired after Mac OSX Finder. 3 | [Github File Finder](https://raw.github.com/sr3d/GithubFinder/master/public/img/githubfinder.png) 4 | 5 | To see the finder in action, go to 6 | 7 | 8 | ## Features 9 | 10 | - Support Greasemonkey/Greaskit script: . After installing this script, a new button will appear on your Github repo page to let you quickly open the currently view repository in Github Finder. 11 | 12 | [Github File Greasemonkey Button](http://sr3d.github.com/GithubFinder/userscript.user.js) 13 | 14 | - Bookmarklet: If you don't have GreaseMonkey/Greasekit installed, you can drag the bookmarklet to your address bar. The bookmarklet will open a new GHFinder window of the currently viewed repository. 15 | - Switch between repos and branches with ease. 16 | - Keyboard navigation support with arrow keys. 17 | - Text files can be previewed inline with syntax highlighting (support Ruby, JavaScript, CSS, and HTML) with Textmate-like Twilight theme. 18 | - Files can be diff-ed directly between commits (diff is done by difflib). 19 | - 100% javascript with full cross-domain support. For functionalities that Github API, the app relies on a proxy running on Heroku () to get the data. 20 | - flexible framework to customize and extend the existing functionalities. 21 | - GitHub JS API wrapper (/javascripts/gh.js). 22 | 23 | 24 | # 10K App 25 | 26 | Github Finder is built as a contest entry into the 10K Apart contest (http://10k.aneventapart.com/).. I wanted to develop a real useful app instead of a mindless canvas drawing (which is cool, but not that useful), and I always wanted to browse Github repositories easier and faster. 27 | 28 | ## Build 29 | 30 | Since the app needs to be under 10,240 bytes, it needs special optimization to achieve this size. The unminifed, uncompressed JS is about 30+ KB, all compressed down to 8.5KB or so. 31 | 32 | To build the app, run the provided ./build script to automatically bundle up everything into the ./app folder 33 | 34 | For an IE9 build, run ./ie9build 35 | 36 | ## App Bootstrap 37 | 38 | The minified app is working as follow: 39 | 40 | - bootstrap.js loads code from png and eval 41 | - javascripts/app.js file is executed (this file is appended at the end of the files list) 42 | 43 | 44 | # Architecture 45 | 46 | The code is somewhat modularized with a main class (F, defined in /javascripts/f.js) for the Finder itself. This class can be extended to add more functionalities through various plugins. Currently the plugins are 47 | 48 | - Diff: allowing user to diff 2 different files from Github. 49 | - Code Highlighter: currently supports Ruby, JavaScript, CSS, and HTML. 50 | - Keyboard navigation: user can use the arrow keys to navigate. 51 | - ResizablePanel: enable the panels to be resizeable. 52 | - GithubReadme: automatically grab the Readme files from github. Currently supported Markdown (.md, .markdown), Textile (.textile) readme files (Github only renders these files automatically) 53 | - Bookmarklet: lets user create a bookmark to quickly access GithubFinder 54 | - Greasemonk/Greasekit userscript: extend Github's interface with a button linking to Github Finder to allow quick access. 55 | 56 | Combined with the build script, this architecture is flexible enough to enable or disable certain components to achieve the right size/features. 57 | 58 | The Application also relies on JSONP to grab the data from Github. The provided proxy.php script can handle both normal Ajax Requests and JSONP requests. However, the proxy now is done by a small Sinatra app running on Heroku at . The source for this app is at 59 | 60 | 61 | # Credits 62 | 63 | Github Finder is possible by leveraging these tools: 64 | 65 | - string2png is inspired by NIHILOGIC. 66 | - The javascript implementation of difflib by Snowtide.com. 67 | - Dandean JSONP implementation for Prototype. Code is patched to support onSuccess() event, and to also hook into Ajax.Responders live-cycle (useful for indicator). 68 | - Github API. 69 | 70 | 71 | # About 72 | 73 | - My name is Alex Le. I'm an entrepreneur and I build web applications. I'm a single founder bootstrapping Marrily (), an online wedding planner. Come check it out! My blog is at 74 | -------------------------------------------------------------------------------- /bin/pngout: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/bin/pngout -------------------------------------------------------------------------------- /bin/yuicompressor-2.4.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/bin/yuicompressor-2.4.2.jar -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'ftools' 3 | require 'fileutils' 4 | require 'rubygems' 5 | require 'RMagick' 6 | include Magick 7 | 8 | require 'open3' 9 | 10 | def merge( files = [] ) 11 | merged_file = "" 12 | files.each do |file| 13 | File.open( file, "r") { |f| 14 | merged_file += f.read + "\n" 15 | } 16 | end 17 | merged_file 18 | end 19 | 20 | def minify( string = '', type = 'js' ) 21 | cmd = "java -jar bin/yuicompressor-2.4.2.jar --type #{type}" 22 | stdin, stdout, stderr = Open3.popen3( cmd ) 23 | stdin.write string 24 | stdin.close 25 | stdout.read 26 | end 27 | 28 | def minify_to_file( string, type, output ) 29 | cmd = "java -jar bin/yuicompressor-2.4.2.jar -v --type #{type} -o #{output}" 30 | # puts cmd 31 | stdin, stdout, stderr = Open3.popen3( cmd ) 32 | stdin.write string 33 | stdin.close 34 | puts stderr.read 35 | end 36 | 37 | def string2png( string, output ) 38 | image = Magick::Image.new 1,string.length 39 | for i in 0 .. string.length 40 | color = '#%02X0000' % string[i] 41 | pixel = Magick::Pixel.from_color color 42 | image.pixel_color 0, i, pixel 43 | end 44 | image.compression = ZipCompression 45 | image.write("png8:"+ output) 46 | 47 | image = nil 48 | end 49 | 50 | 51 | 52 | 53 | release_path = 'app' 54 | 55 | # Clean up 56 | puts "Clean up release folder (#{release_path})" 57 | FileUtils.rm_rf release_path 58 | File.makedirs release_path + '/img' 59 | File.makedirs release_path + '/js' 60 | 61 | files_to_copy = { 62 | 'index.app.html' => 'index.html', 63 | 'img/dir.gif' => 'img/dir.gif', 64 | 'img/la.gif' => 'img/la.gif', 65 | 'img/la_h.gif' => 'img/la_h.gif', 66 | 'img/txt.gif' => 'img/txt.gif', 67 | } 68 | 69 | puts "Copy files" 70 | files_to_copy.each do |src,dest| 71 | dest = release_path + '/' + dest 72 | puts " cp #{src} => #{dest}" 73 | File.copy src, dest 74 | end 75 | 76 | 77 | js_files = [ 78 | 'javascripts/vendors/jsonp.js', 79 | 'javascripts/util.js', 80 | 'javascripts/f.js', 81 | 'javascripts/p.js', 82 | 'javascripts/gh.js', 83 | 84 | 'javascripts/plugins/base.js', 85 | # 'javascripts/diff.js', 86 | # 'javascripts/keyboard.js', 87 | 88 | 'javascripts/plugins/code_highlighter.js', 89 | 'javascripts/vendors/code_highlighter.js', 90 | 91 | # 'javascripts/plugins/resizeable_panel.js', 92 | 93 | 94 | 95 | # 'javascripts/vendors/difflib.js', 96 | # 'javascripts/vendors/diffview.js', 97 | 98 | 'javascripts/app.js' 99 | ] 100 | 101 | 102 | puts "bundling js and css:" 103 | 104 | puts " minifying js..." 105 | js = minify merge( js_files ) 106 | 107 | puts " minifying css..." 108 | css = minify(merge([ 109 | 'styles.css', 110 | 'css/code_highlighter.css' 111 | ]), 'css') 112 | 113 | 114 | puts " bundling..." 115 | string2png js + "~10K~" + css, release_path + '/c.png' # bundle both js + css into 1 image 116 | 117 | 118 | files_to_minify = [ 119 | { :files => [ 'javascripts/bootstrap.js'], :output => 'js/bootstrap.js', :type => 'js' } 120 | # { :files => [ 'styles.css'], :output => 'styles.css', :type => 'css' } 121 | ] 122 | files_to_minify.each do |group| 123 | puts "minify #{group[:output]}" 124 | minify_to_file merge(group[:files]), group[:type], release_path + '/' + group[:output] 125 | end 126 | 127 | 128 | puts "------ Size Report ------" 129 | 130 | files_to_skip_counting = [ 131 | 'prototype.js', 132 | 'styles.css', 133 | 'img' 134 | ] 135 | 136 | 137 | total_size = 0 138 | Dir.glob( [release_path + '/*', release_path + '/img/*',release_path + '/js/*' ] ).each do |file| 139 | next if files_to_skip_counting.include? File.basename(file) 140 | puts "#{"%-20.20s" % file} #{"%30s" % File.stat( file ).size}" 141 | total_size += File.stat( file ).size 142 | end 143 | puts "Total size: #{total_size}" 144 | puts "10K Limit: #{- total_size + 10240}" 145 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require './githubfinder' 2 | run Sinatra::Application -------------------------------------------------------------------------------- /githubfinder.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | 3 | get '/' do 4 | "It works!" 5 | end -------------------------------------------------------------------------------- /ie9build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'ftools' 3 | require 'fileutils' 4 | require 'rubygems' 5 | require 'RMagick' 6 | include Magick 7 | 8 | require 'open3' 9 | 10 | def merge( files = [] ) 11 | merged_file = "" 12 | files.each do |file| 13 | File.open( file, "r") { |f| 14 | merged_file += f.read + "\n" 15 | } 16 | end 17 | merged_file 18 | end 19 | 20 | def minify( string = '', type = 'js' ) 21 | cmd = "java -jar bin/yuicompressor-2.4.2.jar --type #{type}" 22 | stdin, stdout, stderr = Open3.popen3( cmd ) 23 | stdin.write string 24 | stdin.close 25 | stdout.read 26 | end 27 | 28 | def minify_to_file( string, type, output ) 29 | cmd = "java -jar bin/yuicompressor-2.4.2.jar -v --type #{type} -o #{output} --charset utf-8" 30 | # puts cmd 31 | stdin, stdout, stderr = Open3.popen3( cmd ) 32 | stdin.write string 33 | stdin.close 34 | puts stderr.read 35 | end 36 | 37 | def string2png( string, output ) 38 | image = Magick::Image.new 1,string.length 39 | for i in 0 .. string.length 40 | color = '#%02X0000' % string[i] 41 | pixel = Magick::Pixel.from_color color 42 | image.pixel_color 0, i, pixel 43 | end 44 | image.compression = ZipCompression 45 | image.write("png8:"+ output) 46 | 47 | image = nil 48 | end 49 | 50 | def png2string(image) 51 | string = '' 52 | Image.read(File.open( image, "r" )).first.each_pixel do |px,x,y| 53 | string += (px.red % 256).chr 54 | end 55 | string 56 | end 57 | 58 | 59 | def string2pngs( string, output ) 60 | puts "original string length #{string.length}" 61 | limit = 8192 #8192 # IE9 max for getImageData 62 | images_count = (string.length * 1.0 / limit ).ceil 63 | 64 | files = [] 65 | 66 | for i in 0 ... images_count 67 | 68 | filename = output.split('.') 69 | filename[ filename.length - 2 ] += i.to_s 70 | filename = filename.join('.') 71 | 72 | files << filename 73 | 74 | from = i * limit 75 | to = from + [ limit, string.length - from ].min 76 | 77 | puts "output from #{from} to #{to}" 78 | string2png string[from,to], filename 79 | 80 | files << filename 81 | end 82 | 83 | # verify 84 | # s = '' 85 | # files.each{ |file| s += png2string(file) } 86 | # raise ('invalid string, expecting ' + s + "\n\n---but got---\n\n " + string) unless s == string 87 | 88 | # total_size = 0 89 | # files.each{ |file| puts "#{"%-20.20s" % file} #{"%30s" % File.stat( file ).size}"; total_size += File.stat( file ).size } 90 | # puts total_size 91 | 92 | files 93 | end 94 | 95 | 96 | def pngout files = [] 97 | files.each do |file| 98 | cmd = "bin/pngout #{file} -c2 -f3 -b128 -kbKGD -v" 99 | puts `#{cmd}` 100 | end 101 | end 102 | 103 | 104 | release_path = 'ie9' 105 | 106 | # Clean up 107 | puts "Clean up release folder (#{release_path})" 108 | FileUtils.rm_rf release_path 109 | File.makedirs release_path + '/img' 110 | File.makedirs release_path + '/js' 111 | 112 | files_to_copy = { 113 | 114 | 'index.ie9.html' => 'index.html', 115 | # 'ie9fix.css' => 'ie9fix.css', 116 | 'img/dir.gif' => 'img/dir.gif', 117 | 'img/la.gif' => 'img/la.gif', 118 | 'img/la_h.gif' => 'img/la_h.gif', 119 | 'img/txt.gif' => 'img/txt.gif', 120 | 121 | 122 | # 'img/dir.png' => 'img/dir.png', 123 | # 'img/la.png' => 'img/la.png', 124 | # 'img/la_h.png' => 'img/la_h.png', 125 | # 'img/txt.png' => 'img/txt.png', 126 | # 'javascripts/prototype.js' => 'prototype.js', 127 | 128 | 129 | # 'javascripts/vendors/jsonp.js' => 'jsonp.js', 130 | # 'javascripts/util.js' => 'util.js', 131 | # 'javascripts/f.js' => 'f.js', 132 | # 'javascripts/p.js' => 'p.js', 133 | # 'javascripts/diff.js' => 'diff.js', 134 | # 'javascripts/gh.js' => 'gh.js' , 135 | # 'javascripts/keyboard.js' => 'keyboard.js', 136 | # 137 | # 138 | # 'javascripts/vendors/difflib.js' => 'difflib.js', 139 | # 'javascripts/vendors/diffview.js' => 'diffview.js' 140 | } 141 | 142 | puts "Copy files" 143 | files_to_copy.each do |src,dest| 144 | dest = release_path + '/' + dest 145 | puts " cp #{src} => #{dest}" 146 | File.copy src, dest 147 | end 148 | 149 | 150 | js_files = [ 151 | 'javascripts/vendors/jsonp.js', 152 | 'javascripts/util.js', 153 | 'javascripts/f.js', 154 | 'javascripts/p.js', 155 | 'javascripts/gh.js', 156 | 157 | 'javascripts/plugins/base.js', 158 | # 'javascripts/diff.js', 159 | 'javascripts/keyboard.js', 160 | 161 | # 'javascripts/plugins/resizeable_panel.js', 162 | 'javascripts/plugins/github_readme.js', 163 | 'javascripts/plugins/bookmarklet.js', 164 | 165 | 166 | # 'javascripts/plugins/code_highlighter.js', 167 | # 'javascripts/vendors/code_highlighter.js', 168 | # 'javascripts/vendors/difflib.js', 169 | # 'javascripts/vendors/diffview.js', 170 | 171 | 172 | 'javascripts/app.js' 173 | ] 174 | 175 | 176 | puts "bundling js and css:" 177 | 178 | puts " minifying js..." 179 | js = minify merge( js_files ) 180 | 181 | puts " minifying css..." 182 | css = minify(merge([ 183 | 'styles.css', 184 | # 'github_readme.css' 185 | 186 | ]), 'css') 187 | 188 | 189 | puts " bundling..." 190 | # string2png js + "~10K~" + css, release_path + '/c.png' # bundle both js + css into 1 image 191 | 192 | code_pngs = string2pngs js + "~10K~" + css, release_path + '/js/c.png' 193 | 194 | pngout code_pngs # further compress! 195 | 196 | files_to_minify = [ 197 | { :files => [ 'javascripts/bootstrap.js'], :output => 'js/bootstrap.js', :type => 'js' }, 198 | # { :files => [ 'ie9fix.css'], :output => 'ie9fix.css', :type => 'css' } 199 | ] 200 | files_to_minify.each do |group| 201 | puts "minify #{group[:output]}" 202 | minify_to_file merge(group[:files]), group[:type], release_path + '/' + group[:output] 203 | end 204 | 205 | 206 | puts "------ Size Report ------" 207 | 208 | files_to_skip_counting = [ 209 | 'prototype.js', 210 | 'styles.css', 211 | 'img', 212 | 'js' 213 | ] 214 | 215 | 216 | total_size = 0 217 | Dir.glob( [release_path + '/*', release_path + '/img/*',release_path + '/js/*' ] ).each do |file| 218 | # puts file 219 | next if files_to_skip_counting.include? File.basename(file) 220 | 221 | puts "#{"%-20.20s" % file} #{"%30s" % File.stat( file ).size}" 222 | total_size += File.stat( file ).size 223 | end 224 | puts "Total size: #{total_size}" 225 | puts "10K Limit: #{- total_size + 10240}" 226 | 227 | 228 | 229 | #puts png2string( release_path + '/c.png')[-30,30] 230 | 231 | 232 | 233 | # string2pngs js + "~10K~" + css, release_path + '/c.png' 234 | -------------------------------------------------------------------------------- /index.app.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.ie9.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /index.min.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Github File Finder 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /info.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js2png: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'ftools' 3 | require 'rubygems' 4 | require 'RMagick' 5 | include Magick 6 | 7 | files = [ 8 | 'javascripts/f.js', 9 | 'javascripts/p.js', 10 | 'javascripts/gh.js', 11 | 'javascripts/diff.js', 12 | 'javascripts/keyboard.js', 13 | 14 | 'javascripts/vendors/difflib.js', 15 | 'javascripts/vendors/diffview.js' 16 | # 'javascripts/prototype.js' 17 | ] 18 | 19 | temp_file = 'merge.js' 20 | min_temp_file = 'merge.min.js' 21 | image_file = "code.png" 22 | crushed_image_file = "code.crushed.png" 23 | 24 | # Merged and minify scripts 25 | merged_file = "" 26 | files.each {|file| 27 | File.open( file, "r") { |f| 28 | merged_file += f.read + "\n" 29 | } 30 | } 31 | File.open( temp_file , "w") {|f| f.write(merged_file) } 32 | 33 | cmd = "java -jar bin/yuicompressor-2.4.2.jar #{temp_file} -o #{min_temp_file}" 34 | 35 | js = `#{cmd}` 36 | 37 | # debug out put 38 | # File.open( min_temp_file , "w") {|f| f.write(js) } 39 | js = '' 40 | File.open( min_temp_file, "r") { |f| js += f.read + "\n" } 41 | 42 | image = Magick::Image.new(1,js.length) 43 | i = 0 44 | while i < js.length do 45 | # color is in format #RRGGBB 46 | color = '#%02X0000' % js[i] 47 | 48 | x, y = 0, i 49 | pixel = Magick::Pixel.from_color color 50 | image.pixel_color x, y, pixel 51 | i += 1 52 | end 53 | 54 | 55 | image.compression = ZipCompression 56 | image.write("png8:"+ image_file) 57 | 58 | File.open( temp_file , "r" ) 59 | pic = (Image.read(image_file)).first 60 | 61 | 62 | # puts "Validates Output" 63 | # s = '' 64 | # pic.each_pixel do |px, x, y| 65 | # s += (px.red % 256).chr 66 | # end 67 | # puts s == js 68 | 69 | 70 | puts "Script Length: #{js.length}" 71 | 72 | temp_file = 'merge.js' 73 | min_temp_file = 'merge.min.js' 74 | image_file = "code.png" 75 | [ temp_file, min_temp_file, image_file ].each do |file| 76 | puts "#{"%-20.20s" % file} #{"%30s" % File.stat( file ).size}" 77 | end 78 | -------------------------------------------------------------------------------- /min/bootstrap.js: -------------------------------------------------------------------------------- 1 | document.on("dom:loaded",function(){var img=new Image();img.onload=function(){var js="",canvas=document.createElement("canvas"),context=canvas.getContext("2d"),width=img.width,height=img.height;canvas.width=canvas.style.width=width;canvas.height=canvas.style.height=height;context.drawImage(img,0,0);var data=context.getImageData(0,0,width,height).data;for(var i=0,len=data.length;i0){js+=String.fromCharCode(data[i])}}eval(js);run()};img.src=window.codePng}); -------------------------------------------------------------------------------- /optimize: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'ftools' 3 | 4 | files = [ 5 | 'javascripts/vendors/jsonp.js', 6 | 'javascripts/util.js', 7 | 'javascripts/f.js', 8 | 'javascripts/p.js', 9 | 'javascripts/diff.js', 10 | 'javascripts/gh.js', 11 | 'javascripts/keyboard.js', 12 | 13 | 'javascripts/vendors/difflib.js', 14 | 'javascripts/vendors/diffview.js', 15 | ] 16 | 17 | total_size = 0 18 | files.each do |file| 19 | puts "#{"%-20.20s" % file} #{"%30s" % File.stat( file ).size}" 20 | total_size += File.stat( file ).size 21 | end 22 | 23 | puts "#{"%-20.20s" % "Total Size"} #{"%30s" % total_size}" 24 | 25 | full_size = total_size 26 | 27 | puts "--------------------------" 28 | 29 | # Optimize 30 | total_size = 0 31 | files.each do |file| 32 | 33 | # minify the size 34 | if ['.css', '.js'].include? File.extname(file) 35 | 36 | minified_file = "./min/#{File.basename(file)}" 37 | 38 | puts minified_file 39 | cmd = "java -jar bin/yuicompressor-2.4.2.jar #{file} -v -o #{minified_file}" 40 | 41 | `#{cmd}` 42 | 43 | puts "#{"%-20.20s" % minified_file} #{"%30s" % File.stat( minified_file ).size}" 44 | total_size += File.stat( minified_file ).size 45 | 46 | else 47 | puts "#{"%-20.20s" % file} #{"%30s" % File.stat( file ).size}" 48 | total_size += File.stat( file ).size 49 | end 50 | 51 | end 52 | 53 | puts "Total size: #{total_size}" 54 | 55 | puts "Optimization Ratio: #{"%.2f" % ((1 - total_size * 1.0 /full_size) * 100)}%" -------------------------------------------------------------------------------- /proxy.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/css/code_highlighter.css: -------------------------------------------------------------------------------- 1 | #code.Dark { background-color: #191919; color: white; } 2 | #code.Dark .comment { color: #5E5A5F; } 3 | #code.Dark .string { color: #93996D; } 4 | #code.Dark .keywords { color: #CAA76F;} 5 | #code.Dark .brackets { color: #F5F8F8; } 6 | #code.Dark .symbol { color: #C06F52; } 7 | 8 | /* CSS */ 9 | #code.Dark .properties { color: #C4AD7A} 10 | #code.Dark .selectors { color: #977042 } 11 | 12 | 13 | 14 | 15 | #code.Light { background-color: white; color: black; } 16 | #code.Light .comment { color: green; } 17 | #code.Light .string { color: teal; } 18 | #code.Light .keywords { color: navy; font-weight: bold; } 19 | #code.Light .brackets { color: navy; } 20 | #code.Light .symbol { font-weight: bold; } 21 | 22 | /* CSS */ 23 | #code.Light .properties { color: #C4AD7A} 24 | #code.Light .selectors { color: #977042 } 25 | 26 | /* CPP */ 27 | #code.Ligh .datatype { color: #006699; } 28 | #code.Dark .datatype { font-weight: bold;} -------------------------------------------------------------------------------- /public/github_readme.css: -------------------------------------------------------------------------------- 1 | #readme_wrapper { min-width: 920px; width: 100%; /*border: 1px solid #DCDCDC */} 2 | #readme img { max-width: 100%; border: 0px; padding: 10px; } 3 | 4 | /* thanks! github! <3 */ 5 | .wikistyle h1,.wikistyle h2,.wikistyle h3,.wikistyle h4,.wikistyle h5,.wikistyle h6{border:0!important;} 6 | .wikistyle h1{font-size:170%!important;border-top:4px solid #aaa!important;padding-top:.5em!important;margin-top:1.5em!important;} 7 | .wikistyle h1:first-child{margin-top:0!important;padding-top:.25em!important;border-top:none!important;} 8 | .wikistyle h2{font-size:150%!important;margin-top:1.5em!important;border-top:4px solid #e0e0e0!important;padding-top:.5em!important;} 9 | .wikistyle h3{margin-top:1em!important;} 10 | .wikistyle p{margin:1em 0!important;line-height:1.5em!important;} 11 | .wikistyle a.absent{color:#a00;} 12 | .wikistyle ul,#wiki-form .content-body ul{margin:1em 0 1em 2em!important;} 13 | .wikistyle ol,#wiki-form .content-body ol{margin:1em 0 1em 2em!important;} 14 | .wikistyle ul ul,.wikistyle ul ol,.wikistyle ol ol,.wikistyle ol ul,#wiki-form .content-body ul ul,#wiki-form .content-body ul ol,#wiki-form .content-body ol ol,#wiki-form .content-body ol ul{margin-top:0!important;margin-bottom:0!important;} 15 | .wikistyle blockquote{margin:1em 0!important;border-left:5px solid #ddd!important;padding-left:.6em!important;color:#555!important;} 16 | .wikistyle dt{font-weight:bold!important;margin-left:1em!important;} 17 | .wikistyle dd{margin-left:2em!important;margin-bottom:1em!important;} 18 | .wikistyle table{margin:1em 0!important;} 19 | .wikistyle table th{border-bottom:1px solid #bbb!important;padding:.2em 1em!important;} 20 | .wikistyle table td{border-bottom:1px solid #ddd!important;padding:.2em 1em!important;} 21 | .wikistyle pre{margin:1em 0!important;font-size:12px!important;background-color:#f8f8ff!important;border:1px solid #dedede!important;padding:.5em!important;line-height:1.5em!important;color:#444!important;overflow:auto!important;} 22 | .wikistyle pre code{padding:0!important;font-size:12px!important;background-color:#f8f8ff!important;border:none!important;} 23 | .wikistyle code{font-size:12px!important;background-color:#f8f8ff!important;color:#444!important;padding:0 .2em!important;border:1px solid #dedede!important;} 24 | .wikistyle img{max-width:100%;} 25 | .wikistyle pre.console{margin:1em 0!important;font-size:12px!important;background-color:black!important;padding:.5em!important;line-height:1.5em!important;color:white!important;} 26 | .wikistyle pre.console code{padding:0!important;font-size:12px!important;background-color:black!important;border:none!important;color:white!important;} 27 | .wikistyle pre.console span{color:#888!important;} 28 | .wikistyle pre.console span.command{color:yellow!important;} 29 | 30 | .wikistyle .frame{margin:0;display:inline-block;} 31 | .wikistyle .frame img{display:block;} 32 | .wikistyle .frame>span{display:block;border:1px solid #aaa;padding:4px;} 33 | .wikistyle .frame span span{display:block;font-size:10pt;margin:0;padding:4px 0 2px 0;text-align:center;line-height:10pt;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;} 34 | .wikistyle .float-left{float:left;padding:.5em 1em .25em 0;} 35 | .wikistyle .float-right{float:right;padding:.5em 0 .25em 1em;} 36 | .wikistyle .align-left{display:block;text-align:left;} 37 | .wikistyle .align-center{display:block;text-align:center;} 38 | .wikistyle .align-right{display:block;text-align:right;} 39 | .wikistyle.gollum.footer{border-top:4px solid #f0f0f0;margin-top:2em;} 40 | .wikistyle.gollum>h1:first-child{display:none;} 41 | .wikistyle.gollum.asciidoc>div#header>h1:first-child{display:none;} 42 | .wikistyle.gollum.asciidoc .ulist p,.wikistyle.gollum.asciidoc .olist p{margin:0!important;} 43 | .wikistyle.gollum.asciidoc .loweralpha{list-style-type:lower-alpha;} 44 | .wikistyle.gollum.asciidoc .lowerroman{list-style-type:lower-roman;} 45 | .wikistyle.gollum.asciidoc .upperalpha{list-style-type:upper-alpha;} 46 | .wikistyle.gollum.asciidoc .upperroman{list-style-type:upper-roman;} 47 | .wikistyle.gollum.org>p.title:first-child{display:none;} 48 | .wikistyle.gollum.org p:first-child+h1{border-top:none!important;} 49 | .wikistyle.gollum.pod>a.dummyTopAnchor:first-child+h1{display:none;} 50 | .wikistyle.gollum.pod h1 a{text-decoration:none;color:inherit;} 51 | 52 | 53 | -------------------------------------------------------------------------------- /public/img/dir.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/dir.gif -------------------------------------------------------------------------------- /public/img/dir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/dir.png -------------------------------------------------------------------------------- /public/img/folder_explore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/folder_explore.png -------------------------------------------------------------------------------- /public/img/githubfinder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/githubfinder.png -------------------------------------------------------------------------------- /public/img/la.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/la.gif -------------------------------------------------------------------------------- /public/img/la.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/la.png -------------------------------------------------------------------------------- /public/img/la_h.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/la_h.gif -------------------------------------------------------------------------------- /public/img/la_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/la_h.png -------------------------------------------------------------------------------- /public/img/txt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/txt.gif -------------------------------------------------------------------------------- /public/img/txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/img/txt.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Github File Finder 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 52 | 53 | 54 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /public/javascripts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/javascripts/.DS_Store -------------------------------------------------------------------------------- /public/javascripts/app.js: -------------------------------------------------------------------------------- 1 | /* JS to execute specifically in the bootstrap 2 | This script is to be embedded in the image to further save space 3 | */ 4 | GH.setProxy('http://alexle.net/experiments/githubfinder/proxy.php?url='); 5 | f = new F(); 6 | -------------------------------------------------------------------------------- /public/javascripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | var f,proxy,d=document; 2 | 3 | var addCss = function(s) { 4 | var c = d.createElement('style'); 5 | c.type = 'text/css'; 6 | if (c.styleSheet) 7 | c.styleSheet.cssText = s; 8 | else 9 | c.appendChild(d.createTextNode(s)); 10 | d.getElementsByTagName('HEAD')[0].appendChild(c); 11 | } 12 | 13 | /* xtract textual content from an image*/ 14 | var x = function(z, m, ix ) { // image, callback, chunk index 15 | var o = new Image(); 16 | o.onload = function() { 17 | var s = "", 18 | c = d.createElement("canvas"), 19 | t = c.getContext("2d"), 20 | w = o.width, 21 | h = o.height; 22 | c.width = c.style.width = w; 23 | c.height = c.style.height = h; 24 | t.drawImage(o, 0, 0); 25 | 26 | var b = t.getImageData( 0, 0, w, h ).data; //b : bucket of data 27 | for(var i= 0; i < b.length; i += 4) { 28 | if( b[i] > 0 ) 29 | s += String.fromCharCode(b[i]); 30 | } 31 | 32 | m(s, ix); 33 | } 34 | o.src = z; 35 | } 36 | 37 | var fc = 3, flag = 0, chks = [], js; 38 | var run = function(s,i) { 39 | chks[i] = s 40 | if( ++flag != fc ) return; 41 | 42 | /* Now we have the full script, let's the fun begin */ 43 | var js = (chks[0] + chks[1] + chks[3]).split('~10K~'); 44 | addCss(js[1]); 45 | try{ eval(js[0]); } catch(ex) { alert(ex); } 46 | } 47 | 48 | document.observe('dom:loaded',function() { 49 | /* IE9 has a bug with getImageData to read all the pixel data. 50 | So the JS was splitted into smaler images instead. */ 51 | for(var i = 0; i < fc; i++ ) { 52 | x('js/c' + i + '.png', run, i); 53 | } 54 | }); 55 | -------------------------------------------------------------------------------- /public/javascripts/diff.js: -------------------------------------------------------------------------------- 1 | /* Diff plugin 2 | require difflib and diffview 3 | */ 4 | var Diff = Class.create( PluginBase, { 5 | mixin: { 6 | diff: function( commitId1, commitId2, filename) { 7 | 8 | $('f').hide(); 9 | var file1, file2, diff, flag = 0; 10 | 11 | var process = function(file, mainFile) { 12 | 13 | if( mainFile ) 14 | file1 = file; 15 | else 16 | file2 = file; 17 | 18 | // debugger 19 | 20 | if( ++flag < 2 ) return; 21 | 22 | try{ 23 | file1 = difflib.stringAsLines(file1); 24 | 25 | if( file1.length > 500 26 | && !confirm("Diffing large file will take a long time\nDo you want to continue?")) { 27 | return; 28 | } 29 | 30 | file2 = difflib.stringAsLines(file2); 31 | var sm = new difflib.SequenceMatcher( file1, file2 ), 32 | opc = sm.get_opcodes(), 33 | dO = $("diffoutput").update('').show(); 34 | var node = diffview.buildView({ 35 | baseTextLines: file1, 36 | newTextLines: file2, 37 | opcodes: opc, 38 | baseTextName: s(commitId1), 39 | newTextName: s(commitId2) 40 | }); 41 | dO.appendChild( node ); 42 | } catch(ex) { 43 | alert(ex); 44 | } 45 | } // process 46 | 47 | var u = this.u, r = this.r, b = this.b; 48 | 49 | /* load a file from a tree */ 50 | var lf = function(sha, fn, mainFile) { 51 | GH.Raw.loadBlobAtCommit( u, r , sha, filename, { 52 | onSuccess: function(response) { 53 | process(response.responseText, mainFile); 54 | } 55 | }); 56 | }; 57 | 58 | lf(commitId1, filename, true); 59 | lf(commitId2, filename); 60 | } // function 61 | } 62 | 63 | 64 | ,initialize: function($super, f) { 65 | $super(f); 66 | } 67 | 68 | }); 69 | 70 | /* add the plugin to the plugins list */ 71 | window.FP.push(Diff); -------------------------------------------------------------------------------- /public/javascripts/f.js: -------------------------------------------------------------------------------- 1 | /* to padd to get exactly 10,240 bytes */ 2 | // ;"ALEXLE"; 3 | window.F = Class.create({ 4 | initialize: function(options){ 5 | options = Object.extend( { 6 | user_id: 'rails' 7 | ,repository: 'rails' 8 | ,branch: 'master' 9 | }, options || {} ); 10 | 11 | this.ps = []; 12 | this.shas = {}; 13 | 14 | this.u = options.user_id; 15 | this.r = options.repository; 16 | this.b = options.branch; 17 | 18 | this.render(); 19 | 20 | this.repo = null; 21 | 22 | /* Prototype RC2 */ 23 | // document.on('click','a[data-sha]', function( event, element ){ 24 | // this.click( element.readAttribute('data-sha'), element ); 25 | // element.blur(); 26 | // }.bind(this) ); 27 | 28 | document.observe('click', function(e) { 29 | e = e.findElement(); 30 | if( !e.readAttribute('data-sha') ) return; 31 | this.click( e.readAttribute('data-sha'), e ); 32 | e.blur(); 33 | }.bind(this)); 34 | 35 | var idc = $('in'), 36 | s = function() { idc.show() }, 37 | h = function() { if( Ajax.activeRequestCount == 0 ) idc.hide() }; 38 | Ajax.Responders.register( { 39 | onException: function(r,x) { console.log(x);h() } 40 | ,onComplete: h 41 | ,onCreate: s 42 | }); 43 | 44 | /* init plugins */ 45 | if( FP ) 46 | for( var i = 0; i < FP.length; i++ ) 47 | new FP[i](this); 48 | 49 | /* extractURL: if user assigns user_id, repo, branch */ 50 | this.xU(); 51 | this.oR(); // open repo 52 | } 53 | 54 | 55 | ,xU: function() { 56 | 57 | } 58 | 59 | ,render: function() { 60 | $(document.body).insert(this.h()); 61 | this.psW = $('ps_w'); 62 | this.bW = $('b_w'); 63 | 64 | $('repo_form').observe('submit', function(e) { 65 | this.browse(); 66 | e.stop(); 67 | return false; 68 | }.bind(this)); 69 | // debugger 70 | } 71 | 72 | ,h: function() { 73 | return [ 74 | '
', 75 | '
', 76 | '
', 77 | '
', 78 | '
', 79 | 'Github Finder', 80 | '', 81 | '
', 82 | 'Repo: http://github.com/ ', 83 | 'Branch: ', // branches 84 | '', 85 | '
', 86 | '
', 87 | '', 88 | '
', 89 | '
', // .p 90 | '
', // #r_w 91 | 92 | '
', // #repo info wrapper 93 | '
', 94 | '
', 95 | 96 | '
', // browser wrapper 97 | '
', 98 | '
', 99 | 100 | '
', // info_wrapper 101 | '
Info
', 102 | '
Select an item or navigate with arrow keys
', 103 | '
', 104 | 105 | // '
', 106 | '
', // #finder 107 | 108 | '', // #f_c_w 134 | 135 | '' // # content 141 | ].join(' '); 142 | } 143 | 144 | /* openRepo */ 145 | ,oR: function(repo) { 146 | this.reset() 147 | 148 | var u,r,b; 149 | if( !repo ) { 150 | /* check URL params */ 151 | var p = uP(); 152 | if( p["user_id"] && p["repo"] ) { 153 | u = this.u = p["user_id"]; 154 | r = this.r = p["repo"] 155 | b = this.b = p["branch"] || 'master'; 156 | } else { 157 | // debugger 158 | /* if user just come from a github repo ... */ 159 | var m = (new RegExp("^https?://github.com/(.+)","i")).exec(document.referrer), 160 | path = m ? m[1].split('/') : []; 161 | 162 | if( path[0] && path[1] ) { 163 | u = this.u = path[0]; 164 | r = this.r = path[1]; 165 | b = this.b = path[3] || 'master'; 166 | } else { /* default to app settings */ 167 | u = this.u; 168 | r = this.r; 169 | b = this.b; 170 | } 171 | } 172 | } else { 173 | /* User hits the "Go" button: grabbing the user/repo */ 174 | repo = repo.split('/'); 175 | if( repo.length < 2 ) { alert('invalid repository!'); return; } 176 | 177 | u = this.u = repo[0]; 178 | r = this.r = repo[1]; 179 | b = this.b = ($('brs') ? $F('brs') : b) || 'master'; 180 | } 181 | 182 | 183 | $('r').value = u + '/' + r; 184 | 185 | /* Load the master branch */ 186 | GH.Commits.listBranch( u, r, b, { 187 | onData: function(cs) { 188 | // if(!cs.commits) { alert('repo not found'); return; } 189 | var tree_sha = cs.commits[0].tree; 190 | this.renderPanel(tree_sha); 191 | }.bind(this) 192 | }); 193 | 194 | /* Show the repo info */ 195 | GH.Repo.show( u, r, { 196 | onData: function(repo) { 197 | this.repo = repo; 198 | this.renderRepoInfo(); 199 | }.bind(this) 200 | }); 201 | 202 | /* Show branches info */ 203 | GH.Repo.listBranches( u, r, { 204 | onData: function(bes) { 205 | this.bes = $H(bes); 206 | this.rBs(); 207 | }.bind(this) 208 | }); 209 | 210 | } 211 | 212 | ,reset: function() { 213 | $('f_c_w').hide(); 214 | this.cI = -1; 215 | this.pI = 0; 216 | 217 | while(this.ps.length > 0) 218 | (this.ps.pop()).dispose(); 219 | } 220 | 221 | ,browse: function() { 222 | if( _gaq ) _gaq.push(["_trackEvent", "browse repo", "go", $F('r') ] ); 223 | 224 | $('i').innerHTML = ''; 225 | this.oR( $F('r') || $('r').readAttribute('placeholder') ); 226 | 227 | return false; 228 | } 229 | /* render the status bar */ 230 | ,renderRepoInfo: function() { 231 | $('r_i').innerHTML = this.repo.description; 232 | } 233 | 234 | /* render branches */ 235 | ,rBs: function() { 236 | var h = ''); 244 | $('brs_w').innerHTML = h + ''; 245 | } 246 | 247 | ,renderPanel: function( sh, ix, it ) { 248 | ix = ix || 0; 249 | /* clear previously opened panels */ 250 | for( var i = this.ps.length - 1; i > ix; i-- ) { 251 | (this.ps.pop()).dispose(); 252 | } 253 | this.open( sh, it ); 254 | } 255 | 256 | ,_resizePanelsWrapper: function() { 257 | var w = (this.ps.length * 201); 258 | this.psW.style.width = w + 'px'; 259 | 260 | /* scroll to the last panel */ 261 | this.bW.scrollLeft = w; 262 | } 263 | 264 | /* request the content of the tree and render the panel */ 265 | ,open: function( tree_sha, item ) { 266 | GH.Tree.show( this.u, this.r, this.b, tree_sha, { 267 | onData: function(tree) { // tree is already sorted 268 | /* add all items to cache */ 269 | for( var i = 0, len = tree.length; i < len; i++ ) 270 | this.shas[ tree[i].sha ] = tree[i]; 271 | 272 | var name = item ? item.name : '' ; 273 | // debugger 274 | var p = new P( this, { tree: tree, index: this.ps.length, name: name, tree_sha: tree_sha, item: item } ); 275 | this.ps.push( p ); 276 | 277 | this._resizePanelsWrapper(); 278 | 279 | }.bind(this) 280 | }); 281 | } 282 | 283 | /** 284 | * @sha: the sha of the object 285 | * @e: the source element 286 | * @kb: is this trigged by the keyboard 287 | */ 288 | ,click: function(sha, e, kb) { 289 | // console.log("kb" + kb); 290 | // debugger 291 | var it = this.shas[ sha ], 292 | ix = +(e.up('.panel')).readAttribute('data-index'), 293 | path = ""; 294 | 295 | 296 | /* set selection cursor && focus the item */ 297 | e.up('ul').select('li.cur').invoke('removeClassName','cur'); 298 | var p = e.up('div.panel'), 299 | li = e.up('li').addClassName('cur'), 300 | posTop = li.positionedOffset().top + li.offsetHeight - p.offsetHeight; 301 | if( posTop > p.scrollTop) { 302 | p.scrollTop = posTop ; 303 | } 304 | 305 | /* current index */ 306 | this.cI = it.index; 307 | this.pI = ix; // current panel index; 308 | 309 | /* remember the current selected item */ 310 | this.ps[ ix ].cI = it.index; 311 | 312 | 313 | /* don't be trigger happy: ptm = preview timer */ 314 | if(this._p) clearTimeout( this._p ); 315 | 316 | /* set a small delay here incase user switches really fast (e.g. keyboard navigation ) */ 317 | this._p = setTimeout( function(){ 318 | 319 | if( it.type == 'tree' ) { 320 | this.renderPanel( it.sha, ix, it ); 321 | // don't show file preview panel 322 | $('f_c_w').hide(); 323 | } else { 324 | 325 | $('f_c_w').show(); 326 | // console.log("it %o", JSON.stringify(it)); 327 | 328 | if( /text|javascript|application\/x-/.test(it.mime_type) ) { 329 | $('f').innerHTML = '
Loading File

'; 330 | GH.Blob.show( this.u, this.r, it.sha, { onSuccess: function(r) { 331 | this.previewTextFile(r.responseText, it); 332 | }.bind(this)} ); 333 | } 334 | } 335 | 336 | 337 | /* display file info */ 338 | for( var i = 0; i <= ix; i++ ) 339 | path += this.ps[i].name + "/"; 340 | path += it.name; 341 | 342 | 343 | var c, cs; 344 | var info = function() { 345 | var h = [ 346 | '
Name
', 347 | '', 350 | it.name, 351 | '', 352 | '
', 353 | '
', 354 | '
Path
' + path + '
', 355 | 356 | '
', 357 | '
Last Committed
' + 358 | s( c.id ) + 359 | ( Prototype.Browser.IE ? '' : ' on ' + (new Date(c.committed_date)).toString() ) + 360 | '
', 361 | '
', 362 | 363 | '
', 364 | 'Author
', 365 | '' + c.author.name + '', 366 | ' (' + c.author.email +')', 367 | '
', 368 | '
', 369 | '
', 370 | 'Commit Message
', 371 | c.message, 372 | '
' 373 | ]; 374 | $('i').update( h.join('')); 375 | }.bind(this); 376 | 377 | /* showPreview */ 378 | var p = function() { 379 | $('diffoutput').hide(); 380 | $('f_c_w').show(); 381 | $('f_h').innerHTML = path; 382 | } 383 | 384 | /* commits log */ 385 | var cl = function() { 386 | var dW, dH, dP, 387 | csHTML = '
'; 388 | // debugger 389 | var dl = function(a,b,l) { // difflink a, b, label 390 | // google event tracking to track the diff usage 391 | var eventTracker = '_gaq.push(["_trackEvent", "commits log", "diff", "' + l + '"]);'; 392 | return '' + l + ' '; 395 | }; 396 | 397 | /* to get an object at a commit: need the commit id, then path 398 | then it's just 399 | http://github.com/:user_id/:repo/blob/:commit_id/:path 400 | */ 401 | 402 | for( var i = 0; i < cs.length; i++ ) { 403 | dW = cs.length > 1 ? '
Diff with: ' : ''; // diffWith 404 | dH = i > 0 ? dl( c , cs[i], 'Head') : '' ; // diffHead 405 | dP = cs[i+1] ? dl(cs[i], cs[i+1], 'Previous') : '' ; // diffPrevious 406 | 407 | csHTML += 408 | '
' + 409 | '' + s(cs[i].id) +'' + ' by ' + cs[i].author.name + 410 | dW + dH + (dH && dP ? ' - ' : '') + dP + 411 | '
'; 412 | }; 413 | // csHTML.push('
'); 414 | $('c_l_w').update( csHTML + '
' ); 415 | }; 416 | 417 | /* query the cs to get a list of cs and info */ 418 | GH.Commits.list( this.u, this.r, this.b, path, { onData: function(cms) { // cms == commits 419 | it.c = c = cms[0]; // also assign the it's c to keep track of folder's latest commit 420 | cs = cms; 421 | 422 | info(); 423 | 424 | if( it.type != 'tree' ) { 425 | p(); 426 | cl(); 427 | } 428 | 429 | }.bind(this)}); 430 | }.bind(this), (kb ? 350 : 10)); // time out 431 | 432 | 433 | } 434 | 435 | 436 | ,previewTextFile: function( text, it ) { 437 | text = text.replace(/\r\n/, "\n").split(/\n/); 438 | 439 | var ln = [], 440 | l = [], 441 | sloc = 0; 442 | for( var i = 0, len = text.length; i < len; i++ ) { 443 | ln.push( '' + (i + 1) + "\n"); 444 | 445 | l.push( text[i] ? text[i].replace(/&/g, '&').replace(/', 452 | '' + it.mode + '', 453 | '' + text.length + ' lines (' + sloc +' sloc)', 454 | '' + it.size + ' bytes', 455 | 'Theme: ', 459 | '
', 460 | 461 | '
', // file content scroll 462 | '', 463 | '', 464 | '', 469 | 470 | '', 475 | '', 476 | '' 477 | ]; 478 | 479 | $('diffoutput').hide(); 480 | $('f').update( html.join('') ).show(); 481 | 482 | /* HACK!! */ 483 | $('theme').observe('change', function() { 484 | window.f.theme = $F('theme'); 485 | $('code').removeClassName('Light').removeClassName('Dark').addClassName(window.f.theme); 486 | }); 487 | } 488 | 489 | ,diff:function(){ alert('Diff is disabled.'); } 490 | }); -------------------------------------------------------------------------------- /public/javascripts/gh.js: -------------------------------------------------------------------------------- 1 | window.GH = { 2 | hash: {} 3 | // ,proxy: 'http://alexle.net/experiments/githubfinder/proxy.php?url=' 4 | ,proxy: './proxy.php?url=' 5 | // ,proxy: '' 6 | ,api: 'https://github.com/api/v2/json' 7 | 8 | /* set the proxy.php url and switch to the correct AR (AjaxRequest) */ 9 | ,setProxy: function(p) { 10 | this.proxy = p; 11 | // window.AR = p.indexOf('./') == 0 ? Ajax.Request : JSP; 12 | window.AR = JSP; 13 | } 14 | 15 | ,Commits: { 16 | _cache: [] 17 | /* list all commits for a specific branch */ 18 | ,listBranch: function(u, r, b, o ) { 19 | var onData = o.onData, 20 | url = GH.api + '/commits/list/' + u + '/' + r + '/' + b; 21 | o.onSuccess = function(res) { 22 | onData( res.responseText ); 23 | } 24 | new JSP( url, o ); 25 | } 26 | 27 | ,list: function( u, r, b, path, o ) { 28 | var self = this, 29 | url = GH.api + '/commits/list/' + u + '/' + r + '/' + b + path, 30 | onData = o.onData; 31 | 32 | o.onSuccess = function(res) { 33 | var cs = res.responseText.commits; 34 | // if(!cs) { alert('not found'); return;} 35 | /* cache the commits */ 36 | self._cache[ url ] = cs; 37 | onData( cs ); 38 | } 39 | 40 | /* hit the cache first */ 41 | if( this._cache[ url ] ) { 42 | onData( this._cache[ url ] ); 43 | return; 44 | } 45 | 46 | new JSP( url, o ); 47 | } 48 | 49 | ,show: function( u, r, sha, o ) { 50 | var self = this, 51 | url = GH.api + '/commits/show/' + u + '/' + r + '/' + sha, 52 | onData = o.onData; 53 | 54 | o.onSuccess = function(res) { 55 | var c = res.responseText.commit; 56 | /* cache */ 57 | self._cache[ sha ] = c; 58 | onData( c ); 59 | } 60 | 61 | /* hit the cache first */ 62 | if( this._cache[ sha ] ) { 63 | onData( this._cache[ sha ] ); 64 | return; 65 | } 66 | 67 | new JSP( url, o ); 68 | } 69 | } 70 | 71 | ,Tree: { 72 | _cache: {} 73 | ,show: function( u, r, b, tree_sha, o ) { 74 | var self = this, 75 | url = GH.api + '/tree/show/' + u +'/' + r +'/' + tree_sha, 76 | onData = o.onData; 77 | 78 | o.onSuccess = function(res) { 79 | var tree = res.responseText.tree; 80 | // if(!tree) { alert('not found'); return;} 81 | tree = tree.sort(function(a,b){ 82 | // blobs always lose to tree 83 | if( a.type == 'blob' && b.type == 'tree' ) 84 | return 1; 85 | if( a.type == 'tree' && b.type == 'blob' ) 86 | return -1; 87 | return a.name > b.name ? 1 : ( a.name < b.name ? - 1 : 0 ); 88 | }); 89 | 90 | /* add the index to the item */ 91 | for( var i = 0, len = tree.length; i < len; i++ ) { 92 | tree[i].index = i; 93 | } 94 | 95 | /* cache the tree so that we don't have to re-request every time */ 96 | self._cache[ tree_sha ] = tree; 97 | 98 | onData(tree); 99 | } 100 | 101 | 102 | /* hit the cache first */ 103 | if( this._cache[ tree_sha ] ) { 104 | onData( this._cache[ tree_sha ] ); 105 | return; 106 | } 107 | 108 | new JSP( url, o); 109 | } 110 | } 111 | 112 | ,Blob: { 113 | show: function( u, r, sha, o ) { 114 | var url = GH.api + '/blob/show/' + u + '/' + r + '/' + sha; 115 | new AR( GH.proxy + url, o ); 116 | } 117 | 118 | /** 119 | * u,r,b: user, repo, branch 120 | * fn: filename 121 | * o: the options, with callback 122 | */ 123 | ,loadPage: function(u,r,b,fn, o) { 124 | var url = 'http://github.com/' + u + '/' + r + '/blob/' + b +'/' + fn; 125 | new AR( GH.proxy + url, o ); 126 | } 127 | } 128 | 129 | ,Repo: { 130 | show: function( u, r, o ) { 131 | var url = GH.api + '/repos/show/' + u + '/' + r, 132 | onData = o.onData; 133 | 134 | o.onSuccess = function(res) { 135 | onData(res.responseText.repository); 136 | } 137 | new JSP( url, o ); 138 | } 139 | 140 | ,listBranches: function( u, r, o ) { 141 | var url = GH.api + '/repos/show/' + u + '/' + r + '/branches', 142 | onData = o.onData; 143 | o.onSuccess = function(res) { 144 | var branches = res.responseText.branches; 145 | onData(branches); 146 | } 147 | new JSP( url, o ); 148 | } 149 | } 150 | 151 | ,Raw: { 152 | loadBlobAtCommit: function( u, r, commitId, path, options ) { 153 | //http://github.com/:user_id/:repo/raw/:commit_id/:path 154 | // http://github.com/mojombo/grit/raw/c0f0b4f7a62d2e563b48d0dc5cd9eb3c21e3b4c2/lib/grit.rb 155 | // https://raw.github.com/mojombo/grit/c0f0b4f7a62d2e563b48d0dc5cd9eb3c21e3b4c2/lib/grit.rb 156 | url = 'https://raw.github.com/' + u + '/' + r + '/raw/' + commitId + path; 157 | new AR( GH.proxy + url, options ); 158 | } 159 | } 160 | }; -------------------------------------------------------------------------------- /public/javascripts/keyboard.js: -------------------------------------------------------------------------------- 1 | var Keyboard = Class.create( PluginBase, { 2 | initialize: function($super, f) { 3 | // $super(f); 4 | 5 | document.observe('keydown', function(e) { 6 | if(e.findElement().tagName == 'INPUT') return; // user has focus in something, bail out. 7 | 8 | // var k = e.which ? e.which : e.keyCode; // keycode 9 | var k = e.which || e.keyCode; // keycode 10 | 11 | var cI = f.cI, 12 | pI = f.pI; 13 | 14 | var p = f.ps[pI]; // panel 15 | var t = p.tree; // the panel's tree 16 | 17 | 18 | var d = function() { 19 | if( t[ ++cI ] ) { 20 | var item = t[cI]; 21 | // debugger 22 | f.click( item.sha, $$('#p' + pI + ' a')[cI], true ); 23 | } else { 24 | cI--; 25 | } 26 | }; 27 | 28 | var u = function() { 29 | if( t[ --cI ] ) { 30 | var item = t[cI]; 31 | f.click( item.sha, $$('#p' + pI + ' a')[cI], true ); 32 | } else { 33 | cI++; 34 | } 35 | } 36 | 37 | var l = function() { 38 | if( f.ps[--pI] ) { 39 | // debugger 40 | t = f.ps[pI].tree; 41 | // get index of the previously selected item 42 | cI = f.ps[pI].cI; 43 | // var item = f.ps[pI]; 44 | f.click( t[cI].sha, $$('#p' + pI + ' a')[cI], true ); 45 | 46 | } else { 47 | pI++; // undo 48 | } 49 | } 50 | 51 | 52 | var r = function() { 53 | if( !t[cI] || t[cI].type != 'tree' ) return; 54 | 55 | if( f.ps[++pI] ) { 56 | t = f.ps[pI].tree; 57 | cI = -1; 58 | d(); // down! 59 | 60 | } else { 61 | pI--; // undo 62 | } 63 | } 64 | 65 | // k == 40 ? d() : ( k == 39 ? r() : ( k == 38 ? u() : ( k == 37 ? l() : ''; 66 | switch( k ) { 67 | case 40: // key down 68 | d(); 69 | break; 70 | 71 | case 38: // up 72 | u(); 73 | break; 74 | 75 | case 37: //left 76 | l(); 77 | break 78 | 79 | case 39: // right 80 | r(); 81 | break; 82 | default: 83 | break; 84 | } 85 | 86 | 87 | // console.log("keypress"); 88 | 89 | if( k >= 37 && k <= 40) 90 | e.stop(); 91 | 92 | }); 93 | } 94 | }); 95 | 96 | /* add the plugin to the plugins list */ 97 | FP.push(Keyboard); -------------------------------------------------------------------------------- /public/javascripts/p.js: -------------------------------------------------------------------------------- 1 | /* Panel */ 2 | window.P = Class.create({ 3 | initialize: function(f, options) { 4 | this.f = f; 5 | this.tree = options.tree || []; 6 | this.index = options.index || 0 ; 7 | this.name = options.name; 8 | this.item = options.item; 9 | 10 | this.r(); 11 | } 12 | 13 | ,dispose: function() { 14 | $('p' + this.index ).remove(); 15 | this.p = null; 16 | } 17 | 18 | ,r: function() { 19 | this.f.psW.insert({ bottom: this.h() }); 20 | } 21 | 22 | ,h: function() { 23 | var it, css, recent, ix=this.index, t=this.tree,bH = this.f.bW.offsetHeight, 24 | h = '
    '; 25 | 26 | for( var i = 0; i < t.length; i++ ) { 27 | it = t[i]; 28 | 29 | h += '
  • ' + 30 | '' + 31 | '' + 32 | it.name + 33 | '' + 34 | ''+ 35 | '
  • '; 36 | } 37 | h += '
'; 38 | return '
' + h + '
'; 39 | } 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /public/javascripts/plugins/base.js: -------------------------------------------------------------------------------- 1 | ;window.FP = []; // this array contains the list of all registered plugins 2 | 3 | var PluginBase = Class.create( { 4 | initialize: function(o) { 5 | if( !this.mixin ) this.mixin = {}; 6 | Object.extend( o, this.mixin ); 7 | } 8 | } ); -------------------------------------------------------------------------------- /public/javascripts/plugins/bookmarklet.js: -------------------------------------------------------------------------------- 1 | /* Book marklet */ 2 | var BML = Class.create( PluginBase, { 3 | initialize: function(f) { 4 | var e = $('url_w'), 5 | h = 6 | '
Drag Open in GHFinder to your bookmark bar
(To open any Github repo in GHFinder)' + 9 | '
' 10 | // ]; 11 | e.insert(h); 12 | } 13 | }); 14 | 15 | FP.push(BML); 16 | 17 | // Original function 18 | // YUI Online compressor: http://refresh-sf.com/yui/#output 19 | // 20 | // (function(){ 21 | // var m = (new RegExp("github.com\/(.+)","i")).exec(window.location.href); 22 | // var path = m ? m[1].split('/') : []; 23 | // var user = path[0]; 24 | // var repo = path[1]; 25 | // var branch = path[3]; 26 | // var url = 'http://sr3d.github.com/GithubFinder/?utm_source=bml' + (m ? '&user_id=' + user + '&repo=' + repo + (branch ? '&branch=' + branch : '' ) : ''); 27 | // 28 | // if(!url) { 29 | // alert('Invalid Github URL'); 30 | // return; 31 | // } 32 | // 33 | // window.open(url); 34 | // })() -------------------------------------------------------------------------------- /public/javascripts/plugins/code_highlighter.js: -------------------------------------------------------------------------------- 1 | /* code highliter */ 2 | var CH = Class.create( PluginBase, { 3 | initialize: function($super, f) { 4 | $super(f); 5 | 6 | // f.theme = 'Light'; 7 | f.theme = 'Dark'; 8 | 9 | var hlt = CodeHighlighter; 10 | 11 | var getFiletype = function(filename,text) { 12 | var fileType, 13 | matchingRules = { 14 | 'ruby': [ /\.rb$/i, /\.ru$/i, /\bRakefile\b/i, /\bGemfile\b/i, /\.gemspec\b/i, /\bconsole\b/i, /\.rake$/i ] 15 | ,'css': [ /\.css/i ] 16 | ,'html': [ /\.html?$/i, /\.aspx$/i, /\.php$/i, /\.erb$/i ] 17 | ,'javascript': [ /\.js$/i ] 18 | ,'python': [ /\.py$/i ] 19 | ,'applescript': [ /\.applescript$/i ] 20 | ,'yaml': [ /\.yml$/i ] 21 | ,'cpp': [ /\.c$/i, /\.cpp$/i, /\.h$/i ] 22 | ,'clojure': [ /\.clj$/i ] 23 | ,'haskell': [ /\.hs$/i ] 24 | }; 25 | 26 | 27 | $H(matchingRules).each(function(type) { 28 | for( var i = 0; i < type.value.length; i++ ) { 29 | if( type.value[i].match(filename) ) { 30 | fileType = type.key; 31 | return; 32 | } 33 | } 34 | } ); 35 | 36 | // debugger 37 | 38 | /* attempt to futher detect the fileType */ 39 | if( !fileType ) { 40 | text = text.replace(/\r\n/, "\n").split(/\n/)[0]; 41 | fileType = /ruby/i.test(text) ? 'ruby' : 42 | /python/i.test(text) ? 'python' : 43 | /php/i.test(text) ? 'php' : 44 | ''; 45 | } 46 | 47 | return fileType; 48 | } 49 | 50 | var old = f.previewTextFile; 51 | f.previewTextFile = function( text, item ) { 52 | old(text,item); 53 | var codeEl = $('code'); 54 | codeEl.className = f.theme; // clear previous syntax class 55 | codeEl.addClassName(getFiletype(item.name,text)); 56 | 57 | hlt.init(); 58 | } 59 | 60 | } 61 | 62 | /* add the link to the stylesheet */ 63 | // ,addStylesheet: function() { 64 | // // 65 | // var css = document.createElement('link'); 66 | // css.href = 'css/code_highlighter.css'; 67 | // css.rel = 'stylesheet'; 68 | // css.type = 'text/css'; 69 | // document.body.appendChild(css); 70 | // } 71 | 72 | }); 73 | 74 | FP.push(CH); -------------------------------------------------------------------------------- /public/javascripts/plugins/github_readme.js: -------------------------------------------------------------------------------- 1 | var Readme = Class.create( PluginBase, { 2 | initialize: function($super, f) { 3 | $super(f); 4 | 5 | this.f = f; 6 | this.readme = null; 7 | // this.addStylesheet(); 8 | 9 | var self = this, 10 | old = f._resizePanelsWrapper.bind(f); 11 | 12 | f._resizePanelsWrapper = function() { 13 | old(); 14 | 15 | if( self.readme != null ) {return; } // don't run it again 16 | 17 | self.readme = false; 18 | var tree = this.ps[0].tree, 19 | readmeRegex = /\breadme.md|readme.markdown|readme.textile\b/i; 20 | 21 | for( var i = 0; i < tree.length; i++ ) { 22 | if( readmeRegex.test(tree[i].name) ) { 23 | self.readme = tree[i].name; 24 | break; 25 | } 26 | } 27 | 28 | if( !self.readme ) return; 29 | 30 | self.loadReadme(); 31 | }; 32 | 33 | 34 | var oB = f.browse.bind(f); 35 | f.browse = function() { 36 | /* reset the readme stuff */ 37 | self.readme = null; 38 | if($('readme_wrapper')) $('readme_wrapper').remove(); 39 | 40 | oB(); 41 | } 42 | } 43 | 44 | ,loadReadme: function() { 45 | $('f_c_w').insert({ after:'

README


'}); 46 | GH.Blob.loadPage( this.f.u, this.f.r, this.f.b, this.readme, { 47 | onSuccess: function(response) { 48 | var html = response.responseText, 49 | div = document.createElement('div'); 50 | div.innerHTML = html.replace(//g, ''); 51 | $('readme').update( $(div).down('div#readme').innerHTML ); 52 | 53 | div = null; 54 | } 55 | }); 56 | } 57 | 58 | /* add the link to the stylesheet */ 59 | // ,addStylesheet: function() { 60 | // var css = document.createElement('link'); 61 | // css.href = 'github_readme.css'; 62 | // css.rel = 'stylesheet'; 63 | // css.type = 'text/css'; 64 | // document.body.appendChild(css); 65 | // } 66 | }); 67 | 68 | // if( !Prototype.Browser.IE ) 69 | window.FP.push(Readme); -------------------------------------------------------------------------------- /public/javascripts/plugins/permalink.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Plugin to let the page bookmarkable 3 | */ 4 | var Permalink = Class.create( PluginBase, { 5 | initialize: function($super, f) { 6 | $super(f); 7 | this.f = f; 8 | 9 | var self = this, 10 | old = f._resizePanelsWrapper.bind(f); 11 | 12 | // override previewTextfile 13 | 14 | } 15 | }); 16 | 17 | // if( !Prototype.Browser.IE ) 18 | window.FP.push(Permalink); -------------------------------------------------------------------------------- /public/javascripts/plugins/prefetch_tree.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sr3d/GithubFinder/df54b3f3a6d4bb56be04da9987911083580a67b0/public/javascripts/plugins/prefetch_tree.js -------------------------------------------------------------------------------- /public/javascripts/plugins/resizeable_panel.js: -------------------------------------------------------------------------------- 1 | var ResizablePanel = Class.create( PluginBase, { 2 | initialize: function($super, f) { 3 | $super(f); 4 | this.extend(); 5 | 6 | this.f = f; 7 | this.widths = []; // to keep track of the width of the panels 8 | 9 | var self = this; 10 | var oldResizePanel = f._resizePanelsWrapper.bind(f); 11 | f._resizePanelsWrapper = function() { 12 | // oldResizePanel(); 13 | var defaultWidth = 201; 14 | var totalWidth = 0; 15 | 16 | /* have to take into account the width of the resized panels */ 17 | for( var i = 0; i < this.ps.length; i++ ) { 18 | totalWidth += self.widths[i] ? self.widths[i] : defaultWidth; 19 | } 20 | 21 | /* adding in the width or resizer*/ 22 | totalWidth += this.ps.length * (self.resizeWidth+1); 23 | 24 | this.psW.style.width = totalWidth + 'px'; 25 | 26 | /* scroll to the last panel */ 27 | this.bW.scrollLeft = totalWidth; 28 | 29 | /* adjusting the height of the finder panels based on the scrollbar */ 30 | var scrollbarSize = self.scrollbarSize(); 31 | if( totalWidth > this.bW.offsetWidth ) { 32 | var newHeight = this.psW.style.height = (this.bW.offsetHeight - scrollbarSize) + 'px'; 33 | this.psW.select('div').each(function(div) { 34 | div.style.height = newHeight; 35 | } ); 36 | } else { 37 | if( this.psW.style.height != this.bW.style.height ) { 38 | var newHeight = this.psW.style.height = this.bW.style.height; 39 | this.psW.select('div'). 40 | invoke('setStyle',{height: newHeight} ); 41 | } 42 | } 43 | }.bind(f); 44 | 45 | /* insert vertical resizable */ 46 | $('finder').removeClassName('tbb').insert({ 47 | after: '
'}); 48 | } 49 | 50 | ,extend: function() { 51 | var self = this; 52 | /* extend the Panel class */ 53 | window.P = Class.create( window.P, { 54 | r: function( $super ) { 55 | $super(); 56 | 57 | var p = this.p = $('p' + this.index); 58 | 59 | /* set the width of the panel to the previously set width (if needed) */ 60 | if( self.widths[ this.index ] ) 61 | p.style.width = self.widths[ this.index ] + 'px'; 62 | 63 | /* draw the resizer bar */ 64 | var height = $('b_w').offsetHeight + 'px', 65 | style = 'height:' + height + ';width:2px', 66 | html = '
', 67 | psW = $('ps_w'); 68 | 69 | psW.insert(html); 70 | self.resizeWidth = $('resize' + this.index).offsetWidth; 71 | 72 | this.updateFileLabel(); 73 | } 74 | 75 | ,dispose: function($super) { 76 | $super(); 77 | $('resize' + this.index ).remove(); 78 | } 79 | 80 | ,updateFileLabel: function() { 81 | var l = this.p.offsetWidth / 8; 82 | this.p.select('a').each(function(a) { 83 | a.innerHTML = t(a.readAttribute('data-name'), l); 84 | }); 85 | } 86 | } ); 87 | 88 | /* handle resizing drag */ 89 | document.observe('mousedown', function(event) { 90 | var e = event.findElement(); 91 | 92 | if( !e.hasClassName('resize') ) return; 93 | 94 | this.element = e; 95 | this.start = [event.clientX, event.clientY]; 96 | 97 | /* make sure the cursor stays as col-resize */ 98 | document.body.style.cursor = e.hasClassName('hrz') ? 'row-resize' : 'col-resize'; 99 | event.stop(); 100 | 101 | }.bind(this)); 102 | 103 | 104 | document.observe('mouseup', function(event) { 105 | /* not dragging, bail out */ 106 | if( !this.element ) return; 107 | 108 | /* user drags, resizing ... */ 109 | if( this.element.hasClassName('hrz') ) { // horizontal split 110 | var b = $('b_w'), 111 | i = $('i_w'), 112 | end = event.clientY, 113 | min = 300, 114 | newHeight = b.offsetHeight + (end - this.start[1]); 115 | 116 | newHeight = newHeight < min ? min : newHeight; 117 | i.style.height = b.style.height = newHeight + 'px'; 118 | this.f._resizePanelsWrapper(); 119 | 120 | } else { // vertical split 121 | var end = event.clientX, 122 | pI = +this.element.readAttribute('data-index'), 123 | p = $('p' + pI ), 124 | min = 200, 125 | newWidth = parseInt(p.offsetWidth) + (end - this.start[0]); 126 | 127 | newWidth = newWidth < min ? min : newWidth; 128 | p.style.width = newWidth + 'px'; 129 | 130 | /* store width so we can remember */ 131 | this.widths[ pI ] = newWidth; 132 | 133 | this.f.ps[pI].updateFileLabel(); 134 | } 135 | 136 | /* reset the cursor */ 137 | document.body.style.cursor = ''; 138 | this.element = null; 139 | this.f._resizePanelsWrapper(); 140 | 141 | }.bind(this)); 142 | 143 | } 144 | 145 | /* dynamically calculate the scrollbar size of the */ 146 | ,scrollbarSize: function() { 147 | if( this.sbs) return this.sbs; 148 | 149 | var html = [ 150 | '
', 465 | '
',
466 |                 ln.join(''),
467 |               '
', 468 | '
', 471 | '
',
472 |                 l.join("\n"),
473 |               '
', 474 | '