diff --git a/src/adapter.github.js b/src/adapter.github.js index 579459c..e7c4766 100644 --- a/src/adapter.github.js +++ b/src/adapter.github.js @@ -161,17 +161,19 @@ GitHub.prototype.getRepoFromPath = function(showInNonCodePage, currentRepo, toke /** * Retrieves the code tree of a repository. - * @param {Object} repo - the repo whose code tree is to be retrieved. - * @param {String} token - the access token. + * @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.getCodeTree = function(repo, token, cb) { - var self = this - , folders = { '': [] } +GitHub.prototype.getCodeTree = function(opts, cb) { + var self = this + , folders = { '': [] } + , repo = opts.repo + , token = opts.token , encodedBranch = encodeURIComponent(decodeURIComponent(repo.branch)) - , $dummyDiv = $('
') + , $dummyDiv = $('
') - 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) { @@ -192,6 +194,10 @@ GitHub.prototype.getCodeTree = function(repo, token, 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('/') @@ -201,10 +207,16 @@ GitHub.prototype.getCodeTree = function(repo, token, 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') { diff --git a/src/chrome/manifest.json b/src/chrome/manifest.json index a0c676a..fee00e8 100755 --- a/src/chrome/manifest.json +++ b/src/chrome/manifest.json @@ -1,6 +1,6 @@ { "name": "Octotree", - "version": "1.7.2", + "version": "2.0.0", "manifest_version": 2, "author": "Buu Nguyen", "description": "Display GitHub code in tree format", diff --git a/src/constants.js b/src/constants.js index 28ee744..b654d47 100644 --- a/src/constants.js +++ b/src/constants.js @@ -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' } \ No newline at end of file diff --git a/src/firefox/package.json b/src/firefox/package.json index 45bce9a..99664cc 100644 --- a/src/firefox/package.json +++ b/src/firefox/package.json @@ -11,7 +11,7 @@ "icon": "data/icons/icon48.png", "icon64": "data/icons/icon64.png", "license": "MIT", - "version": "1.7.2", + "version": "2.0.0", "permissions": { "cross-domain-content": ["https://api.github.com", "https://github.com"] } diff --git a/src/octotree.js b/src/octotree.js index f9ba5b6..e73a08c 100755 --- a/src/octotree.js +++ b/src/octotree.js @@ -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) @@ -108,12 +114,9 @@ $(document).ready(function() { if (repoChanged || reload === true) { $document.trigger(EVENT.REQ_START) currRepo = repo + treeView.showHeader(repo) - - adapter.getCodeTree(repo, token, function(err, tree) { - if (err) errorView.show(err) - else treeView.show(repo, tree) - }) + treeView.show(repo, token) } else treeView.syncSelection() } diff --git a/src/octotree.less b/src/octotree.less index 46bb885..dff7e21 100755 --- a/src/octotree.less +++ b/src/octotree.less @@ -202,6 +202,11 @@ label { font-weight: normal !important; } + + label.disabled { + color: gray; + } + input[type=text], textarea { width: 100%; } diff --git a/src/safari/Info.plist b/src/safari/Info.plist index 024a5d1..594863a 100755 --- a/src/safari/Info.plist +++ b/src/safari/Info.plist @@ -13,9 +13,9 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleShortVersionString - 1.7.2 + 2.0.0 CFBundleVersion - 1.7.2 + 2.0.0 Chrome Content diff --git a/src/template.html b/src/template.html index d82831b..9a2b29b 100644 --- a/src/template.html +++ b/src/template.html @@ -53,7 +53,10 @@
- + +
+
+
diff --git a/src/view.options.js b/src/view.options.js index e871803..351618b 100644 --- a/src/view.options.js +++ b/src/view.options.js @@ -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() }, @@ -91,4 +109,4 @@ function OptionsView($dom, store) { completeFn ) } -} \ No newline at end of file +} diff --git a/src/view.tree.js b/src/view.tree.js index 1782952..1e9bba5 100644 --- a/src/view.tree.js +++ b/src/view.tree.js @@ -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) }