├── .jshintignore
├── .gitignore
├── .npmignore
├── .jshint
├── userscript.header.ejs
├── lib
├── core
│ ├── plugins.js
│ ├── storage.js
│ └── utils.js
├── plugins
│ ├── repo-counts.js
│ ├── twitter-link.js
│ ├── pull-request-links.js
│ ├── gh-pages-link.js
│ ├── fork-count.js
│ ├── settings.js
│ └── repo-filter-info.js
├── index.js
└── extras
│ └── ay-pie-chart.js
├── package.json
├── .gitmodules
├── README.md
├── gulpfile.js
└── plugins-invalid
├── editor-theme.js
└── code-search.js
/.jshintignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .git*
2 | _ignore/
3 | build/
4 | test/
5 | .DS_Store
6 | .npm-debug.log
7 | .project
8 | .travis.yml
9 | TODO.md
10 |
--------------------------------------------------------------------------------
/.jshint:
--------------------------------------------------------------------------------
1 | {
2 | "curly": false,
3 | "noempty": true,
4 | "newcap": false,
5 | "eqeqeq": true,
6 | "eqnull": true,
7 | "undef": true,
8 | "devel": true,
9 | "node": true,
10 | "browser": true,
11 | "evil": false,
12 | "latedef": true,
13 | "nonew": true,
14 | "trailing": true,
15 | "immed": true,
16 | "smarttabs": true,
17 | "strict": true,
18 | "globals": {
19 | "define": true
20 | }
21 | }
--------------------------------------------------------------------------------
/userscript.header.ejs:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name GitHub Enhancement Suite
3 | // @namespace https://github.com/skratchdot/github-enhancement-suite
4 | // @description <%= description %>
5 | // @include https://github.com/*
6 | // @match https://github.com/*
7 | // @run-at document-end
8 | // @icon http://skratchdot.com/favicon.ico
9 | // @downloadURL https://github.com/skratchdot/github-enhancement-suite/raw/master/enhancement-suite.user.js
10 | // @updateURL https://github.com/skratchdot/github-enhancement-suite/raw/master/enhancement-suite.user.js
11 | // @version <%= version %>
12 | // ==/UserScript==
13 |
--------------------------------------------------------------------------------
/lib/core/plugins.js:
--------------------------------------------------------------------------------
1 | /* autogenerated by running `gulp plugins` (see gulpfile.js) */
2 | "use strict";
3 | exports["fork-count"] = require("../plugins/fork-count");
4 | exports["gh-pages-link"] = require("../plugins/gh-pages-link");
5 | exports["pull-request-links"] = require("../plugins/pull-request-links");
6 | exports["repo-counts"] = require("../plugins/repo-counts");
7 | exports["repo-filter-info"] = require("../plugins/repo-filter-info");
8 | exports.settings = require("../plugins/settings");
9 | exports["twitter-link"] = require("../plugins/twitter-link");
10 | exports.pluginNames = ["fork-count","gh-pages-link","pull-request-links","repo-counts","repo-filter-info","settings","twitter-link"];
11 |
--------------------------------------------------------------------------------
/lib/plugins/repo-counts.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 |
4 | exports.name = 'Repo Counts';
5 | exports.description = 'A user script to display repo counts when browsing Github repository pages.';
6 | exports.enabledSelector = 'body.page-profile .tabnav-tab.selected:contains("Repositories")';
7 |
8 | exports.onPage = function () {
9 | // Make input filter smaller when the "new repo" button exists
10 | if (jQuery('body.page-profile .filter-bar a.new-repo').length > 0) {
11 | jQuery('#your-repos-filter').css('width', '180px');
12 | }
13 | jQuery('.page-profile ul.repo_filterer li a').each(function () {
14 | try {
15 | var elem = jQuery(this),
16 | selector = elem.data('filter'),
17 | elements = jQuery('ul.js-repo-list').find('li' + selector);
18 | elem.append(' (' + elements.size() + ')');
19 | elem.css('font-size', '11px');
20 | } catch (e) {}
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/lib/core/storage.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var util = require('util');
3 | var getPath = require('object-path-get');
4 | var setPath = require('object-path-set');
5 | var rootKey = 'GES.by.skratchdot';
6 |
7 | var getRoot = function () {
8 | try {
9 | var root = JSON.parse(window.localStorage.getItem(rootKey));
10 | if (!util.isObject(root)) {
11 | root = {};
12 | }
13 | return root;
14 | } catch (e) {
15 | return {};
16 | }
17 | };
18 |
19 | exports.get = function (key, defaultValue) {
20 | var root = getRoot();
21 | return getPath(root, key, defaultValue);
22 | };
23 |
24 | exports.set = function (key, value) {
25 | var root = getRoot();
26 | root = setPath(root, key, value);
27 | window.localStorage.setItem(rootKey, JSON.stringify(root));
28 | };
29 |
30 | exports.test = function () {
31 | var key = 'localStorageTest';
32 | var value = Date.now();
33 | exports.set(key, value);
34 | return value === exports.get(key, null);
35 | };
36 |
--------------------------------------------------------------------------------
/lib/plugins/twitter-link.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 | var utils = require('../core/utils');
4 |
5 | exports.name = 'Twitter Link';
6 | exports.description = 'Adds a link to Twitter on a user\'s profile page.';
7 | exports.enabledSelector = 'body.page-profile';
8 |
9 | exports.onPage = function () {
10 | var username, $link, twitterSection;
11 | if (jQuery('#skratchdot-twitter-section').length === 0) {
12 | username = utils.getCurrentAuthor();
13 | $link = jQuery('')
14 | .attr('href', '//twitter.com/' + encodeURIComponent(username))
15 | .text('@' + username);
16 | twitterSection = '
';
20 | jQuery('.column.vcard:first ul.vcard-details:first').append(twitterSection);
21 | jQuery('#skratchdot-twitter-link').append($link);
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/lib/plugins/pull-request-links.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 | var utils = require('../core/utils');
4 |
5 | exports.name = 'Pull Request Links';
6 | exports.description = 'A user script to "linkify" the to/from branches on Pull Request pages.';
7 | exports.enabledSelector = '.commit-ref:not(.editor-expander)';
8 |
9 | exports.onPage = function () {
10 | jQuery(exports.enabledSelector).css('cursor', 'pointer').click(function () {
11 | var repo = utils.getCurrentRepo(),
12 | commitInfo = jQuery(this).text().trim().split(':');
13 | console.log(repo, commitInfo);
14 | // When pull requests are coming from the same account, we need to make sure
15 | // commitInfo[0] is the account, and commitInfo[1] is the branch name.
16 | if (commitInfo.length === 1) {
17 | commitInfo = [utils.getCurrentAuthor(), commitInfo[0]];
18 | }
19 | if (repo.length > 0 && commitInfo.length === 2) {
20 | document.location = '/' + commitInfo[0] + '/' + repo + '/tree/' + commitInfo[1];
21 | }
22 | });
23 | };
24 |
--------------------------------------------------------------------------------
/lib/core/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 | var url = require('url');
4 | var storage = require('./storage');
5 |
6 | exports.getCurrentAuthorAndRepo = function () {
7 | var parts = (url.parse(document.location.toString()).pathname || '').split('/');
8 | return {
9 | author: parts[1] || '',
10 | repo: parts[2] || ''
11 | };
12 | };
13 |
14 | exports.getCurrentRepo = function () {
15 | return exports.getCurrentAuthorAndRepo().repo;
16 | };
17 |
18 | exports.getCurrentAuthor = function () {
19 | return exports.getCurrentAuthorAndRepo().author;
20 | };
21 |
22 | exports.isPluginEnabled = function (pluginName) {
23 | // plugins are enabled by default
24 | if (typeof storage.get('enabled.' + pluginName) !== 'boolean') {
25 | storage.set('enabled.' + pluginName, true);
26 | }
27 | return storage.get('enabled.' + pluginName, true);
28 | };
29 |
30 | exports.enablePlugin = function (pluginName, enabled) {
31 | storage.set('enabled.' + pluginName, enabled);
32 | };
33 |
34 | exports.togglePluginEnabled = function (pluginName) {
35 | exports.enablePlugin(pluginName, !exports.isPluginEnabled(pluginName));
36 | };
37 |
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-enhancement-suite",
3 | "version": "2.0.2",
4 | "description": "A collection of userscripts to add functionality when browsing github.com",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "gulp test"
8 | },
9 | "author": "skratchdot",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/skratchdot/github-enhancement-suite/issues"
13 | },
14 | "homepage": "https://github.com/skratchdot/github-enhancement-suite",
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/skratchdot/github-enhancement-suite"
18 | },
19 | "devDependencies": {
20 | "browserify": "^11.0.1",
21 | "d3": "^3.5.6",
22 | "ejs": "^2.3.3",
23 | "gulp": "^3.9.0",
24 | "gulp-insert": "^0.5.0",
25 | "gulp-nodeunit": "0.0.5",
26 | "gulp-rename": "^1.2.2",
27 | "gulp-size": "^2.0.0",
28 | "gulp-uglify": "^1.2.0",
29 | "jquery": "^2.1.4",
30 | "jsxhint": "^0.15.1",
31 | "lodash.debounce": "^3.1.1",
32 | "lodash.throttle": "^3.0.4",
33 | "mutation-summary": "0.0.0",
34 | "object-path-get": "0.0.2",
35 | "object-path-set": "0.0.1",
36 | "react": "^0.13.3",
37 | "reactify": "^1.1.1",
38 | "through2": "^2.0.0",
39 | "vinyl-transform": "^1.0.0"
40 | },
41 | "keywords": [
42 | "github",
43 | "extension",
44 | "browser",
45 | "enhancement"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var storage = require('./core/storage');
3 | var $ = require('jquery');
4 | var plugins = require('./core/plugins');
5 | var utils = require('./core/utils');
6 | var handleOnPage;
7 |
8 | // make sure storage works
9 | if (!storage.test()) {
10 | console.warn('Github Enhancement Suite cannot use localStorage' +
11 | 'and may not work properly.');
12 | }
13 |
14 | // loop through each plugin calling onPage() if it's enabled and the enabledSelector matches
15 | handleOnPage = function () {
16 | plugins.pluginNames.forEach(function (plugin) {
17 | if (utils.isPluginEnabled(plugin) &&
18 | $(plugins[plugin].enabledSelector).length &&
19 | typeof plugins[plugin].onPage === 'function') {
20 | console.log('Firing onPage() for plugin: ' + plugin + ' at ' + Date.now());
21 | plugins[plugin].onPage();
22 | }
23 | });
24 | };
25 |
26 | // handle regular page loads
27 | handleOnPage();
28 |
29 | // handle pjax pages
30 | (function () {
31 | var pjaxActive = false;
32 | var observer = new MutationObserver(function (mutations) {
33 | if ($('.pjax-active').length) {
34 | pjaxActive = true;
35 | } else if (pjaxActive) {
36 | pjaxActive = false;
37 | // do something
38 | setImmediate(function () {
39 | handleOnPage();
40 | });
41 | }
42 | });
43 | observer.observe(document, {
44 | attributes: true,
45 | childList: true,
46 | characterData: true,
47 | characterDataOldValue: true,
48 | subtree: true
49 | });
50 | }());
51 |
--------------------------------------------------------------------------------
/lib/plugins/gh-pages-link.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 | var utils = require('../core/utils');
4 |
5 | exports.name = 'Github Pages Link';
6 | exports.description = 'If a repository has a gh-pages branch, then this will add links to the Github Page, as well as the gh-page source code.';
7 | exports.enabledSelector = '.repo-container .repository-meta.js-details-container';
8 |
9 | exports.onPage = function () {
10 | var data = utils.getCurrentAuthorAndRepo(),
11 | ghPageLink, ghPageSourceLink;
12 | if (data.author !== '' && data.repo !== '') {
13 | if (jQuery('[data-tab-filter="branches"] [data-name="gh-pages"]').length > 0) {
14 | ghPageLink = 'http://' + data.author + '.github.io/' + data.repo;
15 | ghPageSourceLink = 'https://github.com/' + data.author + '/' +
16 | data.repo + '/tree/gh-pages';
17 | if (jQuery('#skratchdot-gh-pages-container').length === 0) {
18 | jQuery(exports.enabledSelector).append('' +
19 | '
gh-pages:' +
20 | '
' +
21 | '
• ' +
22 | '
[gh-pages source]' +
23 | '
');
24 | // Fix html
25 | jQuery('#skratchdot-gh-pages-link').attr('href', ghPageLink).text(ghPageLink);
26 | jQuery('#skratchdot-gh-pages-link-source').attr('href', ghPageSourceLink);
27 | }
28 | } else {
29 |
30 | }
31 |
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "repos/github-code-search.user.js"]
2 | path = repos/github-code-search.user.js
3 | url = git@github.com:skratchdot/github-code-search.user.js.git
4 | [submodule "repos/github-editor-theme.user.js"]
5 | path = repos/github-editor-theme.user.js
6 | url = git@github.com:skratchdot/github-editor-theme.user.js.git
7 | [submodule "repos/github-fork-count.user.js"]
8 | path = repos/github-fork-count.user.js
9 | url = git@github.com:skratchdot/github-fork-count.user.js.git
10 | [submodule "repos/github-get-missing-descriptions.user.js"]
11 | path = repos/github-get-missing-descriptions.user.js
12 | url = git@github.com:skratchdot/github-get-missing-descriptions.user.js.git
13 | [submodule "repos/github-gh-pages-link.user.js"]
14 | path = repos/github-gh-pages-link.user.js
15 | url = git@github.com:skratchdot/github-gh-pages-link.user.js.git
16 | [submodule "repos/github-pull-request-links.user.js"]
17 | path = repos/github-pull-request-links.user.js
18 | url = git@github.com:skratchdot/github-pull-request-links.user.js.git
19 | [submodule "repos/github-repo-counts.user.js"]
20 | path = repos/github-repo-counts.user.js
21 | url = git@github.com:skratchdot/github-repo-counts.user.js.git
22 | [submodule "repos/github-repo-filter-info.user.js"]
23 | path = repos/github-repo-filter-info.user.js
24 | url = git@github.com:skratchdot/github-repo-filter-info.user.js.git
25 | [submodule "repos/github-twitter-link.user.js"]
26 | path = repos/github-twitter-link.user.js
27 | url = git@github.com:skratchdot/github-twitter-link.user.js.git
28 | [submodule "scripts"]
29 | path = scripts
30 | url = git@gist.github.com:5604120.git
31 |
--------------------------------------------------------------------------------
/lib/plugins/fork-count.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 |
4 | exports.name = 'Fork Count';
5 | exports.description = 'Display repo counts (public, private, sources, forks, mirrors) on the profile page underneath a users followers/starred/following count';
6 | exports.enabledSelector = 'body.page-profile .tabnav-tab.selected';
7 |
8 | exports.onPage = function () {
9 | // Initial our variables (and jQuery selectors)
10 | var countRepos = 0,
11 | countPublic = 0,
12 | countPrivate = 0,
13 | countSources = 0,
14 | countForks = 0,
15 | countMirrors = 0,
16 | repoList = jQuery('ul.js-repo-list > li'),
17 | statsContainer = jQuery('.column.vcard:first .vcard-stats'),
18 | stats;
19 |
20 | // insert our container
21 | if (jQuery('#skratchdot-fork-count').length === 0) {
22 | statsContainer.append('');
23 | statsContainer.append('');
24 | }
25 | stats = jQuery('#skratchdot-fork-count');
26 | if (!stats.hasClass('stats-populated') && repoList.length > 0) {
27 | // Loop through all repos, looking for public forks
28 | repoList.each(function () {
29 | try {
30 | var elem = jQuery(this);
31 | countRepos = countRepos + 1;
32 | if (elem.hasClass('public')) {
33 | countPublic = countPublic + 1;
34 | }
35 | if (elem.hasClass('private')) {
36 | countPrivate = countPrivate + 1;
37 | }
38 | if (elem.hasClass('source')) {
39 | countSources = countSources + 1;
40 | }
41 | if (elem.hasClass('fork')) {
42 | countForks = countForks + 1;
43 | }
44 | if (elem.hasClass('mirror')) {
45 | countMirrors = countMirrors + 1;
46 | }
47 | } catch (e) {}
48 | });
49 | stats.html('' + countPublic + ' public, ' +
50 | countPrivate + ' private, ' +
51 | countSources + ' sources, ' +
52 | countForks + ' forks' +
53 | (countMirrors > 0 ? '' + countMirrors + ' mirrors' : '')
54 | );
55 | stats.addClass('stats-populated');
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Github Enhancement Suite
2 | ========================
3 |
4 | ### Description ###
5 |
6 | The [Github Enhancement Suite](https://github.com/skratchdot/github-enhancement-suite)
7 | is a collection of userscripts to add functionality
8 | when browsing [github.com](https://github.com/).
9 |
10 |
11 | ### Installation ###
12 |
13 | 1. Make sure you have user scripts enabled in your browser (these instructions refer to the latest versions of the browsers):
14 | * ***CHROME***: Install [TamperMonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo). Continue to STEP 2.
15 | * ***FIREFOX***: Install [GreaseMonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/). Continue to STEP 2.
16 | * ***IE***: Install [Trixie](http://www.bhelpuri.net/Trixie/). Continue to STEP 2.
17 | * ***OPERA***: Follow instructions located on Opera's site: [User JS](http://www.opera.com/docs/userjs/). Continue to STEP 2.
18 | * ***SAFARI***: Install [NinjaKit](http://d.hatena.ne.jp/os0x/20100612/1276330696). Continue to STEP 2.
19 | 2. Install the "GitHub Enhancement Suite" user script by clicking here: [github-enhancement-suite](https://github.com/skratchdot/github-enhancement-suite/raw/master/build/github-enhancement-suite.user.js).
20 |
21 |
22 | ### Included Userscripts ###
23 |
24 | - [Github: Code Search](https://github.com/skratchdot/github-code-search.user.js)
25 | - [Github: Editor Theme](https://github.com/skratchdot/github-editor-theme.user.js)
26 | - [Github: Fork Count](https://github.com/skratchdot/github-fork-count.user.js)
27 | - [Github: Get Missing Descriptions](https://github.com/skratchdot/github-get-missing-descriptions.user.js)
28 | - [Github: gh-pages Link](https://github.com/skratchdot/github-gh-pages-link.user.js)
29 | - [Github: Pull Request Links](https://github.com/skratchdot/github-pull-request-links.user.js)
30 | - [Github: Repo Counts](https://github.com/skratchdot/github-repo-counts.user.js)
31 | - [Github: Repo Filter Info](https://github.com/skratchdot/github-repo-filter-info.user.js)
32 | - [Github: Twitter Link](https://github.com/skratchdot/github-twitter-link.user.js)
33 |
34 |
35 | ### Development Info ###
36 |
37 | - Cloning the repo:
38 |
39 | git@github.com:skratchdot/github-enhancement-suite.git
40 |
41 | - Updating submodules:
42 |
43 | git submodule foreach git pull
44 |
45 |
46 | ## See Also
47 |
48 | - [Github Discussion](https://github.com/isaacs/github/issues/128)
49 | - [Reddit Enhancement Suite](https://github.com/honestbleeps/Reddit-Enhancement-Suite)
50 | - [Octotree](https://github.com/buunguyen/octotree)
51 | - [Refined Github](https://github.com/sindresorhus/refined-github)
52 | - https://github.com/jayson/github-enhancement-suite
53 | - https://github.com/EvanDotPro/github-enhancement-suite
54 | - https://github.com/mduan/Github-Enhancement-Suite
55 | - https://github.com/rsbrown/ghes
56 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var gulp = require('gulp');
3 | var ejs = require('ejs');
4 | var exec = require('child_process').exec;
5 | var fs = require('fs');
6 | var insert = require('gulp-insert');
7 | var nodeunit = require('gulp-nodeunit');
8 | var size = require('gulp-size');
9 | var uglify = require('gulp-uglify');
10 | var reactify = require('reactify');
11 | var rename = require('gulp-rename');
12 | var through2 = require('through2');
13 | var browserify = require('browserify');
14 |
15 | var getPackage = function () {
16 | return JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
17 | };
18 |
19 | var getHeader = function () {
20 | return ejs.render(fs.readFileSync('./userscript.header.ejs', 'utf-8'), getPackage());
21 | };
22 |
23 | var displaySize = function (title) {
24 | return size({
25 | title: title || '',
26 | showFiles: true
27 | });
28 | };
29 |
30 | gulp.task('plugins', function () {
31 | var plugins = [];
32 | var contents = [
33 | '/* autogenerated by running `gulp plugins` (see gulpfile.js) */',
34 | '"use strict";'
35 | ];
36 | fs.readdirSync('./lib/plugins').forEach(function (file) {
37 | var pluginKey;
38 | file = file.replace('.js', '');
39 | plugins.push(file);
40 | pluginKey = file;
41 | if (pluginKey.indexOf('-') >= 0) {
42 | pluginKey = '["' + pluginKey + '"]';
43 | } else {
44 | pluginKey = '.' + pluginKey;
45 | }
46 | contents.push('exports' + pluginKey + ' = require("../plugins/' + file + '");');
47 | });
48 | contents.push('exports.pluginNames = ' + JSON.stringify(plugins) + ';\n');
49 | fs.writeFileSync('./lib/core/plugins.js', contents.join('\n'), 'utf-8');
50 | });
51 |
52 | gulp.task('userscript', function () {
53 | var header = getHeader() + '\n\n';
54 | return gulp.src('./lib/index.js')
55 | .pipe(through2.obj(function (file, enc, next) {
56 | browserify(file.path)
57 | .transform('reactify')
58 | .bundle(function (err, res) {
59 | // assumes file.contents is a Buffer
60 | file.contents = res;
61 | next(null, file);
62 | });
63 | }))
64 | .pipe(rename('github-enhancement-suite.debug.user.js'))
65 | .pipe(insert.prepend(header))
66 | .pipe(gulp.dest('./build/'))
67 | .pipe(displaySize('Userscript (Debug)'))
68 | .pipe(uglify({output:{max_line_len: 80}}))
69 | .pipe(rename('github-enhancement-suite.user.js'))
70 | .pipe(insert.prepend(header))
71 | .pipe(gulp.dest('./build/'))
72 | .pipe(displaySize('Userscript (Minified)'));
73 | });
74 |
75 | gulp.task('lint', function () {
76 | exec([
77 | 'node',
78 | './node_modules/jsxhint/cli.js',
79 | //'--show-non-errors',
80 | '--exclude-path',
81 | './.jshintignore',
82 | '--config',
83 | './.jshint',
84 | './lib/**/*.js',
85 | './test',
86 | './gulpfile.js'
87 | ].join(' '),
88 | function (err, stdout, stderr) {
89 | if (stdout) {
90 | console.log(stdout);
91 | }
92 | });
93 | });
94 |
95 | gulp.task('test', function () {
96 | gulp.src('test.js')
97 | .pipe(nodeunit());
98 | });
99 |
100 | gulp.task('watch', function () {
101 | gulp.watch(['./lib/**/*.js', './test/**/*.js'], ['lint', 'plugins', 'userscript', 'test']);
102 | });
103 |
104 | // setup default task
105 | gulp.task('default', ['lint', 'plugins', 'userscript', 'test', 'watch']);
106 |
107 | // handle errors
108 | process.on('uncaughtException', function (e) {
109 | console.error(e);
110 | });
111 |
--------------------------------------------------------------------------------
/lib/plugins/settings.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var React = require('react');
3 | var jQuery = require('jquery');
4 | var plugins = require('../core/plugins');
5 | var utils = require('../core/utils');
6 | var packageInfo = require('../../package.json');
7 | // config
8 | var containerId = 'github-enhancement-suite-settings';
9 | var appId = containerId + '-app';
10 |
11 | exports.name = 'Settings';
12 | exports.description = 'A plugin that controls all the Github Enhancement Suite plugin settings and whether or not a plugin is enabled.';
13 | exports.enabledSelector = 'a.js-selected-navigation-item.selected[href="/settings/profile"]';
14 |
15 | exports.onPage = function () {
16 | var App, PluginDisplay, injectContainer;
17 |
18 | injectContainer = function () {
19 | if (jQuery('#' + containerId).length === 0) {
20 | jQuery('.column.three-fourths').append('' +
21 | '' +
22 | '
Github Enhancement Suite
' +
23 | '
' +
24 | '
'
25 | );
26 | }
27 | };
28 |
29 | App = React.createClass({
30 | getDefaultProps: function () {
31 | return {
32 | pluginNames: plugins.pluginNames.filter(function (plugin) {
33 | return plugin !== 'settings';
34 | })
35 | };
36 | },
37 | getInitialState: function () {
38 | return {
39 | lastAction: Date.now()
40 | };
41 | },
42 | onEnableButton: function (pluginName) {
43 | utils.togglePluginEnabled(pluginName);
44 | this.setState({lastAction: Date.now()});
45 | },
46 | render: function () {
47 | var $this = this;
48 | return (
49 |
50 |
57 |
58 | {this.props.pluginNames.map(function (pluginName) {
59 | var plugin = plugins[pluginName];
60 | return (
61 |
71 | );
72 | })}
73 |
74 | );
75 | }
76 | });
77 |
78 | PluginDisplay = React.createClass({
79 | getDefaultProps: function () {
80 | return {
81 | name: '',
82 | description: '',
83 | enabled: false,
84 | onEnableButton: function () {}
85 | };
86 | },
87 | render: function () {
88 | return (
89 |
90 |
91 |
92 |
{this.props.name}
93 |
94 |
101 | {this.props.description}
102 |
103 |
104 |
105 |
108 |
109 |
110 |
111 |
112 | );
113 | }
114 | });
115 |
116 | // setup page
117 | injectContainer();
118 | React.render(, document.getElementById(appId));
119 | };
120 |
--------------------------------------------------------------------------------
/plugins-invalid/editor-theme.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 |
4 | exports.enabledSelector = '.file.js-code-editor .file-actions';
5 |
6 | exports.onPage = function () {
7 | // config variables
8 | var $actions,
9 | brightThemes = {
10 | "ace/theme/chrome" : "Chrome",
11 | "ace/theme/clouds" : "Clouds",
12 | "ace/theme/crimson_editor" : "Crimson Editor",
13 | "ace/theme/dawn" : "Dawn",
14 | "ace/theme/dreamweaver" : "Dreamweaver",
15 | "ace/theme/eclipse" : "Eclipse",
16 | "ace/theme/github" : "GitHub",
17 | "ace/theme/solarized_light" : "Solarized Light",
18 | "ace/theme/textmate" : "TextMate",
19 | "ace/theme/tomorrow" : "Tomorrow",
20 | "ace/theme/xcode" : "XCode"
21 | },
22 | darkThemes = {
23 | "ace/theme/ambiance" : "Ambiance",
24 | "ace/theme/clouds_midnight" : "Clouds Midnight",
25 | "ace/theme/cobalt" : "Cobalt",
26 | "ace/theme/idle_fingers" : "idleFingers",
27 | "ace/theme/kr_theme" : "krTheme",
28 | "ace/theme/merbivore" : "Merbivore",
29 | "ace/theme/merbivore_soft" : "Merbivore Soft",
30 | "ace/theme/mono_industrial" : "Mono Industrial",
31 | "ace/theme/monokai" : "Monokai",
32 | "ace/theme/pastel_on_dark" : "Pastel on dark",
33 | "ace/theme/solarized_dark" : "Solarized Dark",
34 | "ace/theme/twilight" : "Twilight",
35 | "ace/theme/tomorrow_night" : "Tomorrow Night",
36 | "ace/theme/tomorrow_night_blue" : "Tomorrow Night Blue",
37 | "ace/theme/tomorrow_night_bright" : "Tomorrow Night Bright",
38 | "ace/theme/tomorrow_night_eighties" : "Tomorrow Night 80s",
39 | "ace/theme/vibrant_ink" : "Vibrant Ink"
40 | },
41 | defaultTheme = 'ace/theme/twilight',
42 | localStorageKey = 'SKRATCHDOT_EDITOR_THEME',
43 | selectId = 'skratchdot-editor-theme',
44 | setTimeoutCount = 0,
45 | setTimeoutCountMax = 50,
46 | setTimeoutDelay = 100,
47 | // functions
48 | createSelect,
49 | createSelectHelper,
50 | initEditorTheme,
51 | getEditorTheme,
52 | setEditorTheme,
53 | init;
54 |
55 | createSelect = function (selectedTheme) {
56 | var select = '';
60 | return select;
61 | };
62 |
63 | createSelectHelper = function (selectedTheme, themes, label) {
64 | var theme, str = '\n';
76 | return str;
77 | };
78 |
79 | getEditorTheme = function () {
80 | var theme;
81 | if (window.localStorage) {
82 | theme = window.localStorage.getItem(localStorageKey);
83 | }
84 | if (typeof theme !== 'string' ||
85 | 'undefined' === typeof ace ||
86 | !ace.config.modules.hasOwnProperty(theme)) {
87 | theme = defaultTheme;
88 | setEditorTheme(theme);
89 | }
90 | return theme;
91 | };
92 |
93 | setEditorTheme = function (theme) {
94 | if (window.localStorage) {
95 | window.localStorage.setItem(localStorageKey, theme);
96 | }
97 | if ('undefined' !== typeof CodeEditor) {
98 | CodeEditor.ace.setTheme(theme);
99 | }
100 | };
101 |
102 | initEditorTheme = function () {
103 | var theme, $select;
104 | setTimeoutCount += 1;
105 | if (jQuery('.ace_editor').length === 1) {
106 | theme = getEditorTheme();
107 | $actions.prepend(createSelect(theme));
108 | $select = jQuery('#' + selectId);
109 | $select.change(function () {
110 | setEditorTheme(jQuery(this).val());
111 | });
112 | $select.change();
113 | setTimeoutCount = setTimeoutCountMax;
114 | }
115 | if (setTimeoutCount < setTimeoutCountMax) {
116 | setTimeout(initEditorTheme, setTimeoutDelay);
117 | }
118 | };
119 |
120 | init = function () {
121 | $actions = jQuery('.file.js-code-editor .file-actions');
122 |
123 | // only do something when we are editing blobs
124 | if ($actions.length) {
125 | try {
126 | setTimeout(initEditorTheme, setTimeoutDelay);
127 | } catch (e) {}
128 |
129 | }
130 | };
131 |
132 | jQuery(document).ready(init);
133 | };
134 |
--------------------------------------------------------------------------------
/lib/extras/ay-pie-chart.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Pie Chart v0.1.0
3 | * https://github.com/gajus/pie-chart
4 | *
5 | * Licensed under the BSD.
6 | * https://github.com/gajus/pie-chart/blob/master/LICENSE
7 | *
8 | * Author: Gajus Kuizinas
9 | */
10 | /**
11 | * Slightly modified for use in node/browserify by @skratchdot
12 | */
13 | var d3 = require('d3');
14 | var ay = {};
15 |
16 | ay.pie_chart = function (name, data, options) {
17 | 'use strict';
18 | var svg = d3.select('svg.' + name),
19 | chart_size = svg[0][0].clientWidth || svg[0][0].parentNode.clientWidth,
20 | settings = {
21 | radius_inner: 0,
22 | radius_outer: chart_size / 3,
23 | radius_label: chart_size / 3 + 20,
24 | percentage: true,
25 | value: false,
26 | label_margin: 10,
27 | group_data: 0
28 | },
29 | donut,
30 | arc,
31 | slices,
32 | labels_group,
33 | grouped_labels = {left: [], right: []},
34 | labels,
35 | label_boxes,
36 | label_texts,
37 | parameter,
38 | reposition_colliding_labels = function (group) {
39 | group
40 | .sort(function (a, b) {
41 | return (a.y + a.height) - (b.y + b.height);
42 | })
43 | .forEach(function (e, i) {
44 | if (group[i + 1]) {
45 | if (group[i + 1].y - (e.y + e.height) < settings.label_margin) {
46 | group[i + 1].y = (e.y + e.height) + settings.label_margin;
47 | }
48 | }
49 | if (e.x < settings.label_margin) {
50 | e.x = settings.label_margin;
51 | } else if (e.x + e.width > chart_size - settings.label_margin) {
52 | e.x = chart_size - e.width - settings.label_margin;
53 | }
54 | d3.select(labels[0][e.index])
55 | .attr('transform', 'translate(' + e.x + ', ' + e.y + ')');
56 | d3.select(label_boxes[0][e.index])
57 | .attr('x', 0)
58 | .attr('y', -e.height + 2)
59 | .attr('width', e.width + 4)
60 | .attr('height', e.height + 4);
61 | e.textNode
62 | .attr('x', 2)
63 | .attr('y', 2);
64 | });
65 | },
66 | group_data = function (data) {
67 | var data_size = 0,
68 | removed_data_size = 0,
69 | i;
70 | data.forEach(function (e) {
71 | data_size += e.value;
72 | });
73 | // Check if it is worth grouping the data.
74 | for (i = data.length-1; i >= 0; i--) {
75 | if ((data[i].value / data_size) * 100 < settings.group_data) {
76 | removed_data_size++;
77 | }
78 | }
79 | if(removed_data_size > 1) {
80 | removed_data_size = 0;
81 | for (i = data.length-1; i >= 0; i--) {
82 | if ((data[i].value / data_size) * 100 < settings.group_data) {
83 | removed_data_size += data.splice(i, 1)[0].value;
84 | }
85 | }
86 | }
87 | data.push({index: 0, name: 'Other', value: removed_data_size});
88 | return data;
89 | };
90 | if (data.map(function (d) { return d.index; }).indexOf(0) !== -1) {
91 | throw '0 index is reserved for grouped data.';
92 | }
93 | if (options !== undefined) {
94 | for (parameter in options) {
95 | if (options.hasOwnProperty(parameter) && settings[parameter] !== undefined) {
96 | settings[parameter] = options[parameter];
97 | }
98 | }
99 | }
100 | if (settings.group_data) {
101 | data = group_data(data);
102 | }
103 | donut = svg
104 | .append('g')
105 | .attr('class', 'donut')
106 | .attr('transform', 'translate(' + (chart_size / 2) + ', ' + (chart_size / 2) + ')');
107 | arc = d3.svg.arc()
108 | .innerRadius(settings.radius_inner)
109 | .outerRadius(settings.radius_outer);
110 | data = d3.layout.pie()
111 | .value(function (e) {
112 | return e.value;
113 | })
114 | .sort(function (a, b) {
115 | return b.index - a.index;
116 | })(data);
117 | slices = donut
118 | .selectAll('path')
119 | .data(data)
120 | .enter()
121 | .append('path')
122 | .attr('class', function (d) {
123 | return 'g-' + d.data.index;
124 | })
125 | .attr('d', arc)
126 | .on('mouseover', function (d, i) {
127 | d3.select(labels[0][i])
128 | .classed('active', true);
129 | })
130 | .on('mouseout', function (d, i) {
131 | d3.select(labels[0][i])
132 | .classed('active', false);
133 | });
134 |
135 | labels_group = svg
136 | .append('g')
137 | .attr('class', 'labels');
138 | labels = labels_group
139 | .selectAll('g.label')
140 | .data(data)
141 | .enter()
142 | .append('g')
143 | .filter(function (e) {
144 | if (settings.percentage) {
145 | return true;
146 | }
147 | return e.data.name !== undefined;
148 | })
149 | .attr('class', 'label');
150 | label_boxes = labels
151 | .append('rect');
152 | label_texts = labels
153 | .append('text').text(function (e) {
154 | var percentage = (((e.endAngle - e.startAngle) / (2 * Math.PI)) * 100).toFixed(2),
155 | label = [];
156 | if (e.data.name !== undefined) {
157 | label.push(e.data.name);
158 | }
159 | if (settings.value) {
160 | label.push(' - ' + e.data.value);
161 | }
162 | if (settings.percentage) {
163 | label.push(' (' +percentage + '%)');
164 | }
165 |
166 | return label.join(' ');
167 | })
168 | .each(function (d, i) {
169 | var center = arc.centroid(d),
170 | x = center[0],
171 | y = center[1],
172 | h = Math.sqrt(x * x + y * y),
173 | lx = x / h * settings.radius_label + chart_size / 2,
174 | ly = y / h * settings.radius_label + chart_size / 2,
175 | left_aligned = (d.endAngle - d.startAngle) * 0.5 + d.startAngle > Math.PI,
176 | text = d3.select(this),
177 | bb = this.getBBox();
178 | grouped_labels[left_aligned ? 'left' : 'right'].push({
179 | index: i,
180 | width: bb.width,
181 | height: bb.height,
182 | x: left_aligned ? lx - bb.width : lx,
183 | y: ly,
184 | textNode: text
185 | });
186 | });
187 | reposition_colliding_labels(grouped_labels.left);
188 | reposition_colliding_labels(grouped_labels.right);
189 | };
190 | exports.ay = ay;
--------------------------------------------------------------------------------
/plugins-invalid/code-search.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 |
4 | exports.enabledSelector = '';
5 |
6 | exports.onPage = function () {};
7 |
8 | var implementMe = function () {
9 |
10 | // Declare a namespace to store functions in
11 | var SKRATCHDOT = window.SKRATCHDOT || {};
12 |
13 | // GitHub.nameWithOwner used to exist on the page, but was removed.
14 | // Now constructing this with some jQuery selectors
15 | SKRATCHDOT.nameWithOwner = '';
16 | SKRATCHDOT.getNameWithOwner = function () {
17 | var returnValue = '',
18 | repoLink = jQuery('a.js-current-repository');
19 | if (repoLink.length > 0) {
20 | returnValue = repoLink.attr('href').substr(1);
21 | }
22 | return returnValue;
23 | };
24 |
25 | // The function that will be called when repo search is used
26 | SKRATCHDOT.performCodeSearch = function (searchText, startValue) {
27 | jQuery.ajax({
28 | url: '/search',
29 | dataType: 'html',
30 | type: 'GET',
31 | data: {
32 | type: 'Code',
33 | q: searchText + ' repo:' + SKRATCHDOT.nameWithOwner.toLowerCase(),
34 | p: startValue
35 | },
36 | success: function (data) {
37 | try {
38 | var noResultsSelector = '.blankslate',
39 | resultHtml = jQuery(data).find('#code_search_results, ' + noResultsSelector),
40 | resultContainer = jQuery('#skratchdot-result-container'),
41 | reQuotes = new RegExp('([^\\\\])(\\")', 'g'),
42 | newSearchText,
43 | newSearchLink;
44 |
45 | resultContainer.html(resultHtml);
46 |
47 | resultContainer.find('#code_search_results div.pagination a').click(function (e) {
48 | var pageNumber, url, regex, results;
49 |
50 | pageNumber = 1;
51 | url = jQuery(this).attr('href');
52 | regex = new RegExp('(.)*(search\\?p\\=)([0-9]+)', 'gi');
53 | results = regex.exec(url);
54 | if (results.length >= 4) {
55 | pageNumber = parseInt(results[3], 10);
56 | }
57 |
58 | resultContainer.empty();
59 |
60 | // Refresh with the results from the pagination
61 | SKRATCHDOT.performCodeSearch(searchText, pageNumber);
62 |
63 | e.preventDefault();
64 | });
65 |
66 | // search for unescaped quotes. if we find some, create a link
67 | // that will re-try the search with escaped quotes
68 | if (resultHtml.is(noResultsSelector) && searchText.match(reQuotes)) {
69 | newSearchText = searchText.replace(reQuotes, function ($1, $2) {
70 | return $2 + '\\"';
71 | });
72 | newSearchLink = jQuery('searching with escaped quotes');
73 | newSearchLink.click(function (e) {
74 | e.preventDefault();
75 | jQuery('#skratchdot-code-search').find('input:first').val(newSearchText);
76 | SKRATCHDOT.performCodeSearch(newSearchText, 1);
77 | });
78 | jQuery(noResultsSelector).append('Your search contained unescaped quotes. You can also try:
');
79 | jQuery(noResultsSelector).append(newSearchLink);
80 | }
81 | } catch (e) {}
82 | }
83 | });
84 | };
85 |
86 | SKRATCHDOT.codeSearchInit = function () {
87 | var siteContainer = jQuery('div.site div.container'),
88 | repohead = siteContainer.find('div.tabnav'),
89 | jsRepoPjaxContainer = jQuery('#js-repo-pjax-container'),
90 | codeTabSelected,
91 | tabsOnRight;
92 | SKRATCHDOT.nameWithOwner = SKRATCHDOT.getNameWithOwner();
93 | if (repohead.length > 0 && typeof SKRATCHDOT.nameWithOwner === 'string' && SKRATCHDOT.nameWithOwner.length > 0) {
94 | // Do nothing if code tab isn't selected
95 | codeTabSelected = siteContainer.find('ul.tabs li:eq(1) a.selected');
96 | if (codeTabSelected.length === 0) {
97 | return;
98 | }
99 |
100 | tabsOnRight = repohead.find('.tabnav-right ul');
101 |
102 | // Do nothing if there's already a search box
103 | if (repohead.find('input[type=text]').length > 1) {
104 | return;
105 | }
106 |
107 | // Create Search Bar
108 | tabsOnRight.prepend(
109 | jQuery('')
110 | .attr('class', 'search')
111 | .append(
112 | jQuery('')
113 | .attr('id', 'skratchdot-code-search')
114 | .attr('method', 'get')
115 | .attr('action', 'search')
116 | .append(
117 | jQuery('')
118 | .attr('class', 'fieldwrap')
119 | .append(
120 | jQuery('')
121 | .attr('type', 'text')
122 | .attr('placeholder', 'Search Source Code...')
123 | .attr('style', 'border:1px solid #ccc;border-radius:3px;color:#666;min-height:26px;height:26px;padding:0 5px;')
124 | )
125 | .append(
126 | jQuery('')
127 | .attr('class', 'minibutton')
128 | .attr('type', 'submit')
129 | .append('Search')
130 | )
131 | )
132 | )
133 | );
134 |
135 | // When a search is performed
136 | jQuery('#skratchdot-code-search').submit(function (e) {
137 | var searchText = jQuery(this).find('input:first').val();
138 |
139 | e.preventDefault();
140 |
141 | // Clear our ajax container, and get ready to store search results
142 | jsRepoPjaxContainer.empty().append('');
143 |
144 | // Only perform search if we entered a value
145 | if (searchText.length > 0) {
146 | SKRATCHDOT.performCodeSearch(searchText, 1);
147 | } else {
148 | jQuery('#skratchdot-result-container').html('' +
149 | '
' +
150 | '
Please enter a search term into the code search input, and try again.
' +
151 | '
' +
152 | ' You could also try an advanced search.' +
153 | '
' +
154 | '
');
155 | }
156 |
157 | });
158 | }
159 | };
160 |
161 | // onDomReady : setup our page
162 | jQuery(document).ready(function () {
163 | SKRATCHDOT.codeSearchInit();
164 | });
165 | };
166 |
--------------------------------------------------------------------------------
/lib/plugins/repo-filter-info.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var jQuery = require('jquery');
3 | var debounce = require('lodash.debounce');
4 | var ay = require('../extras/ay-pie-chart').ay;
5 | // config vars
6 | var filterDiv;
7 | var chartData;
8 | var colors = [
9 | '#1f77b4', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c',
10 | '#98df8a', '#d62728', '#ff9896', '#9467bd', '#c5b0d5'
11 | ];
12 | // functions
13 | var drawFilterDiv;
14 | var injectCss;
15 | var injectFilterDiv;
16 | var repoFilterIsExpanded;
17 | var repoFilterBuildChart;
18 |
19 | exports.name = 'Repo Filter Info';
20 | exports.description = 'A user script to display some additional info below the repository filter on a user\'s "repositories" page.';
21 | exports.enabledSelector = 'body.page-profile .tabnav-tab.selected:contains("Repositories")';
22 |
23 | exports.onPage = function () {
24 | var list = document.querySelector('.repo-tab .repo-list.js-repo-list');
25 | var observer = new MutationObserver(function (mutations) {
26 | //console.log(Date.now(), mutations.length);
27 | setImmediate(drawFilterDiv);
28 | });
29 | injectCss();
30 | injectFilterDiv();
31 | observer.observe(list, {
32 | attributes: true,
33 | childList: true,
34 | characterData: true,
35 | characterDataOldValue: true,
36 | subtree: true
37 | });
38 | };
39 |
40 | injectCss = function () {
41 | var i, colorNum, cssRule = '', id = 'skratchdot-repo-filter-info-css';
42 | if (jQuery('#' + id).length === 0) {
43 | // Create some styles
44 | cssRule += '';
61 | jQuery('head').append(cssRule);
62 | }
63 | };
64 |
65 | injectFilterDiv = function () {
66 | if (jQuery('#skratchdot-repo-filter-div').length === 0) {
67 | // Create our information div
68 | jQuery('div.js-repo-filter .filter-bar').after(
69 | jQuery('')
70 | .attr('id', 'skratchdot-repo-filter-div')
71 | .css('background', 'none repeat scroll 0 0 #FAFAFB')
72 | .css('border', '1px solid #DDDDDD')
73 | .css('border-radius', '4px 4px 4px 4px')
74 | .css('cursor', 'pointer')
75 | .css('margin-bottom', '10px')
76 | .css('padding', '10px')
77 | .css('text-align', 'center')
78 | .append('')
79 | .append('' +
80 | '
show languages' +
81 | '
' +
82 | '
' +
83 | '
' +
84 | '
' +
85 | '
')
86 | .append('' +
87 | '
' +
90 | '
' +
91 | '
' +
92 | '| Language | ' +
93 | ' | ' +
94 | 'Usage | ' +
95 | 'Repos | ' +
96 | 'Starred | ' +
97 | 'Forks | ' +
98 | '
' +
99 | '
' +
100 | '
')
101 | .append('')
102 | );
103 | filterDiv = jQuery('#skratchdot-repo-filter-div');
104 | // Attach a click event to show/hide language usage
105 | filterDiv.click(function (e) {
106 | e.preventDefault();
107 | if (repoFilterIsExpanded()) {
108 | filterDiv.find('.skratchdot-languages').text('show languages');
109 | filterDiv.find('.show-hide').hide();
110 | } else {
111 | filterDiv.find('.skratchdot-languages').text('hide languages');
112 | filterDiv.find('.show-hide').show();
113 | repoFilterBuildChart();
114 | }
115 | });
116 | }
117 | };
118 |
119 | repoFilterIsExpanded = function () {
120 | return filterDiv.find('.show-hide:visible').length > 0;
121 | };
122 |
123 | drawFilterDiv = debounce(function () {
124 | console.log('drawing filter div...');
125 | var i = 0, othersCount = 0,
126 | total = 0, forks = 0, starred = 0,
127 | languageHash = {}, languageArray = [], languageName = '', language = {},
128 | elements, elem, temp, forkCount, stargazerCount;
129 |
130 | // Initialize Chart Data
131 | chartData = [];
132 |
133 | // Calculate counts
134 | elements = document.querySelectorAll('ul.js-repo-list > li:not([style*="display: none"])');
135 | for (i = 0; i < elements.length; i++) {
136 | elem = jQuery('' + elements[i].innerHTML + '
');
137 | // Do nothing if we are looking at an invalid
138 | if (elem.find('.repo-list-stats').length === 0) {
139 | continue;
140 | }
141 | forkCount = parseInt(elem.find('[aria-label="Forks"]').text().replace(',', ''), 10);
142 | stargazerCount = parseInt(elem.find('[aria-label="Stargazers"]').text().replace(',', ''), 10);
143 | total = total + 1;
144 | forks += forkCount;
145 | starred += stargazerCount;
146 | // get language name
147 | temp = elem.find('.repo-list-stats').clone();
148 | temp.find('.repo-list-stat-item').remove();
149 | languageName = temp.text().trim();
150 | if (languageName === '') {
151 | languageName = 'Unknown';
152 | }
153 | if (!languageHash.hasOwnProperty(languageName)) {
154 | languageHash[languageName] = {
155 | name : languageName,
156 | count : 0,
157 | forks : 0,
158 | starred : 0
159 | };
160 | }
161 | languageHash[languageName].count = languageHash[languageName].count + 1;
162 | languageHash[languageName].forks = languageHash[languageName].forks + forkCount;
163 | languageHash[languageName].starred = languageHash[languageName].starred + stargazerCount;
164 | }
165 |
166 | // Display counts
167 | filterDiv.find('.left').html('Now Showing ' + total + ' Repos');
168 | filterDiv.find('.skratchdot-count-forks').text(forks);
169 | filterDiv.find('.skratchdot-count-starred').text(starred);
170 | filterDiv.find('table tbody').empty();
171 | filterDiv.find('.chart').empty();
172 |
173 | // Convert to array
174 | for (languageName in languageHash) {
175 | if (languageHash.hasOwnProperty(languageName)) {
176 | languageArray.push(languageHash[languageName]);
177 | }
178 | }
179 |
180 | // Sort Array
181 | languageArray.sort(function (a, b) {
182 | return b.count - a.count || a.name > b.name;
183 | });
184 |
185 | // Show languages
186 | for (i = 0; i < languageArray.length; i++) {
187 | language = languageArray[i];
188 | filterDiv.find('table tbody').append('' +
189 | '| ' + language.name + ' | ' +
190 | ' | ' +
191 | '' +
192 | ((language.count / total) * 100).toFixed(2) + ' %' +
193 | ' | ' +
194 | '' + language.count + ' | ' +
195 | '' + language.starred + ' | ' +
196 | '' + language.forks + ' | ' +
197 | '
');
198 |
199 | if (i < colors.length - 1) {
200 | chartData.push({
201 | index: i + 1,
202 | name: language.name,
203 | value: language.count
204 | });
205 | } else if (i !== 0) {
206 | othersCount += language.count;
207 | }
208 | }
209 |
210 | // Add "Others" to chartData
211 | if (othersCount > 0) {
212 | chartData.push({
213 | index: colors.length,
214 | name: 'Other',
215 | value: othersCount
216 | });
217 | }
218 |
219 | // Build Chart
220 | repoFilterBuildChart();
221 | }, 100);
222 |
223 | repoFilterBuildChart = function () {
224 | var $container = filterDiv.find('.chart');
225 | if (repoFilterIsExpanded() && $container.find('svg').length === 0) {
226 | $container.append('');
227 | if ('undefined' !== typeof ay) {
228 | ay.pie_chart('skratchdot-language-chart', chartData, { group_data: 0 });
229 | }
230 | }
231 | };
232 |
--------------------------------------------------------------------------------