├── .gitignore
├── Assets
├── Icon.sketch
└── Screenshot.png
├── Info.plist
├── LICENSE
├── README.md
├── Update.plist
├── dist.sh
├── github-highlight-selected.safariextz
├── icon-128.png
├── icon-48.png
├── icon-64.png
├── manifest.json
└── src
└── highlight-selected.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 |
--------------------------------------------------------------------------------
/Assets/Icon.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuclides/github-highlight-selected/f21457feb8c408fbc47670e1cb913825ff33fa1b/Assets/Icon.sketch
--------------------------------------------------------------------------------
/Assets/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuclides/github-highlight-selected/f21457feb8c408fbc47670e1cb913825ff33fa1b/Assets/Screenshot.png
--------------------------------------------------------------------------------
/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Author
6 | CAO Shan
7 | Builder Version
8 | 9537.85.10.17.1
9 | CFBundleDisplayName
10 | GitHub Highlight Selected
11 | CFBundleIdentifier
12 | com.Nuclides.github-highlight-selected
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleShortVersionString
16 | 1.3
17 | CFBundleVersion
18 | 5
19 | Chrome
20 |
21 | Content
22 |
23 | Scripts
24 |
25 | End
26 |
27 | src/highlight-selected.js
28 |
29 | Start
30 |
31 | src/jquery-2.1.1.min.js
32 |
33 |
34 | Stylesheets
35 |
36 | src/style.css
37 |
38 | Whitelist
39 |
40 | https://github.com/*
41 |
42 |
43 | Description
44 | Safari extension for highlighting selected word in GitHub source view like Sublime Text.
45 | DeveloperIdentifier
46 | 7676B49VAW
47 | ExtensionInfoDictionaryVersion
48 | 1.0
49 | Permissions
50 |
51 | Website Access
52 |
53 | Allowed Domains
54 |
55 | github.com
56 |
57 | Include Secure Pages
58 |
59 | Level
60 | Some
61 |
62 |
63 | Update Manifest URL
64 | https://raw.githubusercontent.com/Nuclides/github-highlight-selected/master/Update.plist
65 | Website
66 | https://github.com/Nuclides/github-highlight-selected
67 |
68 |
69 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 LIU Dongyuan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GitHub Highlight Selected
2 |
3 | [Download from Chrome App Store](https://chrome.google.com/webstore/detail/github-highlight-selected/lhiklbgjcblimmjjflobpncgihagcmbj)
4 |
5 | [Download Safari Extension](https://github.com/Nuclides/github-highlight-selected/blob/master/github-highlight-selected.safariextz?raw=true)
6 |
7 | Chrome extension for highlighting selected word in GitHub source view like Sublime Text.
8 |
9 |
10 |
11 | ## License
12 |
13 | MIT
14 |
--------------------------------------------------------------------------------
/Update.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Extension Updates
6 |
7 |
8 | CFBundleIdentifier
9 | com.Nuclides.github-highlight-selected
10 | Developer Identifier
11 | 7676B49VAW
12 | CFBundleVersion
13 | 5
14 | CFBundleShortVersionString
15 | 1.3
16 | URL
17 | https://github.com/Nuclides/github-highlight-selected/blob/master/github-highlight-selected.safariextz?raw=true
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/dist.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo Distributing the extension...
4 | zip -r ~/Desktop/github-highlight-selected.zip . -x '.*/*' '.*' 'Assets/*'
5 | echo Distribution file created at ~/Desktop/github-highlight-selected.zip
6 |
--------------------------------------------------------------------------------
/github-highlight-selected.safariextz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuclides/github-highlight-selected/f21457feb8c408fbc47670e1cb913825ff33fa1b/github-highlight-selected.safariextz
--------------------------------------------------------------------------------
/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuclides/github-highlight-selected/f21457feb8c408fbc47670e1cb913825ff33fa1b/icon-128.png
--------------------------------------------------------------------------------
/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuclides/github-highlight-selected/f21457feb8c408fbc47670e1cb913825ff33fa1b/icon-48.png
--------------------------------------------------------------------------------
/icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nuclides/github-highlight-selected/f21457feb8c408fbc47670e1cb913825ff33fa1b/icon-64.png
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "GitHub Highlight Selected",
3 | "version": "2.3",
4 | "manifest_version": 2,
5 |
6 | "description": "Highlight selected word in GitHub source view like Sublime Text",
7 | "icons": {
8 | "48": "icon-48.png",
9 | "128": "icon-128.png"
10 | },
11 |
12 | "content_scripts": [ {
13 | "js": [ "src/highlight-selected.js" ],
14 | "matches": [ "https://github.com/*", "https://gist.github.com/*" ]
15 | } ],
16 | "minimum_chrome_version": "20",
17 | "permissions": [
18 | ""
19 | ],
20 |
21 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
22 | }
23 |
--------------------------------------------------------------------------------
/src/highlight-selected.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('load', function() {
2 | var container = '.blob-wrapper, .js-blob-wrapper';
3 | var lastPage = location.href;
4 | var textNodes = [];
5 | var highlighted = [];
6 | var highlightedIndex;
7 |
8 | function wrapTextNodes(query) {
9 | // Why did all the gawd dam text selection stuff fail, urgggggghh....at least this worked!
10 | // http://cwestblog.com/2014/03/14/javascript-getting-all-text-nodes/
11 | function getTextNodesIn(elem, opt_fnFilter) {
12 | if (elem) {
13 | for (var nodes = elem.childNodes, i = nodes.length; i--;) {
14 | var node = nodes[i],
15 | nodeType = node.nodeType;
16 | if (nodeType == 3) {
17 | if (!opt_fnFilter || opt_fnFilter(node, elem)) {
18 | if (node.nodeValue.trim() !== '') {
19 | var span = document.createElement('span');
20 | span.textContent = node.nodeValue;
21 | node.parentNode.replaceChild(span, node);
22 | textNodes.push(span);
23 | }
24 | }
25 | } else if (nodeType == 1 || nodeType == 9 || nodeType == 11) {
26 | getTextNodesIn(node, opt_fnFilter);
27 | }
28 | }
29 | }
30 | }
31 | var elements = document.querySelectorAll(query);
32 | if (elements.length == 0) return;
33 | textNodes = [];
34 |
35 | for (var el of elements) {
36 | getTextNodesIn(el);
37 | }
38 |
39 | textNodes.reverse();
40 | }
41 |
42 | function observePullRequestDiffs(mutationObserverOptions) {
43 | var diffExpandMutationObserver = new MutationObserver(function(mutationRecords) {
44 | wrapTextNodes(container);
45 | });
46 | var diffTables = document.querySelectorAll('.diff-table > tbody');
47 | for (var diffTable of diffTables) {
48 | diffExpandMutationObserver.observe(diffTable, mutationObserverOptions);
49 | }
50 | }
51 |
52 | wrapTextNodes(container);
53 |
54 | // watch for page updating...
55 | var whatToObserve = {
56 | childList: true,
57 | attributes: false,
58 | subtree: false,
59 | attributeOldValue: false /*, attributeFilter: []*/
60 | };
61 | var mutationObserver = new MutationObserver(function(mutationRecords) {
62 | if (location.href != lastPage) {
63 | lastPage = location.href;
64 | wrapTextNodes(container);
65 | observePullRequestDiffs(whatToObserve);
66 | }
67 | });
68 | var codeContainer = document.querySelector('#js-repo-pjax-container');
69 | if (!codeContainer) codeContainer = document.querySelector('#gist-pjax-container'); // for gists
70 | if (!codeContainer) codeContainer = document.querySelector('main'); // It looks that GitHub has updated the DOM. Using `main` as a fallback.
71 | mutationObserver.observe(codeContainer, whatToObserve);
72 | observePullRequestDiffs(whatToObserve);
73 |
74 | function restore() {
75 | highlighted.forEach(function(el) {
76 | if (el.classList.contains('ghs-highlight')) el.classList.remove("ghs-highlight");
77 | else {
78 | var parent = el.parentNode;
79 | parent.replaceChild(el.childNodes[0], el);
80 | parent.normalize();
81 | }
82 | });
83 | highlighted = [];
84 | }
85 |
86 | function selectElement(el) {
87 | var range = document.createRange();
88 | range.selectNodeContents(el);
89 | var sel = window.getSelection();
90 | sel.removeAllRanges();
91 | sel.addRange(range);
92 | }
93 |
94 | function splitReplace(el, str, selectionIndex) {
95 | var strLength = str.length;
96 | var source = el.textContent;
97 | var pos = -1;
98 | var lastPos = 0;
99 | var result = document.createDocumentFragment();
100 | var selected;
101 | while ((pos = source.indexOf(str, pos + 1)) != -1) {
102 | if (pos > lastPos) result.appendChild(document.createTextNode(source.substring(lastPos, pos)));
103 | var highlight = document.createElement('span');
104 | highlight.textContent = source.substring(pos, pos + strLength);
105 | highlight.classList.add('ghs-partial-highlight');
106 | if (pos === selectionIndex) {
107 | selected = highlight;
108 | highlightedIndex = highlighted.length;
109 | }
110 | result.appendChild(highlight);
111 | highlighted.push(highlight);
112 | lastPos = pos + strLength;
113 | }
114 | result.appendChild(document.createTextNode(source.substring(lastPos)));
115 | el.replaceChild(result, el.childNodes[0]);
116 | if (selected) selectElement(selected);
117 | }
118 |
119 | var canvasDraggin = false;
120 |
121 | function barScrollToY(y){
122 | var iHieght = document.documentElement.clientHeight;
123 | var box = document.documentElement.getBoundingClientRect();
124 | var heightRatio = box.height / iHieght;
125 | var half= iHieght/2 ;
126 | window.scrollTo(window.pageXOffset,(y * heightRatio)-half);
127 | }
128 |
129 | document.body.addEventListener('mousedown', function(e) {
130 | if (e.target == canvas && e.which===1) {
131 | canvasDraggin = true;
132 | barScrollToY(e.clientY);
133 | window.addEventListener('mousemove', canvasDragger);
134 | return;
135 | }
136 | if (e.which != 1 || highlighted.length === 0) return;
137 | restore();
138 | canvas.style.display = 'none';
139 | });
140 |
141 | function canvasDragger(e) {
142 | if (!canvasDraggin) return;
143 | barScrollToY(e.clientY);
144 | e.preventDefault();
145 | return false;
146 | }
147 |
148 | document.body.addEventListener('mouseup', function(e) {
149 | if (e.which != 1) return;
150 | if (canvasDraggin){
151 | canvasDraggin = false;
152 | window.removeEventListener('mousemove', canvasDragger);
153 | return;
154 | }
155 | var selection = window.getSelection();
156 | var selected = selection.toString().trim();
157 |
158 | if (selected) {
159 | textNodes.forEach(function(el) {
160 | if (el.textContent == selected) {
161 | el.classList.add("ghs-highlight");
162 | if (el == e.target) highlightedIndex = highlighted.length;
163 | highlighted.push(el);
164 | } else if (el.textContent.indexOf(selected) > -1) {
165 | if (el != e.target) splitReplace(el, selected);
166 | else splitReplace(el, selected, Math.min(selection.anchorOffset, selection.focusOffset));
167 | }
168 |
169 | });
170 | updateHighlighter();
171 | }
172 | });
173 |
174 | window.addEventListener('keydown', function(e) {
175 |
176 | if (highlighted.length === 0 || !e.ctrlKey) return;
177 |
178 | if (e.keyCode == 38) { // down key
179 | highlightedIndex--;
180 | if (highlightedIndex < 0) highlightedIndex = highlighted.length - 1;
181 |
182 | } else if (e.keyCode == 40) { // up key
183 | highlightedIndex++;
184 | if (highlightedIndex >= highlighted.length) highlightedIndex = 0;
185 |
186 | } else return;
187 |
188 | selectElement(highlighted[highlightedIndex]);
189 | showElement(highlighted[highlightedIndex]);
190 | updateHighlighter();
191 | e.preventDefault();
192 | return false;
193 | });
194 |
195 | function showElement(el) {
196 | var rect = el.getBoundingClientRect();
197 | if (rect.bottom >= document.documentElement.clientHeight) el.scrollIntoView(false);
198 | else if (rect.top <= 0) el.scrollIntoView(true);
199 | }
200 |
201 | // Do the Highlighter bar on the right
202 | canvas = document.createElement("canvas");
203 |
204 | canvas.setAttribute('id', 'ghs-bar');
205 | var canvasUpdating = false;
206 | document.body.appendChild(canvas);
207 | var ctx = canvas.getContext('2d');
208 |
209 | function generateHighlighter() {
210 | var canvasHeight = window.document.documentElement.clientHeight; // Height to make the bar
211 | var heightRatio = canvasHeight / document.documentElement.getBoundingClientRect().height;
212 |
213 | canvas.style.display = 'block';
214 | canvas.height = canvasHeight;
215 | canvas.width = 20;
216 |
217 | var lastY = -1;
218 | var y = 0;
219 | highlighted.forEach(function(el, index) {
220 | var box = el.getBoundingClientRect();
221 | y = (((window.scrollY + box.top) * heightRatio) + 0.5) | 0;
222 | if (y == lastY && index != highlightedIndex) return;
223 |
224 | if (index == highlightedIndex) ctx.fillStyle = "rgba(54, 149, 230, 1)";
225 | else ctx.fillStyle = "rgba(241, 209, 47, 1)";
226 | ctx.fillRect(0, y, canvas.width, (((box.height * heightRatio) + 0.5) | 0) || 1);
227 | lastY = y;
228 | });
229 |
230 | var y1 = ((window.scrollY * heightRatio) + 0.5) | 0;
231 | var y2 = ((canvasHeight * heightRatio) + 0.5) | 0;
232 | ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
233 | ctx.fillRect(0, y1, canvas.width, y2);
234 | ctx.lineWidth = 1;
235 | ctx.strokeStyle = "rgba(204, 204, 204, 1)";
236 | ctx.strokeRect(0, y1, canvas.width, y2);
237 | canvasUpdating = false;
238 | }
239 |
240 | function updateHighlighter() {
241 | if (highlighted.length && canvasUpdating === false) {
242 | canvasUpdating = true;
243 | window.requestAnimationFrame(generateHighlighter);
244 | }
245 | }
246 |
247 | window.addEventListener('scroll', updateHighlighter);
248 | window.addEventListener('resize', updateHighlighter);
249 |
250 | // Add the css...with a content script this would be seperate but I put it here for dev purposes..this was made in the Snippets section of the dev tools Sources panel
251 | var css = document.createElement("style");
252 | css.type = "text/css";
253 | css.innerHTML = (function() {
254 | /*
255 | .ghs-highlight,
256 | .ghs-partial-highlight {
257 | outline: 1px solid rgba(255, 181, 21, .6);
258 | background-color: rgba(255, 181, 21, .3);
259 | }
260 |
261 | #ghs-bar {
262 | width: 16px;
263 | border-left: 1px solid #ccc;
264 | background-color: #f3f3f3;
265 | position: fixed;
266 | top: 0px;
267 | bottom: 0px;
268 | right: 0;
269 | height: 100%;
270 | display: none;
271 | }
272 | */
273 | }).toString().split('\n').slice(2, -2).join('\n').trim();
274 | document.body.appendChild(css);
275 | });
276 |
--------------------------------------------------------------------------------