├── LICENSE ├── README.md ├── github-better-header.js ├── header.html ├── manifest.json └── style.css /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 itchyny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Better Header 2 | ## We love the previous header! 3 | ![GitHub Better Header](https://raw.githubusercontent.com/wiki/itchyny/github-better-header/image.png) 4 | 5 | ## Why? 6 | - The Pull request and Issues links are welcome. 7 | - But we miss the profile link in the header. 8 | - There are some other projects which aim as same as this project, but their scripts are loaded after the contents are loaded. Thus the header gets better after a few hundreds of milliseconds. On the other hand, this extension replaces the elements before the document is loaded. 9 | 10 | ## Requirements 11 | - Google Chrome 12 | 13 | ## Installation 14 | - Clone this repository 15 | - Click Load unpacked extension button in the chrome://extensions/. 16 | 17 | ## Author 18 | itchyny (https://github.com/itchyny) 19 | 20 | ## License 21 | This software is released under the MIT License, see LICENSE. 22 | -------------------------------------------------------------------------------- /github-better-header.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | 3 | // Promise-like object of gathering variables 4 | function variablesPool(variables) { 5 | var pool = {}; 6 | var values = {}; 7 | var resolved = false; 8 | var callbacks = []; 9 | pool.set = function(name, value) { 10 | values[name] = value; 11 | if (Object.keys(values).length === variables.filter(function(v) { 12 | return !v.when || values.hasOwnProperty(v.when) && values[v.when]; 13 | }).length) { 14 | resolved = true; 15 | callbacks.forEach(function(callback) { 16 | callback(values); 17 | }); 18 | } 19 | }; 20 | pool.then = function(callback) { 21 | if (resolved) { 22 | callback(values); 23 | } else { 24 | callbacks.push(callback); 25 | } 26 | }; 27 | return pool; 28 | } 29 | 30 | // Mutation observer witness with the settings injected 31 | function witness(setting) { 32 | var varpool = variablesPool(setting.variables); 33 | var htmlpromise = setting.overwrites.map(function(overwrite) { 34 | return [ overwrite.fileName, fetch(chrome.extension.getURL(overwrite.fileName)).then(function(response) { return response.text(); }) ]; 35 | }).reduce(function (o, v) { o[v[0]] = v[1]; return o; }, {}); 36 | return function(mutations) { 37 | [].forEach.call(mutations, function(mutation) { 38 | [].forEach.call(mutation.addedNodes || [], function(node) { 39 | // we firstly check variables 40 | setting.variables.forEach(function(variable) { 41 | if (!variable.done && node.getAttribute && node.getAttribute(variable.attribute) 42 | && (node.getAttribute('name') === variable.name 43 | || node.className && node.className.toString().indexOf(variable.className) >= 0)) { 44 | varpool.set(variable.title, node.getAttribute(variable.attribute)); 45 | variable.done = true; 46 | } 47 | }); 48 | // then check whether or not we overwrite the element 49 | setting.overwrites.forEach(function(overwrite) { 50 | if (!overwrite.done && node.className && node.className.toString().indexOf(overwrite.className) >= 0) { 51 | node.style.display = 'none'; 52 | htmlpromise[overwrite.fileName].then(function(innerHTML) { 53 | varpool.then(function(variables) { 54 | if (!overwrite.when || overwrite.when.split(',').every(x => x.match('^(.*)=(.*)$') ? variables[RegExp.$1] === RegExp.$2 : variables[x])) { 55 | node.innerHTML = Object.keys(variables).reduce(function(innerHTML, name) { 56 | return innerHTML.replace(new RegExp('{{ *' + name + ' *}}', 'g'), variables[name]); 57 | }, innerHTML); 58 | } 59 | node.style.display = ''; 60 | }); 61 | }); 62 | overwrite.done = true; 63 | } 64 | }); 65 | }); 66 | }); 67 | // if all things done, stop observation 68 | if (setting.variables.every(function(variable) { return variable.done; }) 69 | && setting.overwrites.every(function(overwrite) { return overwrite.done; })) { 70 | this.disconnect(); 71 | } 72 | }; 73 | } 74 | 75 | // overwrite the page with the settings injected 76 | function start(setting) { 77 | var observer = new MutationObserver(witness(setting)); 78 | observer.observe(setting.target, setting.config); 79 | } 80 | 81 | // we start the process 82 | start({ 83 | target: document, 84 | config: { childList: true, subtree: true }, 85 | overwrites: [ 86 | { 87 | className: 'js-header-wrapper', 88 | fileName: 'header.html', 89 | when: 'user,hostname=github.com' 90 | } 91 | ], 92 | variables: [ 93 | { 94 | title: 'hostname', 95 | name: 'hostname', 96 | attribute: 'content' 97 | }, 98 | { 99 | title: 'user', 100 | name: 'user-login', 101 | attribute: 'content' 102 | }, 103 | { 104 | title: 'avatar', 105 | className: 'avatar', 106 | attribute: 'src', 107 | when: 'user' 108 | }, 109 | { 110 | title: 'search_action', 111 | className: 'js-site-search-form', 112 | attribute: 'action', 113 | when: 'user' 114 | }, 115 | { 116 | title: 'search_url', 117 | className: 'js-site-search-form', 118 | attribute: 'data-unscoped-search-url', 119 | when: 'user' 120 | }, 121 | { 122 | title: 'authenticity_token', 123 | name: 'authenticity_token', 124 | attribute: 'value', 125 | when: 'user' 126 | }, 127 | { 128 | title: 'form_nonce', 129 | name: 'html-safe-nonce', 130 | attribute: 'content', 131 | when: 'user' 132 | } 133 | ] 134 | }); 135 | 136 | })(); 137 | -------------------------------------------------------------------------------- /header.html: -------------------------------------------------------------------------------- 1 |
2 | 99 |
100 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "GitHub Better Header", 4 | "version": "0.0", 5 | "author": "itchyny", 6 | "content_scripts": [ { 7 | "all_frames": false, 8 | "js": [ "github-better-header.js" ], 9 | "css": [ "style.css" ], 10 | "run_at": "document_start", 11 | "matches": [ "https://github.com/*" ] 12 | } ], 13 | "web_accessible_resources": [ "*.html" ] 14 | } 15 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | html a, html .btn-link { 2 | color: #4078c0; 3 | } 4 | 5 | html .btn-link.add-line-comment { 6 | color: #ffffff; 7 | } 8 | 9 | html .link-gray-dark:hover { 10 | color: #4078c0!important; 11 | } 12 | 13 | html .state-open, html .state-proposed, html .state-reopened { 14 | background-color: #6cc644; 15 | } 16 | 17 | html body .btn-primary { 18 | background-color: #6cc644; 19 | background-image: linear-gradient(#91dd70, #55ae2e); 20 | border: 1px solid #5aad35; 21 | } 22 | 23 | html body .BtnGroup-item { 24 | border-right-width: 0; 25 | } 26 | 27 | html body .btn-primary:hover, html body .btn-primary:active { 28 | background-color: #55a532; 29 | background-image: linear-gradient(#85d063, #4f992f); 30 | border: 1px solid #519d30; 31 | background-position: 0 0; 32 | } 33 | 34 | html .branch-action-state-clean .branch-action-icon, html .completeness-indicator-success { 35 | background-color: #6cc644; 36 | } 37 | 38 | html .branch-action-state-clean .branch-action-body { 39 | border-color: #95c97e; 40 | } 41 | 42 | html .branch-action-state-clean .branch-action-body::before { 43 | border-right-color: #95c97e; 44 | } 45 | 46 | html .block-diff-added, html .text-green .block-diff-neutral { 47 | background-color: #55a532; 48 | } 49 | 50 | html .text-green { 51 | color: #55a532!important; 52 | } 53 | 54 | html .block-diff-deleted, html .text-red .block-diff-neutral { 55 | background-color: #bd2c00; 56 | } 57 | 58 | html .text-red { 59 | color: #bd2c00!important; 60 | } 61 | 62 | html .reponav-item.selected { 63 | border-color: #d26911 #e5e5e5 transparent; 64 | } 65 | 66 | .github-header .header { 67 | background-color: #f5f5f5!important; 68 | border-bottom: 1px solid #e5e5e5!important; 69 | padding: 10px 0px 8px!important; 70 | } 71 | 72 | .github-header .container { 73 | display: flex; 74 | } 75 | 76 | .github-header .header-logo-invertocat, .github-header .header-logo-wordmark, .github-header .header-nav-link { 77 | color: #333333!important; 78 | } 79 | 80 | .github-header .header-search-scope { 81 | color: #767676!important; 82 | border-right: 1px solid #eeeeee!important; 83 | } 84 | 85 | .github-header .header-search-wrapper { 86 | color: #333333!important; 87 | background-color: #ffffff!important; 88 | border: 1px solid #dddddd!important; 89 | } 90 | 91 | .github-header .header-search-input { 92 | box-shadow: none!important; 93 | min-height: 26px!important; 94 | } 95 | 96 | .form-control.focus { 97 | border-color: #51a7e8!important; 98 | box-shadow: inset 0 1px 2px rgba(0,0,0,0.075), 0 0 5px rgba(81,167,232,0.5)!important; 99 | } 100 | 101 | input.form-control::-webkit-input-placeholder { 102 | color: #767676!important; 103 | } 104 | 105 | .github-header .header-nav, .github-header .user-nav { 106 | list-style-type: none; 107 | display: flex; 108 | } 109 | 110 | .Header-link { 111 | color: #24292e!important; 112 | } 113 | 114 | .github-header .user-nav { 115 | margin-left: auto; 116 | } 117 | 118 | .github-header .user-nav .avatar { 119 | margin-top: 1px!important; 120 | } 121 | 122 | .github-header .header-nav-item { 123 | margin: 5px 7px!important; 124 | } 125 | 126 | .github-header .header-nav-item:first-child { 127 | margin-left: 15px!important; 128 | } 129 | 130 | .github-header .header-nav-item.create-new { 131 | padding-top: 2px!important; 132 | } 133 | 134 | .github-header .header-nav-item.create-new a:hover { 135 | text-decoration: none!important; 136 | } 137 | 138 | .github-header .zh-todo-link { 139 | display: none!important; 140 | } 141 | 142 | .dashboard .Details-content--shown { 143 | display: none!important; 144 | } 145 | 146 | .notifications .unread { 147 | font-weight: 400!important; 148 | } 149 | --------------------------------------------------------------------------------