码云git

This commit is contained in:
inu1255 2017-06-15 16:44:50 +08:00
parent c855e8fdb1
commit 7f4bf0c84b
3 changed files with 339 additions and 354 deletions

View File

@ -1,389 +1,384 @@
class Adapter {
constructor(deps) {
deps.forEach(dep => window[dep]())
this._defaultBranch = {}
}
constructor(deps) {
deps.forEach(dep => window[dep]())
this._defaultBranch = {}
}
/**
* Loads the code tree of a repository.
* @param {Object} opts: {
* path: the starting path to load the tree,
* repo: the current repository,
* node (optional): the selected node (null to load entire tree),
* token (optional): the personal access token
* }
* @param {Function} transform(item)
* @param {Function} cb(err: error, tree: Array[Array|item])
*/
_loadCodeTree(opts, transform, cb) {
const folders = { '': [] }
const $dummyDiv = $('<div/>')
const {path, repo, node} = opts
/**
* Loads the code tree of a repository.
* @param {Object} opts: {
* path: the starting path to load the tree,
* repo: the current repository,
* node (optional): the selected node (null to load entire tree),
* token (optional): the personal access token
* }
* @param {Function} transform(item)
* @param {Function} cb(err: error, tree: Array[Array|item])
*/
_loadCodeTree(opts, transform, cb) {
const folders = { '': [] }
const $dummyDiv = $('<div/>')
const {path, repo, node} = opts
opts.encodedBranch = opts.encodedBranch || encodeURIComponent(decodeURIComponent(repo.branch))
opts.encodedBranch = opts.encodedBranch || encodeURIComponent(decodeURIComponent(repo.branch))
this._getTree(path, opts, (err, tree) => {
if (err) return cb(err)
this._getTree(path, opts, (err, tree) => {
if (err) return cb(err)
this._getSubmodules(tree, opts, (err, submodules) => {
if (err) return cb(err)
this._getSubmodules(tree, opts, (err, submodules) => {
if (err) return cb(err)
submodules = submodules || {}
submodules = submodules || {}
const nextChunk = (iteration = 0) => {
const CHUNK_SIZE = 300
const nextChunk = (iteration = 0) => {
const CHUNK_SIZE = 300
for (let i = 0; i < CHUNK_SIZE; i++) {
const item = tree[iteration * CHUNK_SIZE + i]
for (let i = 0; i < CHUNK_SIZE; i++) {
const item = tree[iteration * CHUNK_SIZE + i]
// we're done
if (item === undefined) {
return cb(null, folders[''])
}
// we're done
if (item === undefined) {
return cb(null, folders[''])
}
// runs transform requested by subclass
if (transform) {
transform(item)
}
// runs transform requested by subclass
if (transform) {
transform(item)
}
// if lazy load and has parent, prefix with parent path
if (node && node.path) {
item.path = node.path + '/' + item.path
}
// if lazy load and has parent, prefix with parent path
if (node && node.path) {
item.path = node.path + '/' + item.path
}
const path = item.path
const type = item.type
const index = path.lastIndexOf('/')
const name = $dummyDiv.text(path.substring(index + 1)).html() // sanitizes, closes #9
const path = item.path
const type = item.type
const index = path.lastIndexOf('/')
const name = $dummyDiv.text(path.substring(index + 1)).html() // sanitizes, closes #9
item.id = NODE_PREFIX + path
item.text = name
item.icon = type // uses `type` as class name for tree node
item.id = NODE_PREFIX + path
item.text = name
item.icon = type // uses `type` as class name for tree node
if (node) {
folders[''].push(item)
}
else {
folders[path.substring(0, index)].push(item)
}
if (node) {
folders[''].push(item)
} else {
folders[path.substring(0, index)].push(item)
}
if (type === 'tree' || type === 'blob') {
if (type === 'tree') {
if (node) item.children = true
else folders[item.path] = item.children = []
}
if (type === 'tree' || type === 'blob') {
if (type === 'tree') {
if (node)
item.children = true
else
folders[item.path] = item.children = []
}
// encodes but retains the slashes, see #274
const encodedPath = path.split('/').map(encodeURIComponent).join('/')
item.a_attr = {
href: this._getItemHref(repo, type, path)
}
}
else if (type === 'commit') {
let moduleUrl = submodules[item.path]
if (moduleUrl) { // fixes #105
// special handling for submodules hosted in GitHub
if (~moduleUrl.indexOf('github.com')) {
moduleUrl = moduleUrl.replace(/^git(:\/\/|@)/, window.location.protocol + '//')
.replace('github.com:', 'github.com/')
.replace(/.git$/, '')
item.text = `<a href="${moduleUrl}" class="jstree-anchor">${name}</a>
// encodes but retains the slashes, see #274
const encodedPath = path.split('/').map(encodeURIComponent).join('/')
item.a_attr = {
href: this._getItemHref(repo, type, path)
}
} else if (type === 'commit') {
let moduleUrl = submodules[item.path]
if (moduleUrl) { // fixes #105
// special handling for submodules hosted in GitHub
if (~moduleUrl.indexOf('github.com')) {
moduleUrl = moduleUrl.replace(/^git(:\/\/|@)/, window.location.protocol + '//')
.replace('github.com:', 'github.com/')
.replace(/.git$/, '')
item.text = `<a href="${moduleUrl}" class="jstree-anchor">${name}</a>
<span>@ </span>
<a href="${moduleUrl}/tree/${item.sha}" class="jstree-anchor">${item.sha.substr(0, 7)}</a>`
}
item.a_attr = { href: moduleUrl }
}
}
}
setTimeout(() => nextChunk(iteration + 1))
}
item.a_attr = { href: moduleUrl }
}
}
}
setTimeout(() => nextChunk(iteration + 1))
}
nextChunk()
})
})
}
nextChunk()
})
})
}
_handleError(jqXHR, cb) {
let error, message, needAuth
_handleError(jqXHR, cb) {
let error, message, needAuth
switch (jqXHR.status) {
case 0:
error = 'Connection error'
message =
`Cannot connect to website.
switch (jqXHR.status) {
case 0:
error = 'Connection error'
message =
`Cannot connect to website.
If your network connection to this website is fine, maybe there is an outage of the API.
Please try again later.`
needAuth = false
break
case 206:
error = 'Repo too large'
message =
`This repository is too large to be retrieved at once.
needAuth = false
break
case 206:
error = 'Repo too large'
message =
`This repository is too large to be retrieved at once.
If you frequently work with this repository, go to Settings and uncheck the "Load entire tree at once" option.`
break
case 401:
error = 'Invalid token'
message =
`The token is invalid.
break
case 401:
error = 'Invalid token'
message =
`The token is invalid.
Follow <a href="${this.getCreateTokenUrl()}" 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 an access token.
Follow <a href="${this.getCreateTokenUrl()}" 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')) {
// It's kinda specific for GitHub
error = 'API limit exceeded'
message =
`You have exceeded the GitHub API hourly limit and need GitHub access token
needAuth = true
break
case 409:
error = 'Empty repository'
message = 'This repository is empty.'
break
case 404:
error = 'Private repository'
message =
`访问私有仓库需要access token.
<a href="${this.getCreateTokenUrl()}" target="_blank">点些链接</a>
去创建一个access token并粘贴到下面.`
needAuth = true
break
case 403:
if (~jqXHR.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
// It's kinda specific for GitHub
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="${this.getCreateTokenUrl()}" target="_blank">this link</a>
to create one and paste it below.`
needAuth = true
break
needAuth = true
break
} else {
error = 'Forbidden'
message =
`禁止访问.
你可能需要提供 access token.
<a href="${this.getCreateTokenUrl()}" target="_blank">点些链接</a>
去创建一个access token并粘贴到下面.`
needAuth = true
break
}
default:
error = message = jqXHR.statusText
needAuth = false
break
}
else {
error = 'Forbidden'
message =
`You are not allowed to access the API.
You might need to provide an access token.
Follow <a href="${this.getCreateTokenUrl()}" 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
})
}
cb({
error: `Error: ${error}`,
message: message,
needAuth: needAuth
})
}
/**
* Inits behaviors after the sidebar is added to the DOM.
* @api public
*/
init($sidebar) {
$sidebar
.resizable({ handles: 'e', minWidth: this.getMinWidth() })
.addClass(this.getCssClass())
}
/**
* Inits behaviors after the sidebar is added to the DOM.
* @api public
*/
init($sidebar) {
$sidebar
.resizable({ handles: 'e', minWidth: this.getMinWidth() })
.addClass(this.getCssClass())
}
/**
* Returns the CSS class to be added to the Octotree sidebar.
* @api protected
*/
getCssClass() {
throw new Error('Not implemented')
}
/**
* Returns the CSS class to be added to the Octotree sidebar.
* @api protected
*/
getCssClass() {
throw new Error('Not implemented')
}
/**
* Returns the minimum width acceptable for the sidebar.
* @api protected
*/
getMinWidth() {
return 200
}
/**
* Returns the minimum width acceptable for the sidebar.
* @api protected
*/
getMinWidth() {
return 200
}
/**
* Returns whether the adapter is capable of loading the entire tree in
* a single request. This is usually determined by the underlying the API.
* @api public
*/
canLoadEntireTree() {
return false
}
/**
* Returns whether the adapter is capable of loading the entire tree in
* a single request. This is usually determined by the underlying the API.
* @api public
*/
canLoadEntireTree() {
return false
}
/**
* Loads the code tree.
* @api public
*/
loadCodeTree(opts, cb) {
throw new Error('Not implemented')
}
/**
* Loads the code tree.
* @api public
*/
loadCodeTree(opts, cb) {
throw new Error('Not implemented')
}
/**
* Returns the URL to create a personal access token.
* @api public
*/
getCreateTokenUrl() {
throw new Error('Not implemented')
}
/**
* Returns the URL to create a personal access token.
* @api public
*/
getCreateTokenUrl() {
throw new Error('Not implemented')
}
/**
* Updates the layout based on sidebar visibility and width.
* @api public
*/
updateLayout(togglerVisible, sidebarVisible, sidebarWidth) {
throw new Error('Not implemented')
}
/**
* Updates the layout based on sidebar visibility and width.
* @api public
*/
updateLayout(togglerVisible, sidebarVisible, sidebarWidth) {
throw new Error('Not implemented')
}
/**
* Returns repo info at the current path.
* @api public
*/
getRepoFromPath(showInNonCodePage, currentRepo, token, cb) {
throw new Error('Not implemented')
}
/**
* Returns repo info at the current path.
* @api public
*/
getRepoFromPath(showInNonCodePage, currentRepo, token, cb) {
throw new Error('Not implemented')
}
/**
* Selects the file at a specific path.
* @api public
*/
selectFile(path) {
window.location.href = path
}
/**
* Selects the file at a specific path.
* @api public
*/
selectFile(path) {
window.location.href = path
}
/**
* Selects a submodule.
* @api public
*/
selectSubmodule(path) {
window.location.href = path
}
/**
* Selects a submodule.
* @api public
*/
selectSubmodule(path) {
window.location.href = path
}
/**
* Opens file or submodule in a new tab.
* @api public
*/
openInNewTab(path) {
window.open(path, '_blank').focus()
}
/**
* Opens file or submodule in a new tab.
* @api public
*/
openInNewTab(path) {
window.open(path, '_blank').focus()
}
/**
* Downloads a file.
* @api public
*/
downloadFile(path, fileName) {
const link = document.createElement('a')
link.setAttribute('href', path.replace(/\/blob\/|\/src\//, '/raw/'))
link.setAttribute('download', fileName)
link.click()
}
/**
* Downloads a file.
* @api public
*/
downloadFile(path, fileName) {
const link = document.createElement('a')
link.setAttribute('href', path.replace(/\/blob\/|\/src\//, '/raw/'))
link.setAttribute('download', fileName)
link.click()
}
/**
* Gets tree at path.
* @param {Object} opts - {token, repo}
* @api protected
*/
_getTree(path, opts, cb) {
throw new Error('Not implemented')
}
/**
* Gets tree at path.
* @param {Object} opts - {token, repo}
* @api protected
*/
_getTree(path, opts, cb) {
throw new Error('Not implemented')
}
/**
* Gets submodules in the tree.
* @param {Object} opts - {token, repo, encodedBranch}
* @api protected
*/
_getSubmodules(tree, opts, cb) {
throw new Error('Not implemented')
}
/**
* Gets submodules in the tree.
* @param {Object} opts - {token, repo, encodedBranch}
* @api protected
*/
_getSubmodules(tree, opts, cb) {
throw new Error('Not implemented')
}
/**
* Returns item's href value.
* @api protected
*/
_getItemHref(repo, type, encodedPath) {
return `/${repo.username}/${repo.reponame}/${type}/${repo.branch}/${encodedPath}`
}
/**
* Returns item's href value.
* @api protected
*/
_getItemHref(repo, type, encodedPath) {
return `/${repo.username}/${repo.reponame}/${type}/${repo.branch}/${encodedPath}`
}
}
class PjaxAdapter extends Adapter {
constructor() {
super(['jquery.pjax.js'])
constructor() {
super(['jquery.pjax.js'])
$.pjax.defaults.timeout = 0 // no timeout
$(document)
.on('pjax:send', () => $(document).trigger(EVENT.REQ_START))
.on('pjax:end', () => $(document).trigger(EVENT.REQ_END))
}
// @override
// @param {Object} opts - {pjaxContainer: the specified pjax container}
// @api public
init($sidebar, opts) {
super.init($sidebar)
opts = opts || {}
const pjaxContainer = opts.pjaxContainer
if (!window.MutationObserver) return
// Some host switch pages using pjax. This observer detects if the pjax container
// has been updated with new contents and trigger layout.
const pageChangeObserver = new window.MutationObserver(() => {
// Trigger location change, can't just relayout as Octotree might need to
// hide/show depending on whether the current page is a code page or not.
return $(document).trigger(EVENT.LOC_CHANGE)
})
if (pjaxContainer) {
pageChangeObserver.observe(pjaxContainer, {
childList: true,
})
$.pjax.defaults.timeout = 0 // no timeout
$(document)
.on('pjax:send', () => $(document).trigger(EVENT.REQ_START))
.on('pjax:end', () => $(document).trigger(EVENT.REQ_END))
}
else { // Fall back if DOM has been changed
let firstLoad = true, href, hash
function detectLocChange() {
if (location.href !== href || location.hash !== hash) {
href = location.href
hash = location.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(() => {
$(document).trigger(EVENT.LOC_CHANGE)
}, 300) // Wait a bit for pjax DOM change
}
// @override
// @param {Object} opts - {pjaxContainer: the specified pjax container}
// @api public
init($sidebar, opts) {
super.init($sidebar)
opts = opts || {}
const pjaxContainer = opts.pjaxContainer
if (!window.MutationObserver) return
// Some host switch pages using pjax. This observer detects if the pjax container
// has been updated with new contents and trigger layout.
const pageChangeObserver = new window.MutationObserver(() => {
// Trigger location change, can't just relayout as Octotree might need to
// hide/show depending on whether the current page is a code page or not.
return $(document).trigger(EVENT.LOC_CHANGE)
})
if (pjaxContainer) {
pageChangeObserver.observe(pjaxContainer, {
childList: true,
})
} else { // Fall back if DOM has been changed
let firstLoad = true, href, hash
function detectLocChange() {
if (location.href !== href || location.hash !== hash) {
href = location.href
hash = location.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(() => {
$(document).trigger(EVENT.LOC_CHANGE)
}, 300) // Wait a bit for pjax DOM change
}
}
setTimeout(detectLocChange, 200)
}
detectLocChange()
}
setTimeout(detectLocChange, 200)
}
detectLocChange()
}
}
// @override
// @param {Object} opts - {$pjax_container: jQuery object}
// @api public
selectFile(path, opts) {
opts = opts || {}
const $pjaxContainer = opts.$pjaxContainer
// @override
// @param {Object} opts - {$pjax_container: jQuery object}
// @api public
selectFile(path, opts) {
opts = opts || {}
const $pjaxContainer = opts.$pjaxContainer
if ($pjaxContainer.length) {
$.pjax({
// needs full path for pjax to work with Firefox as per cross-domain-content setting
url: location.protocol + '//' + location.host + path,
container: $pjaxContainer
})
if ($pjaxContainer.length) {
$.pjax({
// needs full path for pjax to work with Firefox as per cross-domain-content setting
url: location.protocol + '//' + location.host + path,
container: $pjaxContainer
})
} else { // falls back
super.selectFile(path)
}
}
else { // falls back
super.selectFile(path)
}
}
}

View File

@ -58,7 +58,7 @@ class Oschina extends PjaxAdapter {
// @override
getCreateTokenUrl() {
return `${location.protocol}//${location.host}/settings/tokens/new`
return `http://git.oschina.net/api/v5/swagger`
}
// @override
@ -110,7 +110,7 @@ class Oschina extends PjaxAdapter {
// Get branch by inspecting page, quite fragile so provide multiple fallbacks
const branch =
// Code page
$('.branch-select-menu .select-menu-item.selected').data('name') ||
$('#git-project-branch .text').text() ||
// Pull requests page
($('.commit-ref.base-ref').attr('title') || ':').match(/:(.*)/)[1] ||
// Reuse last selected branch if exist
@ -169,11 +169,11 @@ class Oschina extends PjaxAdapter {
_get(path, opts, cb) {
const host = 'http://git.oschina.net/api/v5'
var url = `${host}/repos/${opts.repo.username}/${opts.repo.reponame}${path || ''}`
const cfg = { url, method: 'GET', cache: false }
if (opts.token) {
url += (url.endsWith("?") ? "&" : "?") + `access_token=${opts.token}`
url += (url.indexOf("?") >= 0 ? "&" : "?") + `access_token=${opts.token}`
}
const cfg = { url, method: 'GET', cache: false }
$.ajax(cfg)
.done((data) => {

View File

@ -7,9 +7,9 @@
<div class="popup">
<div class="arrow"></div>
<div class="content">
Octotree is enabled on this page. Click this button or press
GitCodeTree已启用。点此
<kbd>cmd shift s</kbd> (or <kbd>ctrl shift s</kbd>)
to show it.
显示.
</div>
</div>
</a>
@ -29,21 +29,21 @@
<form class="octotree_view_body">
<div class="message"></div>
<div>
<input name="token" type="text" placeholder="Paste access token here" autocomplete="off">
<input name="token" type="text" placeholder="请在这里填写token" autocomplete="off">
</div>
<div>
<button type="submit" class="btn">Save</button>
<a href="https://github.com/buunguyen/octotree#access-token" target="_blank" tabIndex="-1">Why is this required?</a>
<button type="submit" class="btn">保存</button>
<a href="https://github.com/buunguyen/octotree#access-token" target="_blank" tabIndex="-1">为什么需要填写token?</a>
</div>
<div class="error"></div>
</form>
</div>
<div class="octotree_view octotree_optsview">
<div class="octotree_view_header">Settings</div>
<div class="octotree_view_header">设置</div>
<form class="octotree_view_body">
<div>
<label>Site access token</label>
<label>站点token</label>
<a class="octotree_help" href="https://github.com/buunguyen/octotree#settings" target="_blank" tabIndex="-1">
<span></span>
</a>
@ -52,35 +52,25 @@
<div>
<div>
<label>Hotkeys</label>
<label>热键</label>
</div>
<input type="text" data-store="HOTKEYS">
</div>
<!-- @ifdef CHROME -->
<div class="octotree_github_only">
<div>
<label>GitHub Enterprise URLs</label>
</div>
<textarea data-store="GHEURLS" placeholder="https://github.mysite1.com__SPACES__https://github.mysite2.com">
</textarea>
</div>
<!-- @endif -->
<div>
<label><input type="checkbox" data-store="REMEMBER"> Remember sidebar visibility</label>
<label><input type="checkbox" data-store="REMEMBER"> 记住展开/收起状态</label>
</div>
<div>
<label><input type="checkbox" data-store="NONCODE"> Show in non-code pages</label>
<label><input type="checkbox" data-store="NONCODE"> 在没有代码的页面也显示</label>
</div>
<div class="octotree_github_only">
<label><input type="checkbox" data-store="LOADALL"> Load entire tree at once</label>
<label><input type="checkbox" data-store="LOADALL"> 自动加载</label>
</div>
<div>
<button type="submit" class="btn">Save</button>
<button type="submit" class="btn">保存</button>
</div>
</form>
</div>