Support BitBucket
This commit is contained in:
parent
c5cfdb4e52
commit
770e1e145f
|
@ -48,6 +48,11 @@ Octotree uses [GitHub API](https://developer.github.com/v3/) to retrieve reposit
|
|||
|
||||
When that happens, Octotree will ask for your [GitHub personal access token](https://help.github.com/articles/creating-an-access-token-for-command-line-use). If you don't already have one, [create one](https://github.com/settings/tokens/new), then copy and paste it into the textbox. Note that the minimal scopes that should be granted are `public_repo` and `repo` (if you need access to private repositories).
|
||||
|
||||
#### Bitbucket
|
||||
Octotree uses [Bitbucket API](https://confluence.atlassian.com/bitbucket/repositories-endpoint-1-0-296092719.html) to retrieve repository metadata. By defualt, Octotree will ask for your [Bitbucket App password](https://confluence.atlassian.com/bitbucket/app-passwords-828781300.html). If you don't already have one, [create one](https://bitbucket.org/account/admin/app-passwords) (the minimal requirement is `Repositories`'s `Read` permission), then copy and paste it into the textbox.
|
||||
|
||||
Note that Octotree extract your username from your current page by default for calling Bitbucket API. If fail to extract, Octotree will ask you for a token update, then you just need to prepend your username to the token, separated by a colon, i.e. `USERNAME:TOKEN`.
|
||||
|
||||
### Enterprise URLs
|
||||
By default, Octotree only works on `github.com`. To support enterprise version (Chrome and Opera only), you must grant Octotree sufficient permissions. Follow these steps to do so:
|
||||
|
||||
|
|
|
@ -173,6 +173,7 @@ function buildJs(overrides, ctx) {
|
|||
'./tmp/template.js',
|
||||
'./src/constants.js',
|
||||
'./src/adapters/adapter.js',
|
||||
'./src/adapters/bitbucket.js',
|
||||
'./src/adapters/github.js',
|
||||
'./src/view.help.js',
|
||||
'./src/view.error.js',
|
||||
|
|
|
@ -76,7 +76,7 @@ class Adapter {
|
|||
// encodes but retains the slashes, see #274
|
||||
const encodedPath = path.split('/').map(encodeURIComponent).join('/')
|
||||
item.a_attr = {
|
||||
href: `/${repo.username}/${repo.reponame}/${type}/${repo.branch}/${encodedPath}`
|
||||
href: this._getItemHref(repo, type, path)
|
||||
}
|
||||
}
|
||||
else if (type === 'commit') {
|
||||
|
@ -273,7 +273,7 @@ class Adapter {
|
|||
*/
|
||||
downloadFile(path, fileName) {
|
||||
const link = document.createElement('a')
|
||||
link.setAttribute('href', path.replace(/\/blob\//, '/raw/'))
|
||||
link.setAttribute('href', path.replace(/\/blob\/|\/src\//, '/raw/'))
|
||||
link.setAttribute('download', fileName)
|
||||
link.click()
|
||||
}
|
||||
|
@ -295,4 +295,95 @@ class Adapter {
|
|||
_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}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PjaxAdapter extends Adapter {
|
||||
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,
|
||||
})
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// @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
|
||||
})
|
||||
}
|
||||
else { // falls back
|
||||
super.selectFile(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
const BB_RESERVED_USER_NAMES = [
|
||||
'account', 'dashboard', 'integrations', 'product',
|
||||
'repo', 'snippets', 'support', 'whats-new'
|
||||
]
|
||||
const BB_RESERVED_REPO_NAMES = []
|
||||
const BB_RESERVED_TYPES = ['raw']
|
||||
const BB_404_SEL = '#error.404'
|
||||
const BB_PJAX_CONTAINER_SEL = '#source-container'
|
||||
|
||||
class Bitbucket extends PjaxAdapter {
|
||||
|
||||
constructor() {
|
||||
super(['jquery.pjax.js'])
|
||||
}
|
||||
|
||||
// @override
|
||||
init($sidebar) {
|
||||
const pjaxContainer = $(BB_PJAX_CONTAINER_SEL)[0]
|
||||
super.init($sidebar, {'pjaxContainer': pjaxContainer})
|
||||
}
|
||||
|
||||
// @override
|
||||
getCssClass() {
|
||||
return 'octotree_bitbucket_sidebar'
|
||||
}
|
||||
|
||||
// @override
|
||||
getCreateTokenUrl() {
|
||||
return `${location.protocol}//${location.host}/account/admin/app-passwords/new`
|
||||
}
|
||||
|
||||
// @override
|
||||
updateLayout(togglerVisible, sidebarVisible, sidebarWidth) {
|
||||
$('.octotree_toggle').css('right', sidebarVisible ? '' : -44)
|
||||
$('.aui-header').css('padding-left', sidebarVisible ? '' : 56)
|
||||
$('html').css('margin-left', sidebarVisible ? sidebarWidth : '')
|
||||
}
|
||||
|
||||
// @override
|
||||
getRepoFromPath(showInNonCodePage, currentRepo, token, cb) {
|
||||
|
||||
// 404 page, skip
|
||||
if ($(BB_404_SEL).length) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
// (username)/(reponame)[/(type)]
|
||||
const match = window.location.pathname.match(/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/)
|
||||
if (!match) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
const username = match[1]
|
||||
const reponame = match[2]
|
||||
const type = match[3]
|
||||
|
||||
// Not a repository, skip
|
||||
if (~BB_RESERVED_USER_NAMES.indexOf(username) ||
|
||||
~BB_RESERVED_REPO_NAMES.indexOf(reponame) ||
|
||||
~BB_RESERVED_TYPES.indexOf(type)) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
// Skip non-code page unless showInNonCodePage is true
|
||||
// with Bitbucket /username/repo is non-code page
|
||||
if (!showInNonCodePage &&
|
||||
(!type || (type && type !== 'src'))) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
// Get branch by inspecting page, quite fragile so provide multiple fallbacks
|
||||
const BB_BRANCH_SEL_1 = '.branch-dialog-trigger'
|
||||
|
||||
const branch =
|
||||
// Code page
|
||||
$(BB_BRANCH_SEL_1).attr('title') ||
|
||||
// Assume same with previously
|
||||
(currentRepo.username === username && currentRepo.reponame === reponame && currentRepo.branch) ||
|
||||
// Default from cache
|
||||
this._defaultBranch[username + '/' + reponame]
|
||||
|
||||
const repo = {username: username, reponame: reponame, branch: branch}
|
||||
|
||||
if (repo.branch) {
|
||||
cb(null, repo)
|
||||
}
|
||||
else {
|
||||
this._get('/main-branch', {repo, token}, (err, data) => {
|
||||
if (err) return cb(err)
|
||||
repo.branch = this._defaultBranch[username + '/' + reponame] = data.name || 'master'
|
||||
cb(null, repo)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// @override
|
||||
selectFile(path) {
|
||||
const $pjaxContainer = $(BB_PJAX_CONTAINER_SEL)
|
||||
super.selectFile(path, {'$pjaxContainer': $pjaxContainer})
|
||||
}
|
||||
|
||||
// @override
|
||||
loadCodeTree(opts, cb) {
|
||||
opts.path = opts.node.path
|
||||
this._loadCodeTree(opts, (item) => {
|
||||
if (!item.type) {
|
||||
item.type = 'blob'
|
||||
}
|
||||
}, cb)
|
||||
}
|
||||
|
||||
// @override
|
||||
_getTree(path, opts, cb) {
|
||||
this._get(`/src/${opts.repo.branch}/${path}`, opts, (err, res) => {
|
||||
if (err) return cb(err)
|
||||
const directories = res.directories.map((dir) => ({path: dir, type: 'tree'}))
|
||||
res.files.forEach((file) => {
|
||||
if (file.path.startsWith(res.path)) {
|
||||
file.path = file.path.substring(res.path.length)
|
||||
}
|
||||
})
|
||||
const tree = res.files.concat(directories)
|
||||
cb(null, tree)
|
||||
})
|
||||
}
|
||||
|
||||
// @override
|
||||
_getSubmodules(tree, opts, cb) {
|
||||
if (opts.repo.submodules) {
|
||||
return this._getSubmodulesInCurrentPath(tree, opts, cb)
|
||||
}
|
||||
|
||||
const item = tree.filter((item) => /^\.gitmodules$/i.test(item.path))[0]
|
||||
if (!item) return cb()
|
||||
|
||||
this._get(`/src/${opts.encodedBranch}/${item.path}`, opts, (err, res) => {
|
||||
if (err) return cb(err)
|
||||
// Memoize submodules so that they will be inserted into the tree later.
|
||||
opts.repo.submodules = parseGitmodules(res.data)
|
||||
this._getSubmodulesInCurrentPath(tree, opts, cb)
|
||||
})
|
||||
}
|
||||
|
||||
// @override
|
||||
_getSubmodulesInCurrentPath(tree, opts, cb) {
|
||||
const currentPath = opts.path
|
||||
const isInCurrentPath = currentPath
|
||||
? (path) => path.startsWith(`${currentPath}/`)
|
||||
: (path) => path.indexOf('/') === -1
|
||||
|
||||
const submodules = opts.repo.submodules
|
||||
const submodulesInCurrentPath = {}
|
||||
Object.keys(submodules).filter(isInCurrentPath).forEach((key) => {
|
||||
submodulesInCurrentPath[key] = submodules[key]
|
||||
})
|
||||
|
||||
// Insert submodules in current path into the tree because submodules can not
|
||||
// be retrieved with Bitbucket API but can only by reading .gitmodules.
|
||||
Object.keys(submodulesInCurrentPath).forEach((path) => {
|
||||
if (currentPath) {
|
||||
// `currentPath` is prefixed to `path`, so delete it.
|
||||
path = path.substring(currentPath.length + 1)
|
||||
}
|
||||
tree.push({path: path, type: 'commit'})
|
||||
})
|
||||
cb(null, submodulesInCurrentPath)
|
||||
}
|
||||
|
||||
// @override
|
||||
_get(path, opts, cb) {
|
||||
const host = location.protocol + '//' + 'api.bitbucket.org/1.0'
|
||||
const url = `${host}/repositories/${opts.repo.username}/${opts.repo.reponame}${path || ''}`
|
||||
const cfg = { url, method: 'GET', cache: false }
|
||||
|
||||
if (opts.token) {
|
||||
// Bitbucket App passwords can be used only for Basic Authentication.
|
||||
// Get username of logged-in user.
|
||||
let username = null, token = null
|
||||
|
||||
// Or get username by spliting token.
|
||||
if (opts.token.includes(':')) {
|
||||
const result = opts.token.split(':')
|
||||
username = result[0], token = result[1]
|
||||
}
|
||||
else {
|
||||
const currentUser = JSON.parse($('body').attr('data-current-user'))
|
||||
if (!currentUser || !currentUser.username) {
|
||||
return cb({
|
||||
error: 'Error: Invalid token',
|
||||
message: `Cannot retrieve your user name from the current page.
|
||||
Please update the token setting to prepend your user
|
||||
name to the token, separated by a colon, i.e. USERNAME:TOKEN`,
|
||||
needAuth: true
|
||||
})
|
||||
}
|
||||
username = currentUser.username, token = opts.token
|
||||
}
|
||||
cfg.headers = { Authorization: 'Basic ' + btoa(username + ':' + token) }
|
||||
}
|
||||
|
||||
$.ajax(cfg)
|
||||
.done((data) => cb(null, data))
|
||||
.fail((jqXHR) => {
|
||||
this._handleError(jqXHR, cb)
|
||||
})
|
||||
}
|
||||
|
||||
// @override
|
||||
_getItemHref(repo, type, encodedPath) {
|
||||
return `/${repo.username}/${repo.reponame}/src/${repo.branch}/${encodedPath}`
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
.octotree-show {
|
||||
.octotree_bitbucket_only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.octotree_bitbucket_sidebar {
|
||||
a.octotree_toggle {
|
||||
top: 9px;
|
||||
right: 5px;
|
||||
&:not(.octotree_loading) > span:after {
|
||||
content: data-uri('image/svg+xml;charset=UTF-8', './octicons/chevron-left.svg');
|
||||
}
|
||||
&:not(.octotree_loading):hover > span:after {
|
||||
content: data-uri('image/svg+xml;charset=UTF-8', './octicons/chevron-left-hover.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.split-diff .container {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.octotree_bitbucket_sidebar {
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3.01px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-variant: normal;
|
||||
font-weight: 400;
|
||||
height: 2.14285714em;
|
||||
line-height: 1.42857143;
|
||||
margin: 0;
|
||||
padding: 4px 10px;
|
||||
text-decoration: none;
|
||||
vertical-align: baseline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #e9e9e9;
|
||||
border-color: #999;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
padding-top: 49px;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.octotree_bitbucket_only {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.octotree_views {
|
||||
.octotree_view {
|
||||
.octotree_view_header {
|
||||
height: 49px;
|
||||
background-color: #f3f3f3;
|
||||
background-image: linear-gradient(#f9f9f9, #f3f3f3);
|
||||
background-repeat: repeat-x;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.octotree_help {
|
||||
& > span:before {
|
||||
content: data-uri('image/svg+xml;charset=UTF-8', './octicons/question.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.octotree_treeview {
|
||||
.octotree_header_repo {
|
||||
font-size: 13px;
|
||||
}
|
||||
.octotree_header_repo:before {
|
||||
font-family: 'Atlassian Icons';
|
||||
content: '\f135';
|
||||
color: #707070;
|
||||
}
|
||||
.octotree_header_branch {
|
||||
font-size: 11px;
|
||||
}
|
||||
.octotree_header_branch:before {
|
||||
font-family: 'Atlassian Icons';
|
||||
content: '\f127';
|
||||
color: #707070;
|
||||
}
|
||||
.jstree-icon.tree:before {
|
||||
content: '\f131';
|
||||
color: #707070;
|
||||
}
|
||||
.jstree-icon.blob:before {
|
||||
content: '\f12e';
|
||||
color: #707070;
|
||||
}
|
||||
.jstree-node.jstree-leaf:hover {
|
||||
.jstree-icon.blob {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.jstree-icon.blob:before {
|
||||
content: data-uri('image/svg+xml;charset=UTF-8', './octicons/cloud-download.svg');
|
||||
}
|
||||
}
|
||||
.jstree-icon.commit:before {
|
||||
content: '\f139';
|
||||
color: #707070;
|
||||
}
|
||||
.jstree-anchor {
|
||||
color: #3572b0 !important;
|
||||
& > span {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
.jstree-default {
|
||||
.jstree-wholerow-hovered {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
.jstree-wholerow-clicked {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
}
|
||||
.jstree-icon.tree, .jstree-icon.blob, .jstree-icon.commit {
|
||||
font: normal normal 16px 'Atlassian Icons';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.octotree_toggle, a.octotree_opts {
|
||||
color: black !important;
|
||||
|
||||
&:hover, &.selected {
|
||||
color: #4183C4 !important;
|
||||
}
|
||||
}
|
||||
|
||||
a.octotree_opts {
|
||||
top: 16px;
|
||||
right: 38px;
|
||||
width: 14px;
|
||||
height: 16px;
|
||||
background: data-uri('image/svg+xml;charset=UTF-8', './octicons/gear.svg');
|
||||
&:hover {
|
||||
background: data-uri('image/svg+xml;charset=UTF-8', './octicons/gear-hover.svg');
|
||||
}
|
||||
}
|
||||
|
||||
a.octotree_toggle {
|
||||
top: 5px;
|
||||
right: -44px;
|
||||
|
||||
&:not(.octotree_loading) > span:after {
|
||||
content: data-uri('image/svg+xml;charset=UTF-8', './octicons/chevron-right.svg');
|
||||
}
|
||||
&:not(.octotree_loading):hover > span:after {
|
||||
content: data-uri('image/svg+xml;charset=UTF-8', './octicons/chevron-right-hover.svg');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,22 +16,16 @@ const GH_PJAX_CONTAINER_SEL = '#js-repo-pjax-container, .context-loader-containe
|
|||
const GH_CONTAINERS = '.container, .container-responsive'
|
||||
const GH_RAW_CONTENT = 'body > pre'
|
||||
|
||||
class GitHub extends Adapter {
|
||||
class GitHub extends PjaxAdapter {
|
||||
|
||||
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
|
||||
init($sidebar) {
|
||||
super.init($sidebar)
|
||||
|
||||
if (!window.MutationObserver) return
|
||||
const pjaxContainer = $(GH_PJAX_CONTAINER_SEL)[0]
|
||||
super.init($sidebar, {'pjaxContainer': pjaxContainer})
|
||||
|
||||
// Fix #151 by detecting when page layout is updated.
|
||||
// In this case, split-diff page has a wider layout, so need to recompute margin.
|
||||
|
@ -50,46 +44,6 @@ class GitHub extends Adapter {
|
|||
attributeFilter: ['class'],
|
||||
attributeOldValue: true
|
||||
})
|
||||
|
||||
// GitHub 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)
|
||||
})
|
||||
|
||||
const pjaxContainer = $(GH_PJAX_CONTAINER_SEL)[0]
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// @override
|
||||
|
@ -180,17 +134,7 @@ class GitHub extends Adapter {
|
|||
// @override
|
||||
selectFile(path) {
|
||||
const $pjaxContainer = $(GH_PJAX_CONTAINER_SEL)
|
||||
|
||||
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)
|
||||
}
|
||||
super.selectFile(path, {'$pjaxContainer': $pjaxContainer})
|
||||
}
|
||||
|
||||
// @override
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"128": "icons/icon128.png"
|
||||
},
|
||||
"permissions": [
|
||||
"https://bitbucket.org/*",
|
||||
"https://github.com/*",
|
||||
"storage"
|
||||
],
|
||||
|
|
|
@ -2,7 +2,7 @@ const data = require('sdk/self').data
|
|||
const pageMod = require('sdk/page-mod')
|
||||
|
||||
pageMod.PageMod({
|
||||
include: ['https://github.com/*'],
|
||||
include: ['https://bitbucket.org/*', 'https://github.com/*'],
|
||||
contentScriptFile : [data.url('jquery.js'),
|
||||
data.url('jquery-ui.js'),
|
||||
data.url('jstree.js'),
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
"version": "$VERSION",
|
||||
"permissions": {
|
||||
"cross-domain-content": [
|
||||
"https://api.bitbucket.org",
|
||||
"https://api.github.com",
|
||||
"https://bitbucket.org",
|
||||
"https://github.com"
|
||||
],
|
||||
"private-browsing": true,
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
</array>
|
||||
<key>Whitelist</key>
|
||||
<array>
|
||||
<string>https://bitbucket.org/*</string>
|
||||
<string>https://github.com/*</string>
|
||||
</array>
|
||||
</dict>
|
||||
|
@ -52,6 +53,7 @@
|
|||
<dict>
|
||||
<key>Allowed Domains</key>
|
||||
<array>
|
||||
<string>bitbucket.org</string>
|
||||
<string>github.com</string>
|
||||
</array>
|
||||
<key>Include Secure Pages</key>
|
||||
|
|
|
@ -11,7 +11,20 @@ $(document).ready(() => {
|
|||
}
|
||||
|
||||
function createAdapter() {
|
||||
const normalizeUrl = (url) => url.replace(/(.*?:\/\/[^/]+)(.*)/, '$1')
|
||||
|
||||
const githubUrls = store.get(STORE.GHEURLS).split(/\n/)
|
||||
.map(normalizeUrl)
|
||||
.concat('https://github.com')
|
||||
|
||||
const bitbucketUrls = ['https://bitbucket.org']
|
||||
const currentUrl = `${location.protocol}//${location.host}`
|
||||
|
||||
if (~githubUrls.indexOf(currentUrl)) {
|
||||
return new GitHub(store)
|
||||
} else if (~bitbucketUrls.indexOf(currentUrl)) {
|
||||
return new Bitbucket(store)
|
||||
}
|
||||
}
|
||||
|
||||
function loadExtension() {
|
||||
|
@ -110,7 +123,8 @@ $(document).ready(() => {
|
|||
}
|
||||
|
||||
if (isSidebarVisible()) {
|
||||
const repoChanged = JSON.stringify(repo) !== JSON.stringify(currRepo)
|
||||
const replacer = ['username', 'reponame', 'branch']
|
||||
const repoChanged = JSON.stringify(repo, replacer) !== JSON.stringify(currRepo, replacer)
|
||||
|
||||
if (repoChanged || reload === true) {
|
||||
$document.trigger(EVENT.REQ_START)
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
@import "base";
|
||||
@import "../adapters/bitbucket";
|
||||
@import "../adapters/github";
|
||||
|
|
|
@ -49,6 +49,7 @@ class OptionsView {
|
|||
*/
|
||||
// @ifdef CHROME
|
||||
const $ta = this.$view.find('[data-store$=EURLS]').filter(':visible')
|
||||
if ($ta.length > 0) {
|
||||
const storeKey = $ta.data('store')
|
||||
const urls = $ta.val().split(/\n/).filter((url) => url !== '')
|
||||
|
||||
|
@ -62,6 +63,7 @@ class OptionsView {
|
|||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
// @endif
|
||||
|
||||
return this._saveOptions()
|
||||
|
|
Loading…
Reference in New Issue