Added submodule support
using an ini parser for .gitmodules
This commit is contained in:
parent
d158f10911
commit
f64281175e
|
@ -31,6 +31,7 @@
|
||||||
<string>lib/js/underscore.js</string>
|
<string>lib/js/underscore.js</string>
|
||||||
<string>lib/js/base64.js</string>
|
<string>lib/js/base64.js</string>
|
||||||
<string>lib/js/github.js</string>
|
<string>lib/js/github.js</string>
|
||||||
|
<string>lib/js/iniparser.js</string>
|
||||||
<string>inject.js</string>
|
<string>inject.js</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -10,6 +10,7 @@ pageMod.PageMod({
|
||||||
data.url('lib/js/underscore.js'),
|
data.url('lib/js/underscore.js'),
|
||||||
data.url('lib/js/base64.js'),
|
data.url('lib/js/base64.js'),
|
||||||
data.url('lib/js/github.js'),
|
data.url('lib/js/github.js'),
|
||||||
|
data.url('lib/js/iniparser.js'),
|
||||||
data.url('inject.js')],
|
data.url('inject.js')],
|
||||||
contentStyleFile: [data.url('lib/css/jstree.css'),
|
contentStyleFile: [data.url('lib/css/jstree.css'),
|
||||||
data.url('inject.css')],
|
data.url('inject.css')],
|
||||||
|
|
|
@ -90,7 +90,8 @@ html.octotree {
|
||||||
}
|
}
|
||||||
|
|
||||||
.octotree_treeview .jstree-icon.tree,
|
.octotree_treeview .jstree-icon.tree,
|
||||||
.octotree_treeview .jstree-icon.blob {
|
.octotree_treeview .jstree-icon.blob,
|
||||||
|
.octotree_treeview .jstree-icon.commit {
|
||||||
font: normal normal 16px octicons;
|
font: normal normal 16px octicons;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
@ -106,6 +107,10 @@ html.octotree {
|
||||||
content: '\f011';
|
content: '\f011';
|
||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
|
.octotree_treeview .jstree-icon.commit:before {
|
||||||
|
content: '\f017';
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
.octotree_treeview .jstree-anchor {
|
.octotree_treeview .jstree-anchor {
|
||||||
color: #4183c4 !important;
|
color: #4183c4 !important;
|
||||||
|
@ -142,17 +147,17 @@ html.octotree {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Options form */
|
/* Options form */
|
||||||
.octotree_options div {
|
.octotree_options div {
|
||||||
margin: 6px;
|
margin: 6px;
|
||||||
}
|
}
|
||||||
.octotree_options input {
|
.octotree_options input {
|
||||||
width: 237px;
|
width: 237px;
|
||||||
}
|
}
|
||||||
.octotree_options .error {
|
.octotree_options .error {
|
||||||
color: #900;
|
color: #900;
|
||||||
}
|
}
|
||||||
.octotree_options button {
|
.octotree_options button {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toggle button */
|
/* Toggle button */
|
||||||
|
|
119
src/inject.js
119
src/inject.js
|
@ -3,8 +3,8 @@
|
||||||
, TOKEN = 'octotree.github_access_token'
|
, TOKEN = 'octotree.github_access_token'
|
||||||
, SHOWN = 'octotree.shown'
|
, SHOWN = 'octotree.shown'
|
||||||
, RESERVED_USER_NAMES = [
|
, RESERVED_USER_NAMES = [
|
||||||
'settings', 'orgs', 'organizations',
|
'settings', 'orgs', 'organizations',
|
||||||
'site', 'blog', 'about',
|
'site', 'blog', 'about',
|
||||||
'styleguide', 'showcases', 'trending',
|
'styleguide', 'showcases', 'trending',
|
||||||
'stars', 'dashboard', 'notifications'
|
'stars', 'dashboard', 'notifications'
|
||||||
]
|
]
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
// (username)/(reponame)[/(subpart)]
|
// (username)/(reponame)[/(subpart)]
|
||||||
var match = location.pathname.match(/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/)
|
var match = location.pathname.match(/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/)
|
||||||
if (!match) return false
|
if (!match) return false
|
||||||
|
|
||||||
// Not a repository, skip
|
// Not a repository, skip
|
||||||
if (~RESERVED_USER_NAMES.indexOf(match[1])) return false
|
if (~RESERVED_USER_NAMES.indexOf(match[1])) return false
|
||||||
if (~RESERVED_REPO_NAMES.indexOf(match[2])) return false
|
if (~RESERVED_REPO_NAMES.indexOf(match[2])) return false
|
||||||
|
@ -104,11 +104,11 @@
|
||||||
// Not a code page, skip
|
// Not a code page, skip
|
||||||
if (match[3] && !~['tree', 'blob'].indexOf(match[3])) return false
|
if (match[3] && !~['tree', 'blob'].indexOf(match[3])) return false
|
||||||
|
|
||||||
var branch = $('*[data-master-branch]').data('ref') ||
|
var branch = $('*[data-master-branch]').data('ref') ||
|
||||||
$('*[data-master-branch] > .js-select-button').text() ||
|
$('*[data-master-branch] > .js-select-button').text() ||
|
||||||
'master'
|
'master'
|
||||||
return {
|
return {
|
||||||
username : match[1],
|
username : match[1],
|
||||||
reponame : match[2],
|
reponame : match[2],
|
||||||
branch : branch
|
branch : branch
|
||||||
}
|
}
|
||||||
|
@ -122,32 +122,45 @@
|
||||||
|
|
||||||
api.getTree(encodeURIComponent(repo.branch) + '?recursive=true', function(err, tree) {
|
api.getTree(encodeURIComponent(repo.branch) + '?recursive=true', function(err, tree) {
|
||||||
if (err) return done(err)
|
if (err) return done(err)
|
||||||
tree.forEach(function(item) {
|
fetchSubmoduleData(api, repo, tree, function(err, submods) {
|
||||||
var path = item.path
|
if (err) return done(err)
|
||||||
, type = item.type
|
tree.forEach(function(item) {
|
||||||
, index = path.lastIndexOf('/')
|
var path = item.path
|
||||||
, name = path.substring(index + 1)
|
, type = item.type
|
||||||
, folder = folders[path.substring(0, index)]
|
, index = path.lastIndexOf('/')
|
||||||
, url = '/' + repo.username + '/' + repo.reponame + '/' + type + '/' + repo.branch + '/' + path
|
, name = path.substring(index + 1)
|
||||||
|
, folder = folders[path.substring(0, index)]
|
||||||
|
, url = '/' + repo.username + '/' + repo.reponame + '/' + type + '/' + repo.branch + '/' + path
|
||||||
|
, modkey = ''
|
||||||
|
|
||||||
folder.push(item)
|
folder.push(item)
|
||||||
item.id = PREFIX + path
|
item.id = PREFIX + path
|
||||||
item.text = $dummyDiv.text(name).html() // sanitizes, closes #9
|
item.text = $dummyDiv.text(name).html() // sanitizes, closes #9
|
||||||
item.icon = type // use `type` as class name for tree node
|
item.icon = type // use `type` as class name for tree node
|
||||||
if (type === 'tree') {
|
if (type === 'tree') {
|
||||||
folders[item.path] = item.children = []
|
folders[item.path] = item.children = []
|
||||||
item.a_attr = { href: '#' }
|
item.a_attr = { href: '#' }
|
||||||
}
|
}
|
||||||
else if (type === 'blob') {
|
else if (type === 'blob') {
|
||||||
item.a_attr = { href: url }
|
item.a_attr = { href: url }
|
||||||
}
|
}
|
||||||
|
else if (type === 'commit') {
|
||||||
|
modkey = 'submodule "' + item.path + '"' //key from parsing gitmodules file
|
||||||
|
if(submods && submods[modkey]) {
|
||||||
|
submod = submods[modkey]
|
||||||
|
item.a_attr = { href: submod.url.replace("git@github.com:", "/") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
done(null, sort(root))
|
||||||
})
|
})
|
||||||
|
|
||||||
done(null, sort(root))
|
|
||||||
|
|
||||||
function sort(folder) {
|
function sort(folder) {
|
||||||
folder.sort(function(a, b) {
|
folder.sort(function(a, b) {
|
||||||
if (a.type === b.type) return a.text.localeCompare(b.text)
|
//github treats submodules like folders
|
||||||
|
var compare = ((a.type === 'tree' || a.type === 'commit') &&
|
||||||
|
(b.type === 'tree' || b.type === 'commit'))
|
||||||
|
|
||||||
|
if (a.type === b.type || compare) return a.text.localeCompare(b.text)
|
||||||
return a.type === 'tree' ? -1 : 1
|
return a.type === 'tree' ? -1 : 1
|
||||||
})
|
})
|
||||||
folder.forEach(function(item) {
|
folder.forEach(function(item) {
|
||||||
|
@ -158,6 +171,26 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchSubmoduleData(api, repo, tree, cb) {
|
||||||
|
var submodules = {}
|
||||||
|
, item = null
|
||||||
|
//use tree to find sha for .gitmodules
|
||||||
|
item = _.find(tree, function(file) { return /\.gitmodules/i.test(file.path) })
|
||||||
|
if(item) {
|
||||||
|
api.getBlob(item.sha, function (err, content, sha) {
|
||||||
|
if(err) cb(err, null)
|
||||||
|
if(content) {
|
||||||
|
submodules = iniParser.parse(content)
|
||||||
|
}
|
||||||
|
cb(null, submodules)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cb(null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onFetchError(err) {
|
function onFetchError(err) {
|
||||||
var header = 'Error: ' + err.error
|
var header = 'Error: ' + err.error
|
||||||
, hasToken = !!store.get(TOKEN)
|
, hasToken = !!store.get(TOKEN)
|
||||||
|
@ -176,20 +209,20 @@
|
||||||
break
|
break
|
||||||
case 404:
|
case 404:
|
||||||
header = 'Private repository!'
|
header = 'Private repository!'
|
||||||
message = hasToken
|
message = hasToken
|
||||||
? 'You are not allowed to access this repository.'
|
? 'You are not allowed to access this repository.'
|
||||||
: 'Accessing private repositories requires a GitHub access token. Follow <a href="https://github.com/settings/tokens/new" target="_blank">this link</a> to create one and paste it below.'
|
: 'Accessing private repositories requires a GitHub access token. Follow <a href="https://github.com/settings/tokens/new" target="_blank">this link</a> to create one and paste it below.'
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
if (~err.request.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
|
if (~err.request.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
|
||||||
header = 'API limit exceeded!'
|
header = 'API limit exceeded!'
|
||||||
message = hasToken
|
message = hasToken
|
||||||
? 'You have exceeded the API hourly limit.'
|
? 'You have exceeded the API hourly limit.'
|
||||||
: 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow <a href="https://github.com/settings/tokens/new" target="_blank">this link</a> to create one and paste it below.'
|
: 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow <a href="https://github.com/settings/tokens/new" target="_blank">this link</a> to create one and paste it below.'
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSidebar('<div class="octotree_header_error">' + header + '</div>', message)
|
renderSidebar('<div class="octotree_header_error">' + header + '</div>', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,19 +242,23 @@
|
||||||
.on('click', function(e) {
|
.on('click', function(e) {
|
||||||
var $target = $(e.target)
|
var $target = $(e.target)
|
||||||
if ($target.is('a.jstree-anchor') && $target.children(':first').hasClass('blob')) {
|
if ($target.is('a.jstree-anchor') && $target.children(':first').hasClass('blob')) {
|
||||||
$.pjax({
|
$.pjax({
|
||||||
url : $target.attr('href'),
|
url : $target.attr('href'),
|
||||||
timeout : 5000, //gives it more time, should really have a progress indicator...
|
timeout : 5000, //gives it more time, should really have a progress indicator...
|
||||||
container : $('#js-repo-pjax-container')
|
container : $('#js-repo-pjax-container')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
else if ($target.is('a.jstree-anchor') && $target.children(':first').hasClass('commit')) {
|
||||||
|
//link to submodule new page
|
||||||
|
window.location.href = $target.attr('href')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.on('ready.jstree', function() {
|
.on('ready.jstree', function() {
|
||||||
var headerText = '<div class="octotree_header_repo">' +
|
var headerText = '<div class="octotree_header_repo">' +
|
||||||
repo.username + ' / ' + repo.reponame +
|
repo.username + ' / ' + repo.reponame +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div class="octotree_header_branch">' +
|
'<div class="octotree_header_branch">' +
|
||||||
repo.branch +
|
repo.branch +
|
||||||
'</div>'
|
'</div>'
|
||||||
renderSidebar(headerText)
|
renderSidebar(headerText)
|
||||||
cb()
|
cb()
|
||||||
|
@ -256,7 +293,7 @@
|
||||||
if (shown) $html.removeClass(PREFIX)
|
if (shown) $html.removeClass(PREFIX)
|
||||||
else $html.addClass(PREFIX)
|
else $html.addClass(PREFIX)
|
||||||
store.set(SHOWN, !shown)
|
store.set(SHOWN, !shown)
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveToken(event) {
|
function saveToken(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
@ -283,4 +320,4 @@
|
||||||
return localStorage.setItem(key, JSON.stringify(val))
|
return localStorage.setItem(key, JSON.stringify(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// modified from https://github.com/shockie/node-iniparser/blob/master/lib/node-iniparser.js
|
||||||
|
// https://github.com/shockie/node-iniparser
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var iniParser = {}
|
||||||
|
iniParser.parse = function(data) {
|
||||||
|
var regex = {
|
||||||
|
section: /^\s*\[\s*([^\]]*)\s*\]\s*$/,
|
||||||
|
param: /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/,
|
||||||
|
comment: /^\s*;.*$/
|
||||||
|
}
|
||||||
|
var value = {}
|
||||||
|
var lines = data.split(/\r\n|\r|\n/)
|
||||||
|
var section = null
|
||||||
|
lines.forEach(function(line) {
|
||||||
|
if(regex.comment.test(line)) {
|
||||||
|
return
|
||||||
|
}else if(regex.param.test(line)) {
|
||||||
|
var match = line.match(regex.param)
|
||||||
|
if(section) {
|
||||||
|
value[section][match[1]] = match[2]
|
||||||
|
}else{
|
||||||
|
value[match[1]] = match[2]
|
||||||
|
}
|
||||||
|
}else if(regex.section.test(line)) {
|
||||||
|
var match = line.match(regex.section)
|
||||||
|
value[match[1]] = {}
|
||||||
|
section = match[1]
|
||||||
|
}else if(line.length == 0 && section) {
|
||||||
|
section = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if (typeof exports !== 'undefined') {
|
||||||
|
module.exports = iniParser
|
||||||
|
} else {
|
||||||
|
window.iniParser = iniParser
|
||||||
|
}
|
||||||
|
}).call(this)
|
|
@ -30,6 +30,7 @@
|
||||||
"lib/js/underscore.js",
|
"lib/js/underscore.js",
|
||||||
"lib/js/base64.js",
|
"lib/js/base64.js",
|
||||||
"lib/js/github.js",
|
"lib/js/github.js",
|
||||||
|
"lib/js/iniparser.js",
|
||||||
"inject.js"
|
"inject.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue