├── .babelrc
├── .browserslistrc
├── .eslintrc
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── css
└── styles.css
├── html
├── links.html
└── options.html
├── images
├── link_go_128.png
├── link_go_16.png
├── link_go_19.png
├── link_go_32.png
└── link_go_48.png
├── manifest.json
├── package-lock.json
├── package.json
├── src
├── components
│ ├── LinkList.css
│ ├── LinkList.js
│ ├── LinkListEmpty.js
│ ├── LinkListExpired.js
│ ├── Options.css
│ └── Options.js
├── contentscript.js
├── links.js
├── options.js
└── service_worker.js
└── vendor
└── bootstrap
├── LICENSE
├── css
├── bootstrap-theme.css
└── bootstrap.css
└── fonts
├── glyphicons-halflings-regular.eot
├── glyphicons-halflings-regular.svg
├── glyphicons-halflings-regular.ttf
├── glyphicons-halflings-regular.woff
└── glyphicons-halflings-regular.woff2
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | ["@babel/preset-react", {"runtime": "automatic"}]
5 | ]
6 | }
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | last 2 Chrome versions, Firefox ESR
2 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "settings": {
3 | "react": {
4 | "version": "detect"
5 | }
6 | },
7 | "parser": "@babel/eslint-parser",
8 | "plugins": [
9 | "react"
10 | ],
11 | "env": {
12 | "browser": true,
13 | "node": true
14 | },
15 | "globals": {
16 | "chrome": true,
17 | },
18 | "extends": "eslint:recommended",
19 | "rules": {
20 | "comma-dangle": [1, "always-multiline"],
21 | "jsx-quotes": [2, "prefer-double"],
22 | "react/display-name": [2, {"ignoreTranspilerName": false}],
23 | "react/sort-comp": 2,
24 | "react/jsx-curly-spacing": [2, "never"],
25 | "react/jsx-no-duplicate-props": 2,
26 | "react/jsx-no-undef": 2,
27 | "react/jsx-uses-react": 1,
28 | "react/jsx-uses-vars": 1,
29 | "react/jsx-wrap-multilines": 2,
30 | "react/no-deprecated": 2,
31 | "react/no-unknown-property": 2,
32 | "semi": [2, "always"],
33 | "strict": [2, "global"],
34 | "quotes": [2, "single"],
35 | "no-console": 1,
36 | "no-unused-vars": [2, {"args": "none"}]
37 | },
38 | "parserOptions": {
39 | "ecmaFeatures": {
40 | "modules": true,
41 | "jsx": true
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sublime-project
2 | *.sublime-workspace
3 | dist/
4 | js/
5 | node_modules/
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2012-2014 Don Tong
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
13 | all 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
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: watch
2 | watch: clean
3 | npm run watch
4 |
5 | .PHONY: package
6 | package: build
7 | mkdir -p dist
8 | zip -x\*.DS_Store dist/linkgrabber.zip -r css html images js vendor manifest.json
9 |
10 | .PHONY: build
11 | build: clean
12 | npm run build
13 |
14 | .PHONY: lint
15 | lint:
16 | npm exec eslint src
17 |
18 | .PHONY: clean
19 | clean:
20 | rm -rf js
21 | rm -rf dist
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Hello ###
2 |
3 | https://chrome.google.com/webstore/detail/link-grabber/caodelkhipncidmoebgbbeemedohcdma
4 |
5 | Link Grabber is an extension for Google Chrome that extracts links from an
6 | HTML page and displays them in another tab.
7 |
8 | ### Licenses ###
9 |
10 | This project is open source software that also bundles other open source
11 | software.
12 |
13 | Unless otherwise noted, the MIT License applies.
14 |
15 | Icon files in images/ are derived from icons by FatCow
16 | (http://www.fatcow.com/free-icons) and licensed under the Creative Commons
17 | Attribution 3.0 License
18 |
19 | Files in vendor/bootstrap are licensed under the apache-2.0 license
20 | (vendor/bootstrap/LICENSE)
21 |
--------------------------------------------------------------------------------
/css/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-bottom: 1em;
3 | overflow-y: scroll;
4 | }
5 |
6 | a:visited {
7 | color: #551a8b;
8 | }
9 |
10 | /* horizontal alignment */
11 | .txtC, table .txtC, table tr .txtC{text-align:center;}
12 | .txtL, table .txtL, table tr .txtL{text-align:left;}
13 | .txtR, table .txtR, table tr .txtR{text-align:right;}
14 |
15 | /* vertical alignment */
16 | .txtT, table .txtT, table tr .txtT{vertical-align:top;}
17 | .txtB, table .txtB, table tr .txtB{vertical-align:bottom;}
18 | .txtM, table .txtM, table tr .txtM{vertical-align:middle;}
19 |
20 | .table tbody > tr > td.txtM {
21 | vertical-align: middle;
22 | }
23 |
24 | .d-flex {
25 | display: flex;
26 | }
27 |
28 | .flex-grow-0 {
29 | flex-grow: 0;
30 | }
31 |
32 | .flex-grow-1 {
33 | flex-grow: 1;
34 | }
35 |
36 | .align-items-center {
37 | align-items: center;
38 | }
--------------------------------------------------------------------------------
/html/links.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Extracted Links
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/html/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Link Grabber Options
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/images/link_go_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/images/link_go_128.png
--------------------------------------------------------------------------------
/images/link_go_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/images/link_go_16.png
--------------------------------------------------------------------------------
/images/link_go_19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/images/link_go_19.png
--------------------------------------------------------------------------------
/images/link_go_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/images/link_go_32.png
--------------------------------------------------------------------------------
/images/link_go_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/images/link_go_48.png
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Link Grabber",
3 | "manifest_version": 3,
4 | "version": "0.6.1",
5 | "description": "An easy to use extractor or grabber for hyperlinks on an HTML page",
6 | "permissions": [
7 | "activeTab",
8 | "clipboardWrite",
9 | "contextMenus",
10 | "scripting",
11 | "storage"
12 | ],
13 | "incognito": "split",
14 | "options_ui": {
15 | "browser_style": false,
16 | "page": "/html/options.html"
17 | },
18 | "icons": {
19 | "16": "/images/link_go_16.png",
20 | "32": "/images/link_go_32.png",
21 | "48": "/images/link_go_48.png",
22 | "128": "/images/link_go_128.png"
23 | },
24 | "background": {
25 | "service_worker": "/js/service_worker.js"
26 | },
27 | "action": {
28 | "default_icon": "/images/link_go_19.png",
29 | "default_title": "Extract the links on this page"
30 | },
31 | "browser_specific_settings": {
32 | "gecko": {
33 | "id": "linkgrabber@7fffffff.com"
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "classnames": "~2.3.2",
4 | "lodash.debounce": "~4.0.8",
5 | "react": "~18.2.0",
6 | "react-dom": "~18.2.0"
7 | },
8 | "devDependencies": {
9 | "@babel/core": "~7.24.0",
10 | "@babel/eslint-parser": "~7.23.10",
11 | "@babel/preset-env": "~7.24.0",
12 | "@babel/preset-react": "~7.23.3",
13 | "esbuild": "0.20.1",
14 | "eslint": "~8.57.0",
15 | "eslint-plugin-react": "~7.34.0"
16 | },
17 | "scripts": {
18 | "build": "esbuild src/*.js --bundle --loader:.js=jsx --outdir=js --sourcemap --splitting --format=esm",
19 | "watch": "esbuild src/*.js --bundle --loader:.js=jsx --outdir=js --sourcemap --splitting --format=esm --watch"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/LinkList.css:
--------------------------------------------------------------------------------
1 | .LinkPageHeader {
2 | white-space: nowrap;
3 | line-height: normal;
4 | overflow-x: hidden;
5 | text-overflow: ellipsis;
6 | }
7 |
8 | .LinkPageStatus {
9 | float: right;
10 | }
11 |
12 | .LinkList {
13 | list-style: none;
14 | padding-left: 0;
15 | }
16 |
17 | .LinkListItem {
18 | border-bottom: 1px solid #ddd;
19 | padding-left: 0.5em;
20 | padding-right: 0.5em;
21 | word-wrap: break-word;
22 | }
23 |
24 | .LinkListItem:nth-child(odd) {
25 | background-color: #F5F5F5;
26 | }
27 |
28 | .LinkListItem--blocked > a {
29 | color: #a94442;
30 | }
31 |
32 | .LinkListItem--duplicate > a {
33 | color: #aaa;
34 | }
35 |
36 | .LinkListItem--blocked.LinkListItem--duplicate > a {
37 | color: #ebccd1;
38 | }
39 |
40 | .LinkPageOptionsForm .form-group {
41 | margin-bottom: 1em;
42 | }
43 |
44 | .LinkPageOptionsForm > .form-group + .form-group {
45 | margin-left: 1em;
46 | }
47 | @media (max-width: 767px) {
48 | .LinkPageOptionsForm {
49 | margin-bottom: 0em;
50 | }
51 | .LinkPageOptionsForm > .form-group + .form-group {
52 | margin-left: 0;
53 | }
54 | }
55 |
56 | .LinkPageOptionsForm .checkbox-inline {
57 | user-select: none;
58 | }
--------------------------------------------------------------------------------
/src/components/LinkList.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useEffect, useRef, useState} from 'react';
3 | import cx from 'classnames';
4 | import debounce from 'lodash.debounce';
5 | import LinkListEmpty from './LinkListEmpty';
6 | import LinkListExpired from './LinkListExpired';
7 | import './LinkList.css';
8 |
9 | function copyLinks(element) {
10 | const selection = window.getSelection();
11 | const prevRange = selection.rangeCount ? selection.getRangeAt(0).cloneRange() : null;
12 | const tmp = document.createElement('div');
13 | const links = element.querySelectorAll('a');
14 | for (let i = 0; i < links.length; i++) {
15 | const clone = links[i].cloneNode(true);
16 | delete (clone.dataset.reactid);
17 | tmp.appendChild(clone);
18 | tmp.appendChild(document.createElement('br'));
19 | }
20 | document.body.appendChild(tmp);
21 | const copyFrom = document.createRange();
22 | copyFrom.selectNodeContents(tmp);
23 | selection.removeAllRanges();
24 | selection.addRange(copyFrom);
25 | document.execCommand('copy');
26 | document.body.removeChild(tmp);
27 | selection.removeAllRanges();
28 | if (prevRange) {
29 | selection.addRange(prevRange);
30 | }
31 | }
32 |
33 | function groupLinksByDomain(links) {
34 | const indexes = new Array(links.length);
35 | const rh = new Array(links.length);
36 | for (let i = 0; i < links.length; i++) {
37 | indexes[i] = i;
38 | rh[i] = links[i].hostname.toLowerCase().split('.').reverse().join('.');
39 | }
40 | indexes.sort((i, j) => {
41 | if (rh[i] < rh[j]) {
42 | return -1;
43 | }
44 | if (rh[i] > rh[j]) {
45 | return 1;
46 | }
47 | return i - j;
48 | });
49 | return indexes.map(i => links[i]);
50 | }
51 |
52 | function mapBlocked(links, blockedDomains) {
53 | blockedDomains = new Set(blockedDomains);
54 | return links.map(link => {
55 | let hostname = link.hostname.toLowerCase();
56 | const dots = [];
57 | for (let i = 0; i < hostname.length; i++) {
58 | if (hostname[i] === '.') {
59 | dots.push(i);
60 | }
61 | }
62 | if (blockedDomains.has(hostname)) {
63 | return true;
64 | }
65 | for (const dot of dots) {
66 | if (blockedDomains.has(hostname.substr(dot + 1))) {
67 | blockedDomains.add(hostname);
68 | return true;
69 | }
70 | }
71 | return false;
72 | });
73 | }
74 |
75 | function mapDuplicates(links) {
76 | const uniq = new Set();
77 | return links.map(link => {
78 | if (uniq.has(link.href)) {
79 | return true;
80 | }
81 | uniq.add(link.href);
82 | return false;
83 | });
84 | }
85 |
86 | function rejectSameOrigin(links, sourceUrl) {
87 | if (!sourceUrl) {
88 | return links;
89 | }
90 | if (!sourceUrl.startsWith('http://') && !sourceUrl.startsWith('https://')) {
91 | return links;
92 | }
93 | const parser = document.createElement('a');
94 | parser.href = sourceUrl;
95 | if (!parser.origin) {
96 | return links;
97 | }
98 | return links.filter(link => link.origin !== parser.origin);
99 | }
100 |
101 | export default function LinkList(props) {
102 | const linkListRef = useRef(null);
103 |
104 | const [filter, setFilter] = useState('');
105 | const [nextFilter, setNextFilter] = useState('');
106 | const [groupByDomain, setGroupByDomain] = useState(false);
107 | const [hideBlockedDomains, setHideBlockedDomains] = useState(true);
108 | const [hideDuplicates, setHideDuplicates] = useState(true);
109 | const [hideSameOrigin, setHideSameOrigin] = useState(false);
110 |
111 | const applyFilter = debounce(() => setFilter(nextFilter), 100, {trailing: true});
112 | const filterChanged = (event) => setNextFilter(event.target.value);
113 | const toggleBlockedLinks = () => setHideBlockedDomains(x => !x);
114 | const toggleDedup = () => setHideDuplicates(x => !x);
115 | const toggleGroupByDomain = () => setGroupByDomain(x => !x);
116 | const toggleHideSameOrigin = () => setHideSameOrigin(x => !x);
117 |
118 | useEffect(() => {
119 | const h = (event) => {
120 | const selection = window.getSelection();
121 | if (selection.type === 'None' || selection.type === 'Caret') {
122 | copyLinks();
123 | }
124 | };
125 | window.document.addEventListener('copy', h);
126 | return () => {
127 | window.document.removeEventListener('copy', h);
128 | };
129 | }, []);
130 |
131 | useEffect(applyFilter, [nextFilter]);
132 |
133 | if (props.expired) {
134 | return ();
135 | }
136 |
137 | let links = props.links.slice(0);
138 | if (links.length === 0) {
139 | return ();
140 | }
141 |
142 | if (hideSameOrigin) {
143 | links = rejectSameOrigin(links, props.source);
144 | }
145 | if (groupByDomain) {
146 | links = groupLinksByDomain(links);
147 | }
148 |
149 | const blocked = mapBlocked(links, props.blockedDomains);
150 | const duplicates = mapDuplicates(links);
151 | const filterLowerCase = filter.trim().toLowerCase();
152 | const items = links.reduce((memo, link, index) => {
153 | if (hideDuplicates && duplicates[index]) {
154 | return memo;
155 | }
156 | if (hideBlockedDomains && blocked[index]) {
157 | return memo;
158 | }
159 | if (filterLowerCase) {
160 | const lowerHref = link.href.toLowerCase();
161 | if (lowerHref.indexOf(filterLowerCase) < 0) {
162 | return memo;
163 | }
164 | }
165 | const itemClassName = cx('LinkListItem', {
166 | 'LinkListItem--blocked': blocked[index],
167 | 'LinkListItem--duplicate': duplicates[index],
168 | });
169 | memo.push(
170 |
171 | {link.href}
172 |
173 | );
174 | return memo;
175 | }, []);
176 |
177 | return (
178 |
179 |
{props.source}
180 |
206 |
209 |
210 | );
211 | }
212 |
--------------------------------------------------------------------------------
/src/components/LinkListEmpty.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function LinkListEmpty (props) {
4 | return (
5 |
6 |
{props.source}
7 |
8 | No links were found.
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/LinkListExpired.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function LinkListExpired (props) {
4 | return (
5 |
6 |
Expired
7 |
8 | Link information has expired and is no longer available.
9 | Please close this tab and try again.
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/Options.css:
--------------------------------------------------------------------------------
1 | .BadDomainsActionCol {
2 | width: 5em;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/Options.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import './Options.css';
3 |
4 | export default function Options(props) {
5 | return (
6 |
7 |
8 |
9 |
Blocked Domains
10 |
11 |
15 |
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | function BlockedDomainsEditor(props) {
23 | const [saved, setSaved] = useState(false);
24 |
25 | const onSubmit = (event) => {
26 | event.preventDefault();
27 | setSaved(false);
28 | const formData = new FormData(event.target);
29 | props.setBlockedDomains(formData.get('blockedDomains').split('\n'));
30 | setSaved(true);
31 | };
32 |
33 | const blockedDomainsText = props.blockedDomains.join('\n');
34 |
35 | return (
36 |
68 | );
69 | }
--------------------------------------------------------------------------------
/src/contentscript.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | const elements = document.querySelectorAll('a:link:not([href^=javascript])');
3 | const links = new Array(elements.length);
4 | for (let i = 0; i < elements.length; i++) {
5 | links[i] = {
6 | hash: elements[i].hash,
7 | host: elements[i].host,
8 | hostname: elements[i].hostname,
9 | href: elements[i].href,
10 | origin: elements[i].origin,
11 | pathname: elements[i].pathname,
12 | search: elements[i].search,
13 | text: elements[i].text,
14 | };
15 | }
16 | chrome.runtime.sendMessage(null, {type: 'links-found', links: links});
17 | })();
18 |
--------------------------------------------------------------------------------
/src/links.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {createRoot} from 'react-dom/client';
3 | import LinkList from './components/LinkList';
4 |
5 | const target = document.getElementById('LinkList');
6 | const root = createRoot(target);
7 |
8 | function blockedDomainsSet(blockedDomains) {
9 | const set = new Set();
10 | for (let domain of blockedDomains) {
11 | domain = domain.trim().toLowerCase();
12 | if (!domain || domain[0] == '#') {
13 | continue;
14 | }
15 | set.add(domain);
16 | }
17 | return set;
18 | }
19 |
20 | (async function() {
21 | const queryParams = new URLSearchParams(window.location.search);
22 | const session = await chrome.storage.session.get('tabData');
23 | const data = session?.tabData[queryParams.get('tab_id')];
24 | if (!data) {
25 | root.render();
26 | return;
27 | }
28 | const {blockedDomains} = await chrome.storage.sync.get(['blockedDomains']);
29 | document.title = 'Extracted Links for ' + data.source;
30 | root.render(
31 |
36 | );
37 | })();
--------------------------------------------------------------------------------
/src/options.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {createRoot} from 'react-dom/client';
3 | import Options from './components/Options';
4 |
5 | function setBlockedDomains(domains) {
6 | const next = [];
7 | for (let domain of domains) {
8 | domain = domain.trim();
9 | if (!domain) {
10 | continue;
11 | }
12 | next.push(domain);
13 | }
14 | chrome.storage.sync.set({blockedDomains: next});
15 | }
16 |
17 | const root = createRoot(document.getElementById('Options'));
18 |
19 | function render(storage) {
20 | root.render(
21 |
24 | );
25 | }
26 |
27 | let stored = {};
28 |
29 | chrome.storage.onChanged.addListener((changes, areaName) => {
30 | for (let key in changes) {
31 | stored[key] = changes[key].newValue;
32 | }
33 | render(stored);
34 | });
35 |
36 | chrome.storage.sync.get(null, items => {
37 | stored = items;
38 | if (stored.blockedDomains == null) {
39 | stored.blockedDomains = [];
40 | }
41 | render(stored);
42 | });
43 |
--------------------------------------------------------------------------------
/src/service_worker.js:
--------------------------------------------------------------------------------
1 | const DEFAULT_SETTINGS = {
2 | blockedDomains: ['bad1.example.com', 'bad2.example.com', 'bad4.example.com'],
3 | };
4 |
5 | const DEFAULT_SESSION = {
6 | tabData: {},
7 | };
8 |
9 | function warnLastError() {
10 | if (chrome.runtime.lastError) {
11 | console.warn(chrome.runtime.lastError); // eslint-disable-line
12 | }
13 | }
14 |
15 | chrome.runtime.onInstalled.addListener(() => {
16 | chrome.storage.sync.get(DEFAULT_SETTINGS, options => {
17 | chrome.storage.sync.set(options);
18 | });
19 | chrome.contextMenus.create({
20 | id: 'Link Grabber',
21 | title: 'Link Grabber',
22 | contexts: ['page'],
23 | documentUrlPatterns: ['http://*/*', 'https://*/*', 'file://*/*'],
24 | }, warnLastError);
25 | });
26 |
27 | chrome.action.onClicked.addListener((tab) => {
28 | chrome.scripting.executeScript({
29 | target: {tabId: tab.id},
30 | files: ['js/contentscript.js'],
31 | });
32 | });
33 |
34 | chrome.contextMenus.onClicked.addListener((info, tab) => {
35 | chrome.scripting.executeScript({
36 | target: {tabId: tab.id},
37 | files: ['js/contentscript.js'],
38 | });
39 | });
40 |
41 | chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
42 | chrome.storage.session.get(DEFAULT_SESSION).then(session => {
43 | delete session.tabData[tabId];
44 | chrome.storage.session.set(session);
45 | });
46 | });
47 |
48 | chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
49 | if (msg.type === 'links-found') {
50 | const tab = sender.tab;
51 | chrome.storage.session.get(DEFAULT_SESSION).then(session => {
52 | session.tabData[tab.id] = {
53 | source: tab.url,
54 | links: msg.links,
55 | };
56 | return chrome.storage.session.set(session);
57 | }).then(() => {
58 | chrome.tabs.create({
59 | index: tab.index + 1,
60 | openerTabId: tab.id,
61 | url: chrome.runtime.getURL('html/links.html') + '?tab_id=' + String(tab.id),
62 | });
63 | });
64 | return;
65 | }
66 | });
--------------------------------------------------------------------------------
/vendor/bootstrap/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2015 Twitter, Inc
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/bootstrap/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.4.1 (https://getbootstrap.com/)
3 | * Copyright 2011-2019 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | .btn-default,
7 | .btn-primary,
8 | .btn-success,
9 | .btn-info,
10 | .btn-warning,
11 | .btn-danger {
12 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
13 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
14 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
15 | }
16 | .btn-default:active,
17 | .btn-primary:active,
18 | .btn-success:active,
19 | .btn-info:active,
20 | .btn-warning:active,
21 | .btn-danger:active,
22 | .btn-default.active,
23 | .btn-primary.active,
24 | .btn-success.active,
25 | .btn-info.active,
26 | .btn-warning.active,
27 | .btn-danger.active {
28 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
29 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
30 | }
31 | .btn-default.disabled,
32 | .btn-primary.disabled,
33 | .btn-success.disabled,
34 | .btn-info.disabled,
35 | .btn-warning.disabled,
36 | .btn-danger.disabled,
37 | .btn-default[disabled],
38 | .btn-primary[disabled],
39 | .btn-success[disabled],
40 | .btn-info[disabled],
41 | .btn-warning[disabled],
42 | .btn-danger[disabled],
43 | fieldset[disabled] .btn-default,
44 | fieldset[disabled] .btn-primary,
45 | fieldset[disabled] .btn-success,
46 | fieldset[disabled] .btn-info,
47 | fieldset[disabled] .btn-warning,
48 | fieldset[disabled] .btn-danger {
49 | -webkit-box-shadow: none;
50 | box-shadow: none;
51 | }
52 | .btn-default .badge,
53 | .btn-primary .badge,
54 | .btn-success .badge,
55 | .btn-info .badge,
56 | .btn-warning .badge,
57 | .btn-danger .badge {
58 | text-shadow: none;
59 | }
60 | .btn:active,
61 | .btn.active {
62 | background-image: none;
63 | }
64 | .btn-default {
65 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
66 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
67 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
68 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
69 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
70 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
71 | background-repeat: repeat-x;
72 | border-color: #dbdbdb;
73 | text-shadow: 0 1px 0 #fff;
74 | border-color: #ccc;
75 | }
76 | .btn-default:hover,
77 | .btn-default:focus {
78 | background-color: #e0e0e0;
79 | background-position: 0 -15px;
80 | }
81 | .btn-default:active,
82 | .btn-default.active {
83 | background-color: #e0e0e0;
84 | border-color: #dbdbdb;
85 | }
86 | .btn-default.disabled,
87 | .btn-default[disabled],
88 | fieldset[disabled] .btn-default,
89 | .btn-default.disabled:hover,
90 | .btn-default[disabled]:hover,
91 | fieldset[disabled] .btn-default:hover,
92 | .btn-default.disabled:focus,
93 | .btn-default[disabled]:focus,
94 | fieldset[disabled] .btn-default:focus,
95 | .btn-default.disabled.focus,
96 | .btn-default[disabled].focus,
97 | fieldset[disabled] .btn-default.focus,
98 | .btn-default.disabled:active,
99 | .btn-default[disabled]:active,
100 | fieldset[disabled] .btn-default:active,
101 | .btn-default.disabled.active,
102 | .btn-default[disabled].active,
103 | fieldset[disabled] .btn-default.active {
104 | background-color: #e0e0e0;
105 | background-image: none;
106 | }
107 | .btn-primary {
108 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
109 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
110 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
111 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
113 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
114 | background-repeat: repeat-x;
115 | border-color: #245580;
116 | }
117 | .btn-primary:hover,
118 | .btn-primary:focus {
119 | background-color: #265a88;
120 | background-position: 0 -15px;
121 | }
122 | .btn-primary:active,
123 | .btn-primary.active {
124 | background-color: #265a88;
125 | border-color: #245580;
126 | }
127 | .btn-primary.disabled,
128 | .btn-primary[disabled],
129 | fieldset[disabled] .btn-primary,
130 | .btn-primary.disabled:hover,
131 | .btn-primary[disabled]:hover,
132 | fieldset[disabled] .btn-primary:hover,
133 | .btn-primary.disabled:focus,
134 | .btn-primary[disabled]:focus,
135 | fieldset[disabled] .btn-primary:focus,
136 | .btn-primary.disabled.focus,
137 | .btn-primary[disabled].focus,
138 | fieldset[disabled] .btn-primary.focus,
139 | .btn-primary.disabled:active,
140 | .btn-primary[disabled]:active,
141 | fieldset[disabled] .btn-primary:active,
142 | .btn-primary.disabled.active,
143 | .btn-primary[disabled].active,
144 | fieldset[disabled] .btn-primary.active {
145 | background-color: #265a88;
146 | background-image: none;
147 | }
148 | .btn-success {
149 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
150 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
151 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
152 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
153 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
154 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
155 | background-repeat: repeat-x;
156 | border-color: #3e8f3e;
157 | }
158 | .btn-success:hover,
159 | .btn-success:focus {
160 | background-color: #419641;
161 | background-position: 0 -15px;
162 | }
163 | .btn-success:active,
164 | .btn-success.active {
165 | background-color: #419641;
166 | border-color: #3e8f3e;
167 | }
168 | .btn-success.disabled,
169 | .btn-success[disabled],
170 | fieldset[disabled] .btn-success,
171 | .btn-success.disabled:hover,
172 | .btn-success[disabled]:hover,
173 | fieldset[disabled] .btn-success:hover,
174 | .btn-success.disabled:focus,
175 | .btn-success[disabled]:focus,
176 | fieldset[disabled] .btn-success:focus,
177 | .btn-success.disabled.focus,
178 | .btn-success[disabled].focus,
179 | fieldset[disabled] .btn-success.focus,
180 | .btn-success.disabled:active,
181 | .btn-success[disabled]:active,
182 | fieldset[disabled] .btn-success:active,
183 | .btn-success.disabled.active,
184 | .btn-success[disabled].active,
185 | fieldset[disabled] .btn-success.active {
186 | background-color: #419641;
187 | background-image: none;
188 | }
189 | .btn-info {
190 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
191 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
192 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
193 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
195 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
196 | background-repeat: repeat-x;
197 | border-color: #28a4c9;
198 | }
199 | .btn-info:hover,
200 | .btn-info:focus {
201 | background-color: #2aabd2;
202 | background-position: 0 -15px;
203 | }
204 | .btn-info:active,
205 | .btn-info.active {
206 | background-color: #2aabd2;
207 | border-color: #28a4c9;
208 | }
209 | .btn-info.disabled,
210 | .btn-info[disabled],
211 | fieldset[disabled] .btn-info,
212 | .btn-info.disabled:hover,
213 | .btn-info[disabled]:hover,
214 | fieldset[disabled] .btn-info:hover,
215 | .btn-info.disabled:focus,
216 | .btn-info[disabled]:focus,
217 | fieldset[disabled] .btn-info:focus,
218 | .btn-info.disabled.focus,
219 | .btn-info[disabled].focus,
220 | fieldset[disabled] .btn-info.focus,
221 | .btn-info.disabled:active,
222 | .btn-info[disabled]:active,
223 | fieldset[disabled] .btn-info:active,
224 | .btn-info.disabled.active,
225 | .btn-info[disabled].active,
226 | fieldset[disabled] .btn-info.active {
227 | background-color: #2aabd2;
228 | background-image: none;
229 | }
230 | .btn-warning {
231 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
232 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
233 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
234 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
235 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
236 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
237 | background-repeat: repeat-x;
238 | border-color: #e38d13;
239 | }
240 | .btn-warning:hover,
241 | .btn-warning:focus {
242 | background-color: #eb9316;
243 | background-position: 0 -15px;
244 | }
245 | .btn-warning:active,
246 | .btn-warning.active {
247 | background-color: #eb9316;
248 | border-color: #e38d13;
249 | }
250 | .btn-warning.disabled,
251 | .btn-warning[disabled],
252 | fieldset[disabled] .btn-warning,
253 | .btn-warning.disabled:hover,
254 | .btn-warning[disabled]:hover,
255 | fieldset[disabled] .btn-warning:hover,
256 | .btn-warning.disabled:focus,
257 | .btn-warning[disabled]:focus,
258 | fieldset[disabled] .btn-warning:focus,
259 | .btn-warning.disabled.focus,
260 | .btn-warning[disabled].focus,
261 | fieldset[disabled] .btn-warning.focus,
262 | .btn-warning.disabled:active,
263 | .btn-warning[disabled]:active,
264 | fieldset[disabled] .btn-warning:active,
265 | .btn-warning.disabled.active,
266 | .btn-warning[disabled].active,
267 | fieldset[disabled] .btn-warning.active {
268 | background-color: #eb9316;
269 | background-image: none;
270 | }
271 | .btn-danger {
272 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
273 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
274 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
275 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
276 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
277 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
278 | background-repeat: repeat-x;
279 | border-color: #b92c28;
280 | }
281 | .btn-danger:hover,
282 | .btn-danger:focus {
283 | background-color: #c12e2a;
284 | background-position: 0 -15px;
285 | }
286 | .btn-danger:active,
287 | .btn-danger.active {
288 | background-color: #c12e2a;
289 | border-color: #b92c28;
290 | }
291 | .btn-danger.disabled,
292 | .btn-danger[disabled],
293 | fieldset[disabled] .btn-danger,
294 | .btn-danger.disabled:hover,
295 | .btn-danger[disabled]:hover,
296 | fieldset[disabled] .btn-danger:hover,
297 | .btn-danger.disabled:focus,
298 | .btn-danger[disabled]:focus,
299 | fieldset[disabled] .btn-danger:focus,
300 | .btn-danger.disabled.focus,
301 | .btn-danger[disabled].focus,
302 | fieldset[disabled] .btn-danger.focus,
303 | .btn-danger.disabled:active,
304 | .btn-danger[disabled]:active,
305 | fieldset[disabled] .btn-danger:active,
306 | .btn-danger.disabled.active,
307 | .btn-danger[disabled].active,
308 | fieldset[disabled] .btn-danger.active {
309 | background-color: #c12e2a;
310 | background-image: none;
311 | }
312 | .thumbnail,
313 | .img-thumbnail {
314 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
315 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
316 | }
317 | .dropdown-menu > li > a:hover,
318 | .dropdown-menu > li > a:focus {
319 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
320 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
321 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
322 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
323 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
324 | background-repeat: repeat-x;
325 | background-color: #e8e8e8;
326 | }
327 | .dropdown-menu > .active > a,
328 | .dropdown-menu > .active > a:hover,
329 | .dropdown-menu > .active > a:focus {
330 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
331 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
332 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
333 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
334 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
335 | background-repeat: repeat-x;
336 | background-color: #2e6da4;
337 | }
338 | .navbar-default {
339 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
340 | background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
342 | background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
344 | background-repeat: repeat-x;
345 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
346 | border-radius: 4px;
347 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
348 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
349 | }
350 | .navbar-default .navbar-nav > .open > a,
351 | .navbar-default .navbar-nav > .active > a {
352 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
353 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
354 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
355 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
356 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
357 | background-repeat: repeat-x;
358 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
359 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
360 | }
361 | .navbar-brand,
362 | .navbar-nav > li > a {
363 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
364 | }
365 | .navbar-inverse {
366 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
367 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
368 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
369 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
370 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
371 | background-repeat: repeat-x;
372 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
373 | border-radius: 4px;
374 | }
375 | .navbar-inverse .navbar-nav > .open > a,
376 | .navbar-inverse .navbar-nav > .active > a {
377 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
378 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
379 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
380 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
381 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
382 | background-repeat: repeat-x;
383 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
384 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
385 | }
386 | .navbar-inverse .navbar-brand,
387 | .navbar-inverse .navbar-nav > li > a {
388 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
389 | }
390 | .navbar-static-top,
391 | .navbar-fixed-top,
392 | .navbar-fixed-bottom {
393 | border-radius: 0;
394 | }
395 | @media (max-width: 767px) {
396 | .navbar .navbar-nav .open .dropdown-menu > .active > a,
397 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
398 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
399 | color: #fff;
400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
405 | background-repeat: repeat-x;
406 | }
407 | }
408 | .alert {
409 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
410 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
411 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
412 | }
413 | .alert-success {
414 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
415 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
416 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
417 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
418 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
419 | background-repeat: repeat-x;
420 | border-color: #b2dba1;
421 | }
422 | .alert-info {
423 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
424 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
425 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
426 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
427 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
428 | background-repeat: repeat-x;
429 | border-color: #9acfea;
430 | }
431 | .alert-warning {
432 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
433 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
435 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
437 | background-repeat: repeat-x;
438 | border-color: #f5e79e;
439 | }
440 | .alert-danger {
441 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
442 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
443 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
444 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
445 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
446 | background-repeat: repeat-x;
447 | border-color: #dca7a7;
448 | }
449 | .progress {
450 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
451 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
453 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
455 | background-repeat: repeat-x;
456 | }
457 | .progress-bar {
458 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
459 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
461 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
463 | background-repeat: repeat-x;
464 | }
465 | .progress-bar-success {
466 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
467 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
469 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
471 | background-repeat: repeat-x;
472 | }
473 | .progress-bar-info {
474 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
475 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
476 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
477 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
478 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
479 | background-repeat: repeat-x;
480 | }
481 | .progress-bar-warning {
482 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
483 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
484 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
485 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
486 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
487 | background-repeat: repeat-x;
488 | }
489 | .progress-bar-danger {
490 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
491 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
492 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
493 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
494 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
495 | background-repeat: repeat-x;
496 | }
497 | .progress-bar-striped {
498 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
499 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
500 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
501 | }
502 | .list-group {
503 | border-radius: 4px;
504 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
505 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
506 | }
507 | .list-group-item.active,
508 | .list-group-item.active:hover,
509 | .list-group-item.active:focus {
510 | text-shadow: 0 -1px 0 #286090;
511 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
512 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
513 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
514 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
515 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
516 | background-repeat: repeat-x;
517 | border-color: #2b669a;
518 | }
519 | .list-group-item.active .badge,
520 | .list-group-item.active:hover .badge,
521 | .list-group-item.active:focus .badge {
522 | text-shadow: none;
523 | }
524 | .panel {
525 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
526 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
527 | }
528 | .panel-default > .panel-heading {
529 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
530 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
531 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
532 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
533 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
534 | background-repeat: repeat-x;
535 | }
536 | .panel-primary > .panel-heading {
537 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
538 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
539 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
540 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
541 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
542 | background-repeat: repeat-x;
543 | }
544 | .panel-success > .panel-heading {
545 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
546 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
547 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
548 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
549 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
550 | background-repeat: repeat-x;
551 | }
552 | .panel-info > .panel-heading {
553 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
554 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
555 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
556 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
557 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
558 | background-repeat: repeat-x;
559 | }
560 | .panel-warning > .panel-heading {
561 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
562 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
563 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
564 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
565 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
566 | background-repeat: repeat-x;
567 | }
568 | .panel-danger > .panel-heading {
569 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
570 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
571 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
572 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
573 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
574 | background-repeat: repeat-x;
575 | }
576 | .well {
577 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
578 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
579 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
580 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
581 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
582 | background-repeat: repeat-x;
583 | border-color: #dcdcdc;
584 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
585 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
586 | }
587 | /*# sourceMappingURL=bootstrap-theme.css.map */
--------------------------------------------------------------------------------
/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/7fffffff/linkgrabber/8d77e25b3ec003c6a511ceaf381c4a6a2e72953b/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------