commit
77282c0a52
|
@ -50,6 +50,9 @@ By default, Octotree only works on `github.com`. To support GitHub Enterprise on
|
|||
|
||||
## Changelog
|
||||
|
||||
### v1.7.2
|
||||
* Fix bug long branches are not loaded correctly due to GitHub DOM change
|
||||
|
||||
### v1.7.1
|
||||
* Fix space between tree and GitHub contents due to GitHub DOM change
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -7,12 +7,13 @@ const
|
|||
'search', 'developer', 'account'
|
||||
]
|
||||
, GH_RESERVED_REPO_NAMES = ['followers', 'following', 'repositories']
|
||||
, GH_BRANCH_SEL = '[aria-label="Switch branches or tags"]'
|
||||
, GH_404_SEL = '#parallax_wrapper'
|
||||
, GH_PJAX_SEL = '#js-repo-pjax-container'
|
||||
, GH_CONTAINERS = '.container'
|
||||
, GH_404_SEL = '#parallax_wrapper'
|
||||
, GH_PJAX_SEL = '#js-repo-pjax-container'
|
||||
, GH_CONTAINERS = '.container'
|
||||
|
||||
function GitHub() {
|
||||
this._defaultBranch = {}
|
||||
|
||||
if (!window.MutationObserver) return
|
||||
|
||||
// Fix #151 by detecting when page layout is updated.
|
||||
|
@ -36,24 +37,9 @@ function GitHub() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Selects a submodule.
|
||||
*/
|
||||
GitHub.prototype.selectSubmodule = function(path) {
|
||||
window.location.href = path
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the file at the given
|
||||
*/
|
||||
GitHub.prototype.downloadFile = function(path, fileName) {
|
||||
var link = document.createElement('a')
|
||||
link.setAttribute('href', path.replace(/\/blob\//, '/raw/'))
|
||||
link.setAttribute('download', fileName)
|
||||
link.click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a path.
|
||||
* Selects a file.
|
||||
* @param {String} path - the file path.
|
||||
* @param {Number} tabSize - the tab size to use.
|
||||
*/
|
||||
GitHub.prototype.selectFile = function(path, tabSize) {
|
||||
var container = $(GH_PJAX_SEL)
|
||||
|
@ -69,8 +55,30 @@ GitHub.prototype.selectFile = function(path, tabSize) {
|
|||
else window.location.href = path + qs // falls back if no container (i.e. GitHub DOM has changed or is not yet available)
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file
|
||||
* @param {String} path - the file path.
|
||||
* @param {String} fileName - the file name.
|
||||
*/
|
||||
GitHub.prototype.downloadFile = function(path, fileName) {
|
||||
var link = document.createElement('a')
|
||||
link.setAttribute('href', path.replace(/\/blob\//, '/raw/'))
|
||||
link.setAttribute('download', fileName)
|
||||
link.click()
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a submodule
|
||||
* @param {String} path - the submodule path.
|
||||
*/
|
||||
GitHub.prototype.selectSubmodule = function(path) {
|
||||
window.location.href = path
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates page layout based on visibility status and width of the Octotree sidebar.
|
||||
* @param {Boolean} sidebarVisible - current visibility of the sidebar.
|
||||
* @param {Number} sidebarWidth - current width of the sidebar.
|
||||
*/
|
||||
GitHub.prototype.updateLayout = function(sidebarVisible, sidebarWidth) {
|
||||
var $containers = $(GH_CONTAINERS)
|
||||
|
@ -89,50 +97,83 @@ GitHub.prototype.updateLayout = function(sidebarVisible, sidebarWidth) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the repository information if user is at a repository URL. Returns `null` otherwise.
|
||||
* Retrieves the repository info at the current location.
|
||||
* @param {Boolean} showInNonCodePage - if false, should not return data in non-code pages.
|
||||
* @param {Object} currentRepo - current repo being shown by Octotree.
|
||||
* @param {String} token - the personal access token.
|
||||
* @param {Function} cb - the callback function.
|
||||
*/
|
||||
GitHub.prototype.getRepoFromPath = function(showInNonCodePage, currentRepo) {
|
||||
GitHub.prototype.getRepoFromPath = function(showInNonCodePage, currentRepo, token, cb) {
|
||||
|
||||
// 404 page, skip
|
||||
if ($(GH_404_SEL).length) return false
|
||||
if ($(GH_404_SEL).length) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
// (username)/(reponame)[/(type)]
|
||||
var match = window.location.pathname.match(/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/)
|
||||
if (!match) return false
|
||||
if (!match) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
var username = match[1]
|
||||
var reponame = match[2]
|
||||
|
||||
// not a repository, skip
|
||||
if (~GH_RESERVED_USER_NAMES.indexOf(match[1])) return false
|
||||
if (~GH_RESERVED_REPO_NAMES.indexOf(match[2])) return false
|
||||
if (~GH_RESERVED_USER_NAMES.indexOf(username) ||
|
||||
~GH_RESERVED_REPO_NAMES.indexOf(reponame)) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
// skip non-code page unless showInNonCodePage is true
|
||||
if (!showInNonCodePage && match[3] && !~['tree', 'blob'].indexOf(match[3])) return false
|
||||
if (!showInNonCodePage && match[3] && !~['tree', 'blob'].indexOf(match[3])) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
// get branch by inspecting page, quite fragile so provide multiple fallbacks
|
||||
var branch =
|
||||
$(GH_BRANCH_SEL).data('ref') ||
|
||||
$(GH_BRANCH_SEL).children('.js-select-button').text() ||
|
||||
(currentRepo.username === match[1] && currentRepo.reponame === match[2] && currentRepo.branch) ||
|
||||
'master'
|
||||
var GH_BRANCH_SEL_1 = '[aria-label="Switch branches or tags"]'
|
||||
var GH_BRANCH_SEL_2 = '.repo-root a[data-branch]'
|
||||
var GH_BRANCH_SEL_3 = '.repository-sidebar a[aria-label="Code"]'
|
||||
|
||||
return {
|
||||
username : match[1],
|
||||
reponame : match[2],
|
||||
branch : branch
|
||||
var branch =
|
||||
// Code page
|
||||
$(GH_BRANCH_SEL_1).attr('title') || $(GH_BRANCH_SEL_2).data('branch') ||
|
||||
// Non-code page
|
||||
($(GH_BRANCH_SEL_3).attr('href') || '').match(/([^\/]+)/g)[3] ||
|
||||
// Assume same with previously
|
||||
(currentRepo.username === username && currentRepo.reponame === reponame && currentRepo.branch) ||
|
||||
// Default from cache
|
||||
this._defaultBranch[username + '/' + reponame]
|
||||
|
||||
var repo = {username: username, reponame: reponame, branch: branch}
|
||||
|
||||
if (repo.branch) {
|
||||
cb(null, repo)
|
||||
}
|
||||
else {
|
||||
this._get(repo, null, token, function (err, data) {
|
||||
if (err) return cb(err)
|
||||
repo.branch = this._defaultBranch[username + '/' + reponame] = data.default_branch || 'master'
|
||||
cb(null, repo)
|
||||
}.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches data of a particular repository.
|
||||
* @param opts: { repo: repository, token (optional): user access token, apiUrl (optional): base API URL }
|
||||
* @param cb(err: error, tree: array (of arrays) of items)
|
||||
* Retrieves the code tree of a repository.
|
||||
* @param {Object} opts: { repo: repository, node(optional): selected node (null for resursively loading), token (optional): user access token, apiUrl (optional): base API URL }
|
||||
* @param {Function} cb(err: error, tree: array (of arrays) of items)
|
||||
*/
|
||||
GitHub.prototype.fetchData = function(opts, cb) {
|
||||
var self = this
|
||||
, repo = opts.repo
|
||||
, folders = { '': [] }
|
||||
GitHub.prototype.getCodeTree = function(opts, cb) {
|
||||
var self = this
|
||||
, folders = { '': [] }
|
||||
, repo = opts.repo
|
||||
, token = opts.token
|
||||
, encodedBranch = encodeURIComponent(decodeURIComponent(repo.branch))
|
||||
, $dummyDiv = $('<div/>')
|
||||
, $dummyDiv = $('<div/>')
|
||||
|
||||
getTree(encodedBranch + '?recursive=true', function(err, tree) {
|
||||
var treePath = (opts.node && (opts.node.sha || encodedBranch)) || (encodedBranch + '?recursive=1')
|
||||
getTree(treePath, function(err, tree) {
|
||||
if (err) return cb(err)
|
||||
|
||||
fetchSubmodules(function(err, submodules) {
|
||||
|
@ -153,6 +194,10 @@ GitHub.prototype.fetchData = function(opts, cb) {
|
|||
// we're done
|
||||
if (item === undefined) return cb(null, folders[''])
|
||||
|
||||
// includes parent path
|
||||
if (opts.node && opts.node.path)
|
||||
item.path = opts.node.path + '/' + item.path
|
||||
|
||||
path = item.path
|
||||
type = item.type
|
||||
index = path.lastIndexOf('/')
|
||||
|
@ -162,10 +207,16 @@ GitHub.prototype.fetchData = function(opts, cb) {
|
|||
item.text = name
|
||||
item.icon = type // use `type` as class name for tree node
|
||||
|
||||
folders[path.substring(0, index)].push(item)
|
||||
if (opts.node) {
|
||||
// no hierarchy in lazy loading
|
||||
folders[''].push(item)
|
||||
}
|
||||
else
|
||||
folders[path.substring(0, index)].push(item)
|
||||
|
||||
if (type === 'tree') {
|
||||
folders[item.path] = item.children = []
|
||||
if (opts.node) item.children = true
|
||||
else folders[item.path] = item.children = []
|
||||
item.a_attr = { href: '#' }
|
||||
}
|
||||
else if (type === 'blob') {
|
||||
|
@ -206,79 +257,81 @@ GitHub.prototype.fetchData = function(opts, cb) {
|
|||
})
|
||||
|
||||
function getTree(tree, cb) {
|
||||
get('/git/trees/' + tree, function(err, res) {
|
||||
self._get(repo, '/git/trees/' + tree, token, function(err, res) {
|
||||
if (err) return cb(err)
|
||||
cb(null, res.tree)
|
||||
})
|
||||
}
|
||||
|
||||
function getBlob(sha, cb) {
|
||||
get('/git/blobs/' + sha, function(err, res) {
|
||||
self._get(repo, '/git/blobs/' + sha, token, function(err, res) {
|
||||
if (err) return cb(err)
|
||||
cb(null, atob(res.content.replace(/\n/g,'')))
|
||||
})
|
||||
}
|
||||
|
||||
function get(path, cb) {
|
||||
var token = opts.token
|
||||
, host = (location.host === 'github.com' ? 'api.github.com' : (location.host + '/api/v3'))
|
||||
, base = location.protocol + '//' + host + '/repos/' + repo.username + '/' + repo.reponame
|
||||
, cfg = { method: 'GET', url: base + path, cache: false }
|
||||
|
||||
if (token) cfg.headers = { Authorization: 'token ' + token }
|
||||
$.ajax(cfg)
|
||||
.done(function(data) {
|
||||
cb(null, data)
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
var createTokenUrl = location.protocol + '//' + location.host + '/settings/tokens/new'
|
||||
, error
|
||||
, message
|
||||
, needAuth
|
||||
|
||||
switch (jqXHR.status) {
|
||||
case 0:
|
||||
error = 'Connection error'
|
||||
message = 'Cannot connect to GitHub. If your network connection to GitHub is fine, maybe there is an outage of the GitHub API. Please try again later.'
|
||||
needAuth = false
|
||||
break
|
||||
case 401:
|
||||
error = 'Invalid token'
|
||||
message = 'The token is invalid. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create a new token and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
case 409:
|
||||
error = 'Empty repository'
|
||||
message = 'This repository is empty.'
|
||||
break
|
||||
case 404:
|
||||
error = 'Private repository'
|
||||
message = 'Accessing private repositories requires a GitHub access token. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create one and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
case 403:
|
||||
if (~jqXHR.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
|
||||
error = 'API limit exceeded'
|
||||
message = 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create one and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
}
|
||||
else {
|
||||
error = 'Forbidden'
|
||||
message = 'You are not allowed to access the API. You might need to provide an access token. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create one and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
}
|
||||
default:
|
||||
error = message = jqXHR.statusText
|
||||
needAuth = false
|
||||
break
|
||||
}
|
||||
cb({
|
||||
error : 'Error: ' + error,
|
||||
message : message,
|
||||
needAuth : needAuth,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
GitHub.prototype._get = function(repo, path, token, cb) {
|
||||
var host = (location.host === 'github.com' ? 'api.github.com' : (location.host + '/api/v3'))
|
||||
, base = location.protocol + '//' + host + '/repos/' + repo.username + '/' + repo.reponame
|
||||
, cfg = { method: 'GET', url: base + (path || ''), cache: false }
|
||||
|
||||
if (token) {
|
||||
cfg.headers = { Authorization: 'token ' + token }
|
||||
}
|
||||
|
||||
$.ajax(cfg)
|
||||
.done(function(data) {
|
||||
cb(null, data)
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
var createTokenUrl = location.protocol + '//' + location.host + '/settings/tokens/new'
|
||||
, error
|
||||
, message
|
||||
, needAuth
|
||||
|
||||
switch (jqXHR.status) {
|
||||
case 0:
|
||||
error = 'Connection error'
|
||||
message = 'Cannot connect to GitHub. If your network connection to GitHub is fine, maybe there is an outage of the GitHub API. Please try again later.'
|
||||
needAuth = false
|
||||
break
|
||||
case 401:
|
||||
error = 'Invalid token'
|
||||
message = 'The token is invalid. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create a new token and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
case 409:
|
||||
error = 'Empty repository'
|
||||
message = 'This repository is empty.'
|
||||
break
|
||||
case 404:
|
||||
error = 'Private repository'
|
||||
message = 'Accessing private repositories requires a GitHub access token. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create one and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
case 403:
|
||||
if (~jqXHR.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
|
||||
error = 'API limit exceeded'
|
||||
message = 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create one and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
}
|
||||
else {
|
||||
error = 'Forbidden'
|
||||
message = 'You are not allowed to access the API. You might need to provide an access token. Follow <a href="' + createTokenUrl + '" target="_blank">this link</a> to create one and paste it below.'
|
||||
needAuth = true
|
||||
break
|
||||
}
|
||||
default:
|
||||
error = message = jqXHR.statusText
|
||||
needAuth = false
|
||||
break
|
||||
}
|
||||
cb({
|
||||
error : 'Error: ' + error,
|
||||
message : message,
|
||||
needAuth : needAuth,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Octotree",
|
||||
"version": "1.7.1",
|
||||
"version": "2.0.0",
|
||||
"manifest_version": 2,
|
||||
"author": "Buu Nguyen",
|
||||
"description": "Display GitHub code in tree format",
|
||||
|
|
|
@ -2,17 +2,18 @@ const
|
|||
PREFIX = 'octotree'
|
||||
|
||||
, STORE = {
|
||||
TOKEN : 'octotree.github_access_token',
|
||||
COLLAPSE : 'octotree.collapse',
|
||||
TABSIZE : 'octotree.tabsize',
|
||||
REMEMBER : 'octotree.remember',
|
||||
LAZYLOAD : 'octotree.lazyload',
|
||||
HOTKEYS : 'octotree.hotkeys',
|
||||
GHEURLS : 'octotree.gheurls',
|
||||
WIDTH : 'octotree.sidebar_width',
|
||||
POPUP : 'octotree.popup_shown',
|
||||
SHOWN : 'octotree.sidebar_shown',
|
||||
NONCODE : 'octotree.noncode_shown',
|
||||
TOKEN : 'octotree.github_access_token',
|
||||
COLLAPSE : 'octotree.collapse',
|
||||
TABSIZE : 'octotree.tabsize',
|
||||
REMEMBER : 'octotree.remember',
|
||||
LAZYLOAD : 'octotree.lazyload',
|
||||
RECURSIVE : 'octotree.recursive',
|
||||
HOTKEYS : 'octotree.hotkeys',
|
||||
GHEURLS : 'octotree.gheurls',
|
||||
WIDTH : 'octotree.sidebar_width',
|
||||
POPUP : 'octotree.popup_shown',
|
||||
SHOWN : 'octotree.sidebar_shown',
|
||||
NONCODE : 'octotree.noncode_shown',
|
||||
}
|
||||
|
||||
, DEFAULTS = {
|
||||
|
@ -21,6 +22,7 @@ const
|
|||
TABSIZE : '',
|
||||
REMEMBER : false,
|
||||
LAZYLOAD : false,
|
||||
RECURSIVE: true,
|
||||
// @ifdef SAFARI
|
||||
HOTKEYS : '⌘+b, ⌃+b',
|
||||
// @endif
|
||||
|
@ -43,4 +45,5 @@ const
|
|||
OPTS_CHANGE : 'octotree:change',
|
||||
VIEW_READY : 'octotree:ready',
|
||||
VIEW_CLOSE : 'octotree:close',
|
||||
FETCH_ERROR : 'octotree:error'
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
"icon": "data/icons/icon48.png",
|
||||
"icon64": "data/icons/icon64.png",
|
||||
"license": "MIT",
|
||||
"version": "1.7.1",
|
||||
"version": "2.0.0",
|
||||
"permissions": {
|
||||
"cross-domain-content": ["https://api.github.com", "https://github.com"]
|
||||
}
|
||||
|
|
|
@ -50,6 +50,9 @@ $(document).ready(function() {
|
|||
showView(hasError ? errorView.$view : treeView.$view)
|
||||
})
|
||||
.on(EVENT.OPTS_CHANGE, optionsChanged)
|
||||
.on(EVENT.FETCH_ERROR, function(event, err) {
|
||||
errorView.show(err)
|
||||
})
|
||||
})
|
||||
|
||||
$document
|
||||
|
@ -81,6 +84,9 @@ $(document).ready(function() {
|
|||
key.unbind(value[0])
|
||||
key(value[1], toggleSidebar)
|
||||
break
|
||||
case STORE.RECURSIVE:
|
||||
reload = true
|
||||
break
|
||||
}
|
||||
})
|
||||
if (reload) tryLoadRepo(true)
|
||||
|
@ -92,33 +98,34 @@ $(document).ready(function() {
|
|||
, shown = store.get(STORE.SHOWN)
|
||||
, lazyload = store.get(STORE.LAZYLOAD)
|
||||
, token = store.get(STORE.TOKEN)
|
||||
, repo = adapter.getRepoFromPath(showInNonCodePage, currRepo)
|
||||
|
||||
if (repo) {
|
||||
$toggler.show()
|
||||
helpPopup.show()
|
||||
|
||||
if (remember && shown) toggleSidebar(true)
|
||||
|
||||
if (!lazyload || isSidebarVisible()) {
|
||||
var repoChanged = JSON.stringify(repo) !== JSON.stringify(currRepo)
|
||||
if (repoChanged || reload === true) {
|
||||
$document.trigger(EVENT.REQ_START)
|
||||
currRepo = repo
|
||||
treeView.showHeader(repo)
|
||||
|
||||
adapter.fetchData({ repo: repo, token: token }, function(err, tree) {
|
||||
if (err) errorView.show(err)
|
||||
else treeView.show(repo, tree)
|
||||
})
|
||||
}
|
||||
else treeView.syncSelection()
|
||||
adapter.getRepoFromPath(showInNonCodePage, currRepo, token, function(err, repo) {
|
||||
if (err) {
|
||||
errorView.show(err)
|
||||
}
|
||||
}
|
||||
else {
|
||||
$toggler.hide()
|
||||
toggleSidebar(false)
|
||||
}
|
||||
else if (repo) {
|
||||
$toggler.show()
|
||||
helpPopup.show()
|
||||
|
||||
if (remember && shown) toggleSidebar(true)
|
||||
|
||||
if (!lazyload || isSidebarVisible()) {
|
||||
var repoChanged = JSON.stringify(repo) !== JSON.stringify(currRepo)
|
||||
if (repoChanged || reload === true) {
|
||||
$document.trigger(EVENT.REQ_START)
|
||||
currRepo = repo
|
||||
|
||||
treeView.showHeader(repo)
|
||||
treeView.show(repo, token)
|
||||
}
|
||||
else treeView.syncSelection()
|
||||
}
|
||||
}
|
||||
else {
|
||||
$toggler.hide()
|
||||
toggleSidebar(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function showView(view) {
|
||||
|
|
|
@ -202,6 +202,11 @@
|
|||
label {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
label.disabled {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
input[type=text], textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.7.1</string>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.7.1</string>
|
||||
<string>2.0.0</string>
|
||||
<key>Chrome</key>
|
||||
<dict/>
|
||||
<key>Content</key>
|
||||
|
|
|
@ -53,7 +53,10 @@
|
|||
<label><input type="checkbox" data-store="LAZYLOAD"> Only load tree when sidebar is open</label>
|
||||
</div>
|
||||
<div>
|
||||
<label><input type="checkbox" data-store="COLLAPSE"> Collapse folders with single sub-folder</label>
|
||||
<label><input type="checkbox" data-store="RECURSIVE"> Load repository recursively</label>
|
||||
</div>
|
||||
<div>
|
||||
<label><input type="checkbox" data-store="COLLAPSE" data-trigger-disable="RECURSIVE"> Collapse folders with single sub-folder</label>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
$(document).ready(function() {
|
||||
// When navigating from non-code pages (i.e. Pulls, Issues) to code page
|
||||
// GitHub doesn't reload the page but uses pjax. Need to detect and load Octotree.
|
||||
var href, hash
|
||||
var firstLoad = true, href, hash
|
||||
function detectLocationChange() {
|
||||
if (location.href !== href || location.hash !== hash) {
|
||||
href = location.href
|
||||
hash = location.hash
|
||||
$(document).trigger(EVENT.LOC_CHANGE, href, hash)
|
||||
|
||||
// If this is the first time this is called, no need to notify change as
|
||||
// Octotree does its own initialization after loading options.
|
||||
if (firstLoad) {
|
||||
firstLoad = false
|
||||
}
|
||||
else {
|
||||
setTimeout(function () {
|
||||
$(document).trigger(EVENT.LOC_CHANGE, href, hash)
|
||||
}, 200) // Waits a bit for pjax DOM change
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(detectLocationChange, 200)
|
||||
}
|
||||
detectLocationChange()
|
||||
|
|
|
@ -6,6 +6,23 @@ function OptionsView($dom, store) {
|
|||
|
||||
this.$view = $view
|
||||
|
||||
$(document).ready(function() {
|
||||
function triggerChange(checkbox) {
|
||||
var store = $(checkbox).data('store')
|
||||
, checkboxs = $view.find('[data-trigger-disable=' + store + ']')
|
||||
checkboxs.prop('disabled', !checkbox.checked).closest('label').toggleClass('disabled', !checkbox.checked)
|
||||
}
|
||||
|
||||
eachOption(
|
||||
function($elm) {
|
||||
// triggers to disable all checkboxs having data-trigger-disable
|
||||
$elm.change(function(event) {
|
||||
triggerChange(event.target)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// hide options view when sidebar is hidden
|
||||
$(document).on(EVENT.TOGGLE, function(event, visible) {
|
||||
if (!visible) toggle(false)
|
||||
|
@ -23,7 +40,8 @@ function OptionsView($dom, store) {
|
|||
else {
|
||||
eachOption(
|
||||
function($elm, key, local, value, cb) {
|
||||
if ($elm.is(':checkbox')) $elm.prop('checked', value)
|
||||
// Original jQuery prop function doesn't trigger change event
|
||||
if ($elm.is(':checkbox')) $elm.prop('checked', value).trigger("change")
|
||||
else $elm.val(value)
|
||||
cb()
|
||||
},
|
||||
|
|
|
@ -74,15 +74,30 @@ TreeView.prototype.showHeader = function(repo) {
|
|||
})
|
||||
}
|
||||
|
||||
TreeView.prototype.show = function(repo, treeData) {
|
||||
TreeView.prototype.show = function(repo, token) {
|
||||
var self = this
|
||||
, treeContainer = self.$view.find('.octotree_view_body')
|
||||
, tree = treeContainer.jstree(true)
|
||||
, collapseTree = self.store.get(STORE.COLLAPSE)
|
||||
, recursiveLoad = self.store.get(STORE.RECURSIVE)
|
||||
|
||||
treeData = sort(treeData)
|
||||
if (collapseTree) treeData = collapse(treeData)
|
||||
tree.settings.core.data = treeData
|
||||
function fetchData(node, success) {
|
||||
var selectedNode = node.original
|
||||
if (node.id === '#') selectedNode = {path: ''}
|
||||
self.adapter.getCodeTree({ repo: repo, token: token, node: recursiveLoad ? null : selectedNode}, function(err, treeData) {
|
||||
if (err) $(self).trigger(EVENT.FETCH_ERROR, [err])
|
||||
else success(treeData)
|
||||
})
|
||||
}
|
||||
|
||||
tree.settings.core.data = function (node, cb) {
|
||||
fetchData(node, function(treeData) {
|
||||
treeData = sort(treeData)
|
||||
if (collapseTree && recursiveLoad)
|
||||
treeData = collapse(treeData)
|
||||
cb(treeData)
|
||||
})
|
||||
}
|
||||
|
||||
treeContainer.one('refresh.jstree', function() {
|
||||
self.syncSelection()
|
||||
|
@ -97,7 +112,7 @@ TreeView.prototype.show = function(repo, treeData) {
|
|||
return a.type === 'blob' ? 1 : -1
|
||||
})
|
||||
folder.forEach(function(item) {
|
||||
if (item.type === 'tree') sort(item.children)
|
||||
if (item.type === 'tree' && item.children !== true && item.children.length > 0) sort(item.children)
|
||||
})
|
||||
return folder
|
||||
}
|
||||
|
@ -119,17 +134,41 @@ TreeView.prototype.show = function(repo, treeData) {
|
|||
|
||||
TreeView.prototype.syncSelection = function() {
|
||||
var tree = this.$view.find('.octotree_view_body').jstree(true)
|
||||
, path = location.pathname
|
||||
, path = decodeURIComponent(location.pathname)
|
||||
, recursiveLoad = this.store.get(STORE.RECURSIVE)
|
||||
|
||||
if (!tree) return
|
||||
tree.deselect_all()
|
||||
|
||||
// e.g. converts /buunguyen/octotree/type/branch/path to path
|
||||
var match = path.match(/(?:[^\/]+\/){4}(.*)/)
|
||||
, nodeId
|
||||
if (match) {
|
||||
nodeId = PREFIX + decodeURIComponent(match[1])
|
||||
tree.select_node(nodeId)
|
||||
tree.open_node(nodeId)
|
||||
if (!match) return
|
||||
|
||||
currentPath = match[1]
|
||||
|
||||
// e.g. converts ["lib/controllers"] to ["lib", "lib/controllers"]
|
||||
function createPaths(fullPath) {
|
||||
var paths = fullPath.split("/")
|
||||
, arrResult = [paths[0]]
|
||||
|
||||
paths.reduce(function(lastPath, curPath) {
|
||||
var path = (lastPath + "/" + curPath)
|
||||
arrResult.push(path)
|
||||
return path
|
||||
})
|
||||
return arrResult
|
||||
}
|
||||
|
||||
function openPathAtIndex (paths, index) {
|
||||
nodeId = PREFIX + paths[index]
|
||||
if (tree.get_node(nodeId)) {
|
||||
tree.deselect_all()
|
||||
tree.select_node(nodeId)
|
||||
tree.open_node(nodeId, function(node){
|
||||
if (index < paths.length - 1) openPathAtIndex(paths, index + 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var paths = recursiveLoad ? [currentPath] : createPaths(currentPath)
|
||||
openPathAtIndex(paths, 0)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue