├── .gitignore
├── src
├── component
│ ├── Body
│ │ ├── index.js
│ │ └── index.css
│ ├── Fontawesome
│ │ └── index.js
│ ├── utility
│ │ ├── randomNumber.js
│ │ ├── applyCSSVar.js
│ │ ├── clearChildNode.js
│ │ ├── isJson.js
│ │ ├── trimString.js
│ │ ├── isValidString.js
│ │ ├── averageColor.js
│ │ ├── minMax.js
│ │ ├── ordinalNumber.js
│ │ ├── dateTime.js
│ │ ├── sortArrayOfObject.js
│ │ ├── makePath.js
│ │ ├── isElementVisible.js
│ │ ├── set.js
│ │ ├── get.js
│ │ ├── complexNode.js
│ │ ├── ordinalWord.js
│ │ ├── node.js
│ │ ├── wordNumber.js
│ │ ├── convertColor.js
│ │ └── randomString.js
│ ├── Base
│ │ └── index.js
│ ├── Bookmark
│ │ ├── index.css
│ │ └── index.js
│ ├── Background
│ │ ├── index.css
│ │ └── index.js
│ ├── Theme
│ │ ├── index.css
│ │ └── index.js
│ ├── BookmarkLink
│ │ ├── index.css
│ │ └── index.js
│ ├── BookmarkOpenAll
│ │ ├── index.js
│ │ └── index.css
│ └── BookmarkGroup
│ │ ├── index.css
│ │ └── index.js
├── icon
│ ├── icon-16.png
│ ├── icon-48.png
│ ├── icon-128.png
│ ├── icon-512.png
│ ├── favicon.svg
│ └── icon-16.svg
├── index.js
├── app
│ ├── index.css
│ └── index.js
├── font
│ └── fa
│ │ ├── fa-brands-400.ttf
│ │ ├── fa-regular-400.ttf
│ │ ├── fa-solid-900.ttf
│ │ ├── fa-solid-900.woff2
│ │ ├── fa-brands-400.woff2
│ │ └── fa-regular-400.woff2
├── style
│ ├── zindex
│ │ └── index.css
│ ├── typography
│ │ └── index.css
│ └── reset
│ │ └── index.css
├── manifest.json
├── index.html
└── config.js
├── asset
└── screenshot
│ └── screenshot-001.gif
├── webpack.dev.js
├── .github
└── workflows
│ └── gh-pages-deploy.yml
├── package.json
├── readme.md
├── webpack.common.js
├── webpack.prod.js
└── license
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | dist
3 | node_modules
4 |
--------------------------------------------------------------------------------
/src/component/Body/index.js:
--------------------------------------------------------------------------------
1 | import './index.css';
2 |
3 | export const Body = function() {}
--------------------------------------------------------------------------------
/src/icon/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/icon/icon-16.png
--------------------------------------------------------------------------------
/src/icon/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/icon/icon-48.png
--------------------------------------------------------------------------------
/src/icon/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/icon/icon-128.png
--------------------------------------------------------------------------------
/src/icon/icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/icon/icon-512.png
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { App } from './app';
2 |
3 | const app = new App();
4 |
5 | app.render();
6 |
--------------------------------------------------------------------------------
/src/app/index.css:
--------------------------------------------------------------------------------
1 | .app {
2 | min-width: 100vw;
3 | min-height: 100vh;
4 | display: grid;
5 | }
6 |
--------------------------------------------------------------------------------
/src/component/Fontawesome/index.js:
--------------------------------------------------------------------------------
1 | import './index.css';
2 |
3 | export const Fontawesome = function() {}
4 |
--------------------------------------------------------------------------------
/src/font/fa/fa-brands-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/font/fa/fa-brands-400.ttf
--------------------------------------------------------------------------------
/src/font/fa/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/font/fa/fa-regular-400.ttf
--------------------------------------------------------------------------------
/src/font/fa/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/font/fa/fa-solid-900.ttf
--------------------------------------------------------------------------------
/src/font/fa/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/font/fa/fa-solid-900.woff2
--------------------------------------------------------------------------------
/src/style/zindex/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --z-index-background: 1000;
3 | --z-index-bookmark: 2000;
4 | }
5 |
--------------------------------------------------------------------------------
/src/font/fa/fa-brands-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/font/fa/fa-brands-400.woff2
--------------------------------------------------------------------------------
/src/font/fa/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/src/font/fa/fa-regular-400.woff2
--------------------------------------------------------------------------------
/asset/screenshot/screenshot-001.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zombieFox/voltTab/HEAD/asset/screenshot/screenshot-001.gif
--------------------------------------------------------------------------------
/src/component/utility/randomNumber.js:
--------------------------------------------------------------------------------
1 | export const randomNumber = (min, max) => {
2 |
3 | return Math.floor(Math.random() * (max - min + 1) + min);
4 |
5 | };
6 |
--------------------------------------------------------------------------------
/src/component/utility/applyCSSVar.js:
--------------------------------------------------------------------------------
1 | export const applyCSSVar = (name, value) => {
2 |
3 | const html = document.querySelector('html');
4 |
5 | html.style.setProperty(name, value);
6 |
7 | };
--------------------------------------------------------------------------------
/src/component/utility/clearChildNode.js:
--------------------------------------------------------------------------------
1 | export const clearChildNode = (element) => {
2 |
3 | while (element.lastChild) {
4 | element.removeChild(element.lastChild);
5 | }
6 |
7 | };
8 |
--------------------------------------------------------------------------------
/src/component/utility/isJson.js:
--------------------------------------------------------------------------------
1 | export const isJson = (string) => {
2 |
3 | try {
4 | JSON.parse(string);
5 | } catch (error) {
6 | return false;
7 | }
8 |
9 | return true;
10 |
11 | };
12 |
--------------------------------------------------------------------------------
/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 |
4 | module.exports = merge(common, {
5 | mode: 'development',
6 | devtool: 'inline-source-map'
7 | });
8 |
--------------------------------------------------------------------------------
/src/component/utility/trimString.js:
--------------------------------------------------------------------------------
1 | export const trimString = (value) => {
2 |
3 | if (typeof value == 'string') {
4 | return value.trim().replace(/\s\s+/g, ' ');
5 | } else {
6 | return value;
7 | }
8 |
9 | };
10 |
--------------------------------------------------------------------------------
/src/component/Base/index.js:
--------------------------------------------------------------------------------
1 | // must be loaded first
2 | import '../../style/reset/index.css';
3 |
4 | // base styles for all components
5 | import '../../style/typography/index.css';
6 | import '../../style/zindex/index.css';
7 |
8 | export const Base = function() {}
9 |
--------------------------------------------------------------------------------
/src/component/utility/isValidString.js:
--------------------------------------------------------------------------------
1 | export const isValidString = (value) => {
2 | let result = false;
3 |
4 | if (typeof value == 'string') {
5 | value = value.trim().replace(/\s/g, '');
6 | if (value != '') {
7 | result = true;
8 | }
9 | }
10 |
11 | return result;
12 | };
13 |
--------------------------------------------------------------------------------
/src/component/utility/averageColor.js:
--------------------------------------------------------------------------------
1 | export const averageColor = function(rgb1, rgb2) {
2 |
3 | return {
4 | r: Math.round(Math.sqrt(Math.pow(rgb1.r, 1.75) + Math.pow(rgb2.r, 1.75) / 2)),
5 | g: Math.round(Math.sqrt(Math.pow(rgb1.g, 1.75) + Math.pow(rgb2.g, 1.75) / 2)),
6 | b: Math.round(Math.sqrt(Math.pow(rgb1.b, 1.75) + Math.pow(rgb2.b, 1.75) / 2))
7 | };
8 |
9 | };
10 |
--------------------------------------------------------------------------------
/src/component/utility/minMax.js:
--------------------------------------------------------------------------------
1 | export const minMax = ({
2 | min = 0,
3 | max = 0,
4 | value = 0
5 | } = {}) => {
6 |
7 | if (value > max) {
8 |
9 | return max;
10 |
11 | } else if (value < min) {
12 |
13 | return min;
14 |
15 | } else if (isNaN(value)) {
16 |
17 | return min;
18 |
19 | } else {
20 |
21 | return value;
22 |
23 | }
24 |
25 | };
26 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Volt Tab",
3 | "short_name": "Volt Tab",
4 | "description": "",
5 | "version": "1.0.0",
6 | "manifest_version": 2,
7 | "chrome_url_overrides": {
8 | "newtab": "index.html"
9 | },
10 | "icons": {
11 | "16": "icon/icon-16.png",
12 | "48": "icon/icon-48.png",
13 | "128": "icon/icon-128.png",
14 | "512": "icon/icon-512.png"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/component/utility/ordinalNumber.js:
--------------------------------------------------------------------------------
1 | export const ordinalNumber = (number) => {
2 |
3 | var j = number % 10;
4 |
5 | var k = number % 100;
6 |
7 | if (j == 1 && k != 11) {
8 | return number + 'st';
9 | }
10 |
11 | if (j == 2 && k != 12) {
12 | return number + 'nd';
13 | }
14 |
15 | if (j == 3 && k != 13) {
16 | return number + 'rd';
17 | }
18 |
19 | return number + 'th';
20 |
21 | };
22 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Volt Tab
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/component/Body/index.css:
--------------------------------------------------------------------------------
1 | ::selection {
2 | background-color: rgb(var(--theme-accent));
3 | color: hsl(var(--theme-text));
4 | }
5 |
6 | html,
7 | body {
8 | background-color: hsl(var(--background-color-hsl));
9 | font-size: var(--font-size);
10 | line-height: 1.6;
11 | font-family: var(--theme-font);
12 | font-weight: var(--font-weight-regular);
13 | font-style: normal;
14 | color: hsl(var(--theme-text));
15 | }
16 |
17 | body {
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | justify-content: center;
22 | align-items: center;
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages-deploy.yml:
--------------------------------------------------------------------------------
1 | name: gh-pages-deploy
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | build-and-deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v2.3.1
14 | with:
15 | persist-credentials: false
16 | - name: Install and Build
17 | run: |
18 | npm install
19 | npm run build
20 | - name: Deploy
21 | uses: JamesIves/github-pages-deploy-action@4.1.3
22 | with:
23 | branch: gh-pages
24 | folder: dist/web
--------------------------------------------------------------------------------
/src/icon/favicon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/icon/icon-16.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/component/utility/dateTime.js:
--------------------------------------------------------------------------------
1 | export const dateTime = () => {
2 |
3 | const date = new Date();
4 |
5 | const month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
6 |
7 | return {
8 | // string: date.constructor(),
9 | // time: date.getTime(),
10 | date: date.getDate(),
11 | day: date.getDay(),
12 | year: date.getFullYear(),
13 | hours: date.getHours(),
14 | milliseconds: date.getMilliseconds(),
15 | minutes: date.getMinutes(),
16 | month: date.getMonth(),
17 | monthString: month[date.getMonth()],
18 | seconds: date.getSeconds()
19 | };
20 |
21 | };
22 |
--------------------------------------------------------------------------------
/src/component/utility/sortArrayOfObject.js:
--------------------------------------------------------------------------------
1 | import { get } from './get';
2 |
3 | export const sortArrayOfObject = (array, key) => {
4 |
5 | array.sort((a, b) => {
6 |
7 | let textA = get({
8 | object: a,
9 | path: key
10 | });
11 |
12 | if (typeof textA == 'string') {
13 | textA = textA.toLowerCase();
14 | }
15 |
16 | let textB = get({
17 | object: b,
18 | path: key
19 | });
20 |
21 | if (typeof textB == 'string') {
22 | textB = textB.toLowerCase();
23 | }
24 |
25 | if (textA < textB) {
26 | return -1;
27 | } else if (textA > textB) {
28 | return 1;
29 | } else {
30 | return 0;
31 | }
32 |
33 | });
34 |
35 | return array;
36 |
37 | };
38 |
--------------------------------------------------------------------------------
/src/component/utility/makePath.js:
--------------------------------------------------------------------------------
1 | export const makePath = (string) => {
2 |
3 | if (string) {
4 |
5 | let array;
6 |
7 | if (string.indexOf('[') != -1 && string.indexOf(']') != -1) {
8 |
9 | array = string.split('.').join(',').split('[').join(',').split(']').join(',').split(',');
10 |
11 | for (var i = 0; i < array.length; i++) {
12 | if (array[i] == '') {
13 | array.splice(i, 1);
14 | }
15 | if (!isNaN(parseInt(array[i], 10))) {
16 | array[i] = parseInt(array[i], 10);
17 | }
18 | }
19 |
20 | } else {
21 |
22 | array = string.split('.');
23 |
24 | }
25 |
26 | return array;
27 |
28 | } else {
29 |
30 | return false;
31 |
32 | }
33 |
34 | };
35 |
--------------------------------------------------------------------------------
/src/component/utility/isElementVisible.js:
--------------------------------------------------------------------------------
1 | export const isElementVisible = (element) => {
2 |
3 | var rect = element.getBoundingClientRect();
4 |
5 | const vWidth = window.innerWidth;
6 |
7 | const vHeight = window.innerHeight;
8 |
9 | const efp = (x, y) => {
10 | return document.elementFromPoint(x, y);
11 | };
12 |
13 | // Return false if element is not in the viewport
14 | if (
15 | rect.right < 0 ||
16 | rect.bottom < 0 ||
17 | rect.left > vWidth ||
18 | rect.top > vHeight
19 | ) {
20 | return false;
21 | }
22 |
23 | // Return true if any of the element four corners are visible
24 | return (
25 | element.contains(efp(rect.left, rect.top)) ||
26 | element.contains(efp(rect.right, rect.top)) ||
27 | element.contains(efp(rect.right, rect.bottom)) ||
28 | element.contains(efp(rect.left, rect.bottom))
29 | );
30 |
31 | };
32 |
--------------------------------------------------------------------------------
/src/component/Bookmark/index.css:
--------------------------------------------------------------------------------
1 | .bookmark {
2 | grid-column: 1 / -1;
3 | grid-row: 1 / -1;
4 | display: grid;
5 | grid-template-columns: calc(var(--bookmark-panel) * 1%) 1fr calc(var(--bookmark-panel) * 1%);
6 | align-items: center;
7 | z-index: var(--z-index-bookmark);
8 | }
9 |
10 | .bookmark-panel {
11 | grid-row: 1 / -1;
12 | align-self: normal;
13 | background-color:
14 | hsla(var(--theme-bookmark-background-color-hsla-h),
15 | calc(var(--theme-bookmark-background-color-hsla-s) * 1%),
16 | calc(var(--theme-bookmark-background-color-hsla-l) * 1%),
17 | var(--theme-bookmark-background-color-hsla-a));
18 | backdrop-filter: blur(calc(var(--theme-bookmark-group-background-blur) * 1px));
19 | pointer-events: none;
20 | z-index: 1;
21 | }
22 |
23 | .bookmark-panel-left {
24 | grid-column: 1 / 2;
25 | }
26 |
27 | .bookmark-panel-right {
28 | grid-column: 3 / 4;
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/index.js:
--------------------------------------------------------------------------------
1 | import { Base } from '../component/Base';
2 | import { Body } from '../component/Body';
3 | import { Background } from '../component/Background';
4 | import { Bookmark } from '../component/Bookmark';
5 | import { Theme } from '../component/Theme';
6 | import { Fontawesome } from '../component/Fontawesome';
7 |
8 | import './index.css';
9 |
10 | export const App = function() {
11 |
12 | this.node = {
13 | app: document.createElement('div')
14 | }
15 |
16 | this.base = new Base();
17 |
18 | this.body = new Body();
19 |
20 | this.theme = new Theme();
21 |
22 | this.fontawesome = new Fontawesome();
23 |
24 | this.background = new Background();
25 |
26 | this.bookmark = new Bookmark();
27 |
28 | this.render = () => {
29 |
30 | this.node.app.classList.add('app');
31 |
32 | this.theme.render();
33 |
34 | this.background.render(this.node.app);
35 |
36 | this.bookmark.render(this.node.app);
37 |
38 | document.querySelector('body').appendChild(this.node.app);
39 |
40 | }
41 |
42 | };
43 |
--------------------------------------------------------------------------------
/src/component/utility/set.js:
--------------------------------------------------------------------------------
1 | import { makePath } from './makePath.js';
2 |
3 | export const set = ({
4 | object = null,
5 | path = null,
6 | value = null
7 | } = {}) => {
8 |
9 | const address = makePath(path);
10 |
11 | const setValue = () => {
12 |
13 | while (address.length > 1) {
14 |
15 | // shift off and store the first
16 | let currentKey = address.shift();
17 |
18 | // if the key is not found make a new object
19 | if (!(currentKey in object)) {
20 | // make an empty object in the current object level
21 | if (isNaN(currentKey)) {
22 | object[currentKey] = {};
23 | } else {
24 | object[currentKey] = [];
25 | }
26 | }
27 |
28 | // drill down the object with the first key
29 | object = object[currentKey];
30 |
31 | }
32 |
33 | let finalKey = address.shift();
34 |
35 | object[finalKey] = value;
36 |
37 | };
38 |
39 | if (object != null && path != null && value != null) {
40 | setValue();
41 | } else {
42 | return false;
43 | }
44 |
45 | };
46 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Volt Tab",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack serve --open --config webpack.dev.js",
8 | "build": "webpack --config webpack.prod.js"
9 | },
10 | "keywords": [
11 | "startpage",
12 | "start-page",
13 | "newtabpage",
14 | "new-tab-page",
15 | "tab",
16 | "chrome-extension",
17 | "extension",
18 | "bookmarks",
19 | "links",
20 | "voltTab",
21 | "volt tab"
22 | ],
23 | "author": "zombieFox",
24 | "license": "GPL-3",
25 | "devDependencies": {
26 | "copy-webpack-plugin": "^11.0.0",
27 | "css-loader": "^6.7.1",
28 | "css-minimizer-webpack-plugin": "^4.0.0",
29 | "html-webpack-plugin": "^5.5.0",
30 | "mini-css-extract-plugin": "^2.6.0",
31 | "sortablejs": "^1.15.0",
32 | "style-loader": "^3.3.1",
33 | "webfontloader": "^1.6.28",
34 | "webpack": "^5.72.1",
35 | "webpack-cli": "^4.9.2",
36 | "webpack-dev-server": "^4.9.0",
37 | "webpack-merge": "^5.8.0",
38 | "zip-webpack-plugin": "^4.0.1"
39 | }
40 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Volt Tab
2 |
3 | A new tab page with bookmark groups and open all controls.
4 |
5 | [](https://zombiefox.github.io/voltTab/)
6 |
7 | ## [See the demo in action](https://zombiefox.github.io/voltTab/)
8 |
9 | * * *
10 |
11 | ## Development
12 |
13 | When developing use:
14 |
15 | - `npm start`
16 |
17 | A development server will automatically open the project in your browser. Normally here: `http://localhost:8080`.
18 |
19 | To build the project use:
20 |
21 | - `npm run build`
22 |
23 | A web ready folder will be created in `/dist/web/`.
24 | A browser addon/extension ready zip will be created in `/dist/extension/`.
25 |
26 | ## Customise
27 |
28 | Edit the [`src/config.js`](src/config.js) file to change the bookmarks, colours and background.
29 |
30 | All colours are defined as [HSL](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl) values in an array. Eg:
31 |
32 | A colour defined as:
33 |
34 | ```
35 | hsl: [204, 100, 72]
36 | ```
37 |
38 | Will be converted to CSS as:
39 |
40 | ```
41 | selector {
42 | color: hsl(204, 100%, 72%);
43 | }
44 | ```
--------------------------------------------------------------------------------
/src/component/utility/get.js:
--------------------------------------------------------------------------------
1 | import { makePath } from './makePath.js';
2 |
3 | export const get = ({
4 | object = null,
5 | path = null
6 | } = {}) => {
7 |
8 | const address = makePath(path);
9 |
10 | const getValue = () => {
11 |
12 | while (address.length > 1) {
13 |
14 | // shift off and store the first key
15 | let currentKey = address.shift();
16 |
17 | // if the key is not found make a new object
18 | if (!(currentKey in object)) {
19 | // make an empty object in the current object level
20 | if (isNaN(currentKey)) {
21 | object[currentKey] = {};
22 | } else {
23 | object[currentKey] = [];
24 | }
25 | }
26 |
27 | // drill down the object with the first key
28 | object = object[currentKey];
29 |
30 | }
31 |
32 | let finalKey = address.shift();
33 |
34 | if (!(finalKey in object)) {
35 | return '';
36 | } else {
37 | return object[finalKey];
38 | }
39 |
40 | };
41 |
42 | if (object != null && path != null) {
43 | return getValue();
44 | } else {
45 | return false;
46 | }
47 |
48 | };
49 |
--------------------------------------------------------------------------------
/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const CopyPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | entry: {
7 | index: path.resolve(__dirname, 'src', 'index.js')
8 | },
9 | output: {
10 | filename: '[name].[contenthash].js',
11 | path: path.resolve(__dirname, 'dist/web'),
12 | clean: true
13 | },
14 | module: {
15 | rules: [{
16 | test: /\.css$/i,
17 | use: ['style-loader', 'css-loader']
18 | }, {
19 | test: /\.(ttf|woff|woff2)$/,
20 | type: 'asset/resource',
21 | generator: {
22 | filename: 'font/[name][ext]',
23 | }
24 | }, {
25 | test: /\.(jpe?g|png|gif|svg)$/i,
26 | type: 'asset/resource',
27 | generator: {
28 | filename: 'image/[name][ext]',
29 | }
30 | }]
31 | },
32 | plugins: [
33 | new HtmlWebpackPlugin({
34 | template: './src/index.html'
35 | }),
36 | new CopyPlugin({
37 | patterns: [{
38 | from: './src/manifest.json',
39 | to: './manifest.json'
40 | }, {
41 | from: './src/icon/',
42 | to: './icon/'
43 | }]
44 | })
45 | ]
46 | };
47 |
--------------------------------------------------------------------------------
/src/component/Background/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --background-color-hsl:
3 | var(--background-color-hsl-h),
4 | calc(var(--background-color-hsl-s) * 1%),
5 | calc(var(--background-color-hsl-l) * 1%);
6 | }
7 |
8 | .background {
9 | display: block;
10 | position: relative;
11 | grid-column: 1 / -1;
12 | grid-row: 1 / -1;
13 | pointer-events: none;
14 | z-index: var(--z-index-background);
15 | }
16 |
17 | .background-color,
18 | .background-image,
19 | .background-gradient {
20 | position: absolute;
21 | top: 0;
22 | left: 0;
23 | width: 100%;
24 | height: 100%;
25 | }
26 |
27 | .background-color {
28 | background-color: hsl(var(--background-color-hsl));
29 | z-index: 1;
30 | }
31 |
32 | .background-image {
33 | background-image: var(--background-image-url);
34 | background-attachment: fixed;
35 | background-size: cover;
36 | background-position: center;
37 | opacity: var(--background-image-opacity);
38 | filter: blur(calc(var(--background-image-blur) * 1px)) grayscale(var(--background-image-grayscale));
39 | z-index: 2;
40 | }
41 |
42 | .background-gradient {
43 | background: linear-gradient(var(--background-gradient-degree), var(--background-gradient-color));
44 | z-index: 3;
45 | }
46 |
--------------------------------------------------------------------------------
/src/component/utility/complexNode.js:
--------------------------------------------------------------------------------
1 | export const complexNode = ({
2 | tag = 'div',
3 | text = false,
4 | complexText = false,
5 | attr = [],
6 | node = []
7 | } = {}) => {
8 |
9 | const element = document.createElement(tag);
10 |
11 | if (text) {
12 |
13 | if (complexText) {
14 |
15 | element.innerHTML = text;
16 |
17 | } else {
18 |
19 | let textNode = document.createTextNode(text);
20 |
21 | element.appendChild(textNode);
22 |
23 | }
24 |
25 | }
26 |
27 | if (attr.length > 0) {
28 | attr.forEach((item) => {
29 |
30 | if ('key' in item && 'value' in item) {
31 | element.setAttribute(item.key, item.value);
32 | } else if ('key' in item) {
33 | element.setAttribute(item.key, '');
34 | }
35 |
36 | });
37 | }
38 |
39 | if (node) {
40 | if (typeof node != 'string') {
41 | if (node.length > 0) {
42 |
43 | node.forEach((item) => {
44 | if (item instanceof HTMLElement) {
45 | element.appendChild(item);
46 | }
47 | });
48 |
49 | } else {
50 |
51 | if (node instanceof HTMLElement) {
52 | element.appendChild(node);
53 | }
54 |
55 | }
56 | }
57 | }
58 |
59 | return element;
60 |
61 | };
62 |
--------------------------------------------------------------------------------
/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 | const path = require('path');
4 | const ZipPlugin = require('zip-webpack-plugin');
5 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
6 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
7 | const TerserPlugin = require('terser-webpack-plugin');
8 |
9 | const version = require('./src/manifest.json').version;
10 | const name = require('./src/manifest.json').name;
11 |
12 | module.exports = merge(common, {
13 | mode: 'production',
14 | optimization: {
15 | minimize: true,
16 | minimizer: [
17 | new CssMinimizerPlugin({
18 | minify: CssMinimizerPlugin.cleanCssMinify
19 | }),
20 | new TerserPlugin({
21 | terserOptions: {
22 | format: {
23 | comments: false,
24 | },
25 | },
26 | extractComments: false,
27 | })
28 | ]
29 | },
30 | module: {
31 | rules: [{
32 | test: /\.css$/i,
33 | use: [MiniCssExtractPlugin.loader, 'css-loader'],
34 | }],
35 | },
36 | plugins: [
37 | new MiniCssExtractPlugin({
38 | filename: '[name].[contenthash].css'
39 | }),
40 | new ZipPlugin({
41 | path: path.resolve(__dirname, 'dist/extension'),
42 | filename: name + ' ' + version + '.zip'
43 | })
44 | ]
45 | });
46 |
--------------------------------------------------------------------------------
/src/component/utility/ordinalWord.js:
--------------------------------------------------------------------------------
1 | export const ordinalWord = (word) => {
2 |
3 | const endsWithDoubleZeroPattern = /(hundred|thousand|(m|b|tr|quadr)illion)$/;
4 |
5 | const endsWithTeenPattern = /teen$/;
6 |
7 | const endsWithYPattern = /y$/;
8 |
9 | const endsWithZeroThroughTwelvePattern = /(Zero|One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Eleven|Twelve)$/;
10 |
11 | const ordinalLessThanThirteen = {
12 | Zero: 'Zeroth',
13 | One: 'First',
14 | Two: 'Second',
15 | Three: 'Third',
16 | Four: 'Fourth',
17 | Five: 'Fifth',
18 | Six: 'Sixth',
19 | Seven: 'Seventh',
20 | Eight: 'Eighth',
21 | Nine: 'Ninth',
22 | Ten: 'Tenth',
23 | Eleven: 'Eleventh',
24 | Twelve: 'Twelfth'
25 | };
26 |
27 | const replaceWithOrdinalVariant = (match, numberWord) => {
28 | return ordinalLessThanThirteen[numberWord];
29 | };
30 |
31 | // Ends with *00 (100, 1000, etc.) or *teen (13, 14, 15, 16, 17, 18, 19)
32 | if (endsWithDoubleZeroPattern.test(word) || endsWithTeenPattern.test(word)) {
33 | return word + 'th';
34 | // Ends with *y (20, 30, 40, 50, 60, 70, 80, 90)
35 | } else if (endsWithYPattern.test(word)) {
36 | return word.replace(endsWithYPattern, 'ieth');
37 | // Ends with one through twelve
38 | } else if (endsWithZeroThroughTwelvePattern.test(word)) {
39 | return word.replace(endsWithZeroThroughTwelvePattern, replaceWithOrdinalVariant);
40 | }
41 |
42 | return word;
43 |
44 | };
45 |
--------------------------------------------------------------------------------
/src/component/Theme/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --font-size: calc(var(--theme-scale) * 0.025vmax);
3 | --font-weight-light: 300;
4 | --font-weight-regular: 400;
5 | --font-weight-medium: 500;
6 | --font-weight-bold: 700;
7 | }
8 |
9 | :root {
10 | --theme-accent:
11 | hsl(var(--theme-accent-hsl-h),
12 | calc(var(--theme-accent-hsl-s) * 1%),
13 | calc(var(--theme-accent-hsl-l) * 1%));
14 | --theme-text:
15 | hsl(var(--theme-text-hsl-h),
16 | calc(var(--theme-text-hsl-s) * 1%),
17 | calc(var(--theme-text-hsl-l) * 1%));
18 | }
19 |
20 | :root {
21 | --theme-transition-xfast-bounce: calc(var(--theme-transition-speed-xfast) * 1s) cubic-bezier(var(--theme-easing-bounce));
22 | --theme-transition-fast-bounce: calc(var(--theme-transition-speed-fast) * 1s) cubic-bezier(var(--theme-easing-bounce));
23 | --theme-transition-medium-bounce: calc(var(--theme-transition-speed-medium) * 1s) cubic-bezier(var(--theme-easing-bounce));
24 | --theme-transition-slow-bounce: calc(var(--theme-transition-speed-slow) * 1s) cubic-bezier(var(--theme-easing-bounce));
25 | --theme-transition-xslow-bounce: calc(var(--theme-transition-speed-xslow) * 1s) cubic-bezier(var(--theme-easing-bounce));
26 | }
27 |
28 | :root {
29 | --theme-transition-xfast-ease: calc(var(--theme-transition-speed-xfast) * 1s) ease-in-out;
30 | --theme-transition-fast-ease: calc(var(--theme-transition-speed-fast) * 1s) ease-in-out;
31 | --theme-transition-medium-ease: calc(var(--theme-transition-speed-medium) * 1s) ease-in-out;
32 | --theme-transition-slow-ease: calc(var(--theme-transition-speed-slow) * 1s) ease-in-out;
33 | --theme-transition-xslow-ease: calc(var(--theme-transition-speed-xslow) * 1s) ease-in-out;
34 | }
35 |
--------------------------------------------------------------------------------
/src/component/BookmarkLink/index.css:
--------------------------------------------------------------------------------
1 | .bookmark-link:link,
2 | .bookmark-link:visited,
3 | .bookmark-link:hover,
4 | .bookmark-link:focus,
5 | .bookmark-link:active {
6 | font-size: calc((var(--bookmark-scale) / 10) * 1em);
7 | color: hsl(var(--bookmark-group-color-primary-hsl-h),
8 | calc(var(--bookmark-group-color-primary-hsl-s) * 1%),
9 | calc(var(--bookmark-group-color-primary-hsl-l) * 1%));
10 | text-decoration: none;
11 | transform: scale(var(--bookmark-icon-scale-hidden));
12 | transition: transform var(--theme-transition-medium-ease) calc(var(--bookmark-link-delay) * 1s);
13 | }
14 |
15 | .bookmark-group-item:focus-within .bookmark-link,
16 | .bookmark-group-active .bookmark-link {
17 | transform: scale(var(--bookmark-icon-scale-visible));
18 | transition: transform var(--theme-transition-slow-bounce) 0s;
19 | }
20 |
21 | .bookmark-group-item:focus-within .bookmark-link:hover,
22 | .bookmark-group-item:focus-within .bookmark-link:focus,
23 | .bookmark-group-active .bookmark-link:hover,
24 | .bookmark-group-active .bookmark-link:focus {
25 | transform: scale(var(--bookmark-icon-scale-hover));
26 | transition: transform var(--theme-transition-fast-bounce);
27 | }
28 |
29 | .bookmark-group-item:focus-within .bookmark-link:active,
30 | .bookmark-group-active .bookmark-link:active {
31 | transform: scale(var(--bookmark-icon-scale-active));
32 | transition: transform var(--theme-transition-fast-bounce);
33 | }
34 |
35 | .bookmark-visual {
36 | min-width: 2em;
37 | min-height: 2em;
38 | display: flex;
39 | align-items: center;
40 | justify-content: center;
41 | }
42 |
43 | .bookmark-letter {
44 | line-height: 1;
45 | }
46 |
47 | .bookmark-image {
48 | max-height: 1em;
49 | }
50 |
--------------------------------------------------------------------------------
/src/component/BookmarkLink/index.js:
--------------------------------------------------------------------------------
1 | import { config } from '../../config';
2 |
3 | import './index.css';
4 |
5 | export const BookmarkLink = function(linkData, listCount, index) {
6 |
7 | this.node = {
8 | link: document.createElement('a'),
9 | visual: document.createElement('div'),
10 | letter: document.createElement('div'),
11 | icon: document.createElement('span'),
12 | image: document.createElement('img')
13 | }
14 |
15 | this.render = () => {
16 |
17 | this.node.link.classList.add('bookmark-link');
18 |
19 | switch (config.bookmark.direction) {
20 |
21 | case 'left':
22 | this.node.link.style.setProperty('--bookmark-link-delay', ((listCount - index) / 20));
23 | break;
24 |
25 | case 'right':
26 | this.node.link.style.setProperty('--bookmark-link-delay', ((listCount - (listCount - index)) / 20));
27 | break;
28 |
29 | }
30 |
31 | this.node.link.href = linkData.url;
32 |
33 | if (config.bookmark.newTab) {
34 |
35 | this.node.link.setAttribute('target', '_blank');
36 |
37 | };
38 |
39 | this.node.visual.classList.add('bookmark-visual');
40 |
41 | this.node.letter.classList.add('bookmark-letter');
42 |
43 | this.node.icon.classList.add('bookmark-icon');
44 |
45 | this.node.image.classList.add('bookmark-image');
46 |
47 | if ('letter' in linkData) {
48 |
49 | this.node.letter.textContent = linkData.letter;
50 |
51 | this.node.visual.appendChild(this.node.letter);
52 |
53 | };
54 |
55 | if ('icon' in linkData) {
56 |
57 | const iconClassList = linkData.icon.split(' ');
58 |
59 | iconClassList.forEach(className => {
60 |
61 | this.node.icon.classList.add(className);
62 |
63 | });
64 |
65 | this.node.visual.appendChild(this.node.icon);
66 |
67 | };
68 |
69 | if ('image' in linkData) {
70 |
71 | this.node.image.src = linkData.image;
72 |
73 | this.node.visual.appendChild(this.node.image);
74 |
75 | };
76 |
77 | this.node.link.appendChild(this.node.visual);
78 |
79 | }
80 |
81 | this.link = () => this.node.link;
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/component/BookmarkOpenAll/index.js:
--------------------------------------------------------------------------------
1 | import { config } from '../../config';
2 |
3 | import './index.css';
4 |
5 | export const BookmarkOpenAll = function(bookmarkGroupData) {
6 |
7 | this.node = {
8 | openAll: document.createElement('div'),
9 | button: document.createElement('button'),
10 | icon: document.createElement('span'),
11 | line: document.createElement('div'),
12 | lineTop: document.createElement('span'),
13 | lineBottom: document.createElement('span')
14 | }
15 |
16 | this.open = () => {
17 |
18 | if ('tabs' in chrome) {
19 |
20 | if (config.bookmark.newTab) {
21 |
22 | bookmarkGroupData.list.forEach(item => {
23 | chrome.tabs.create({ url: item.url, active: false });
24 | });
25 |
26 | } else {
27 |
28 | const first = bookmarkGroupData.list.shift();
29 |
30 | bookmarkGroupData.list.forEach(item => {
31 | chrome.tabs.create({ url: item.url, active: false });
32 | });
33 |
34 | window.location.href = first.url;
35 |
36 | }
37 |
38 | } else {
39 |
40 | bookmarkGroupData.list.forEach((link, index) => {
41 | window.open(link.url, '_blank');
42 | });
43 |
44 | }
45 |
46 | }
47 |
48 | this.render = () => {
49 |
50 | this.node.openAll.classList.add('bookmark-open-all');
51 |
52 | this.node.button.classList.add('bookmark-open-all-button');
53 |
54 | this.node.button.addEventListener('click', () => {
55 |
56 | this.open();
57 |
58 | });
59 |
60 | this.node.icon.classList.add('fa-bolt');
61 |
62 | this.node.icon.classList.add('fa-solid');
63 |
64 | this.node.line.classList.add('bookmark-open-all-line');
65 |
66 | this.node.lineTop.classList.add('bookmark-open-all-line-top');
67 |
68 | this.node.lineBottom.classList.add('bookmark-open-all-line-bottom');
69 |
70 | this.node.line.appendChild(this.node.lineTop);
71 |
72 | this.node.line.appendChild(this.node.lineBottom);
73 |
74 | this.node.button.appendChild(this.node.icon);
75 |
76 | this.node.openAll.appendChild(this.node.line);
77 |
78 | this.node.openAll.appendChild(this.node.button);
79 |
80 | }
81 |
82 | this.openAll = () => this.node.openAll;
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/component/Theme/index.js:
--------------------------------------------------------------------------------
1 | import { config } from '../../config';
2 | import { applyCSSVar } from '../utility/applyCSSVar';
3 | import { trimString } from '../utility/trimString';
4 |
5 | import WebFont from 'webfontloader';
6 |
7 | import './index.css';
8 |
9 | export const Theme = function() {
10 |
11 | this.style = () => {
12 |
13 | applyCSSVar('--theme-scale', config.theme.scale);
14 |
15 | applyCSSVar('--theme-text-hsl-h', config.theme.text.hsl[0]);
16 | applyCSSVar('--theme-text-hsl-s', config.theme.text.hsl[1]);
17 | applyCSSVar('--theme-text-hsl-l', config.theme.text.hsl[2]);
18 |
19 | applyCSSVar('--theme-accent-hsl-h', config.theme.accent.hsl[0]);
20 | applyCSSVar('--theme-accent-hsl-s', config.theme.accent.hsl[1]);
21 | applyCSSVar('--theme-accent-hsl-l', config.theme.accent.hsl[2]);
22 |
23 | applyCSSVar('--theme-transition-speed-xfast', (config.theme.transition.speed.xfast / 100));
24 | applyCSSVar('--theme-transition-speed-fast', (config.theme.transition.speed.fast / 100));
25 | applyCSSVar('--theme-transition-speed-medium', (config.theme.transition.speed.medium / 100));
26 | applyCSSVar('--theme-transition-speed-slow', (config.theme.transition.speed.slow / 100));
27 | applyCSSVar('--theme-transition-speed-xslow', (config.theme.transition.speed.xslow / 100));
28 |
29 | applyCSSVar('--theme-easing-bounce', `${config.theme.easing.bounce[0]}, ${config.theme.easing.bounce[1]}, ${config.theme.easing.bounce[2]}, ${config.theme.easing.bounce[3]}`);
30 |
31 | applyCSSVar('--theme-font', `"${config.theme.font}"`);
32 |
33 | applyCSSVar('--theme-bookmark-background-color-hsla-h', config.theme.bookmark.background.color.hsla[0]);
34 | applyCSSVar('--theme-bookmark-background-color-hsla-s', config.theme.bookmark.background.color.hsla[1]);
35 | applyCSSVar('--theme-bookmark-background-color-hsla-l', config.theme.bookmark.background.color.hsla[2]);
36 | applyCSSVar('--theme-bookmark-background-color-hsla-a', config.theme.bookmark.background.color.hsla[3]);
37 |
38 | applyCSSVar('--theme-bookmark-group-background-blur', config.theme.bookmark.background.blur);
39 |
40 | }
41 |
42 | this.render = () => {
43 |
44 | this.style();
45 |
46 | WebFont.load({
47 | // fontloading: (familyName, fvd) => { console.log('fontloading:', familyName); },
48 | google: { families: [trimString(config.theme.font) + ':100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i'] }
49 | });
50 |
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/src/component/Background/index.js:
--------------------------------------------------------------------------------
1 | import { config } from '../../config';
2 | import { isValidString } from '../utility/isValidString';
3 | import { applyCSSVar } from '../utility/applyCSSVar';
4 |
5 | import './index.css';
6 |
7 | export const Background = function() {
8 |
9 | this.node = {
10 | background: document.createElement('div'),
11 | color: document.createElement('div'),
12 | image: document.createElement('div'),
13 | gradient: document.createElement('div')
14 | }
15 |
16 | this.style = () => {
17 |
18 | applyCSSVar('--background-color-hsl-h', config.background.color.hsl[0]);
19 | applyCSSVar('--background-color-hsl-s', config.background.color.hsl[1]);
20 | applyCSSVar('--background-color-hsl-l', config.background.color.hsl[2]);
21 |
22 | if (isValidString(config.background.image.url)) {
23 |
24 | applyCSSVar('--background-image-url', `url(${config.background.image.url})`);
25 |
26 | applyCSSVar('--background-image-opacity', config.background.image.opacity);
27 |
28 | applyCSSVar('--background-image-grayscale', config.background.image.grayscale);
29 |
30 | applyCSSVar('--background-image-blur', config.background.image.blur);
31 |
32 | }
33 |
34 | if (config.background.gradient.color.length > 0) {
35 |
36 | applyCSSVar('--background-gradient-degree', `${config.background.gradient.degree}deg`);
37 |
38 | let gradientColor = '';
39 |
40 | config.background.gradient.color.forEach((gradientColorItem, index) => {
41 |
42 | gradientColor = gradientColor + `hsla(${gradientColorItem.hsla[0]}, ${gradientColorItem.hsla[1]}%, ${gradientColorItem.hsla[2]}%, ${gradientColorItem.hsla[3]}) ${gradientColorItem.position}%`;
43 |
44 | if (index < config.background.gradient.color.length - 1) {
45 |
46 | gradientColor = gradientColor + ', ';
47 |
48 | };
49 |
50 | });
51 |
52 | applyCSSVar('--background-gradient-color', gradientColor);
53 |
54 | }
55 |
56 | }
57 |
58 | this.render = (element) => {
59 |
60 | this.style();
61 |
62 | this.node.background.classList.add('background');
63 |
64 | this.node.color.classList.add('background-color');
65 |
66 | this.node.image.classList.add('background-image');
67 |
68 | this.node.gradient.classList.add('background-gradient');
69 |
70 | this.node.background.appendChild(this.node.color);
71 |
72 | this.node.background.appendChild(this.node.image);
73 |
74 | this.node.background.appendChild(this.node.gradient);
75 |
76 | element.appendChild(this.node.background);
77 |
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/component/utility/node.js:
--------------------------------------------------------------------------------
1 | export const node = (string, node) => {
2 |
3 | // set element
4 | let tag;
5 |
6 | if (string.indexOf('|') > 0) {
7 | tag = string.slice(0, string.indexOf('|'));
8 | } else {
9 | tag = string;
10 | }
11 |
12 | let text = false;
13 |
14 | if (tag.indexOf(':') > 0) {
15 | // regex
16 | // find all : and split
17 | // ignore all \:
18 | let pair = tag.split(/:(?!.*:\\)/);
19 | tag = pair[0];
20 | // replace \: with :
21 | text = pair[1].replace('\\', ':');
22 | }
23 |
24 | let element = document.createElement(tag);
25 |
26 | if (text && text != '') {
27 | element.innerHTML = text;
28 | }
29 |
30 | let attributes = string.slice(string.indexOf('|') + 1, string.length).split(',');
31 |
32 | // set attributes
33 | if (string.indexOf('|') > 0 && string.indexOf('|') < string.length - 1) {
34 |
35 | attributes.forEach((item, i) => {
36 | if (item.indexOf(':') > 0) {
37 | // if key and value
38 | var pair = item.substring(0, item.indexOf(':')) + ',' + item.substring(item.indexOf(':') + 1, item.length);
39 | pair = pair.split(',');
40 | attributes[i] = {
41 | key: pair[0],
42 | value: pair[1]
43 | };
44 | } else {
45 | // if key only
46 | attributes[i] = {
47 | key: item,
48 | value: undefined
49 | };
50 | }
51 | });
52 |
53 | attributes.forEach((item) => {
54 | if ('key' in item && item.key != undefined && 'value' in item && item.value != undefined) {
55 | element.setAttribute(item.key, item.value);
56 | } else if ('key' in item && item.key != undefined) {
57 | element.setAttribute(item.key, '');
58 | }
59 | });
60 |
61 | }
62 |
63 | if (node) {
64 |
65 | if (typeof node != 'string') {
66 |
67 | if (node.length > 0) {
68 |
69 | node.forEach((item) => {
70 |
71 | if (item instanceof HTMLElement) {
72 |
73 | element.appendChild(item);
74 |
75 | } else {
76 |
77 | let div = document.createElement('div');
78 |
79 | div.innerHTML = item;
80 |
81 | element.appendChild(div.firstChild);
82 |
83 | }
84 |
85 | });
86 |
87 | } else {
88 |
89 | if (node instanceof HTMLElement) {
90 |
91 | element.appendChild(node);
92 |
93 | } else {
94 |
95 | let div = document.createElement('div');
96 |
97 | div.innerHTML = node;
98 |
99 | element.appendChild(div.firstChild);
100 |
101 | }
102 |
103 | }
104 |
105 | }
106 |
107 | }
108 |
109 | return element;
110 | };
111 |
--------------------------------------------------------------------------------
/src/component/Bookmark/index.js:
--------------------------------------------------------------------------------
1 | import { config } from '../../config';
2 | import { BookmarkGroup } from '../BookmarkGroup';
3 | import { applyCSSVar } from '../utility/applyCSSVar';
4 |
5 | import './index.css';
6 |
7 | export const Bookmark = function() {
8 |
9 | this.node = {
10 | bookmark: document.createElement('div'),
11 | group: document.createElement('div'),
12 | panel: document.createElement('div'),
13 | allGroup: []
14 | }
15 |
16 | this.style = () => {
17 |
18 | applyCSSVar('--bookmark-panel', config.bookmark.panel);
19 |
20 | applyCSSVar('--bookmark-scale', config.bookmark.scale);
21 |
22 | applyCSSVar('--bookmark-icon-scale-visible', config.bookmark.iconScaleVisible);
23 |
24 | applyCSSVar('--bookmark-icon-scale-hidden', config.bookmark.iconScaleHidden);
25 |
26 | applyCSSVar('--bookmark-icon-scale-hover', config.bookmark.iconScaleHover);
27 |
28 | applyCSSVar('--bookmark-icon-scale-active', config.bookmark.iconScaleActive);
29 |
30 | applyCSSVar('--bookmark-icon-spacing', config.bookmark.iconSpacing);
31 |
32 | applyCSSVar('--bookmark-group-spacing', config.bookmark.groupSpacing);
33 |
34 | applyCSSVar('--bookmark-tab-spacing', config.bookmark.tabSpacing);
35 |
36 | }
37 |
38 | this.populateGroup = () => {
39 |
40 | config.bookmark.group.forEach(groupItem => {
41 |
42 | const bookmarkGroup = new BookmarkGroup(groupItem, this.node.allGroup);
43 |
44 | bookmarkGroup.render();
45 |
46 | this.node.allGroup.push(bookmarkGroup);
47 |
48 | this.node.group.appendChild(bookmarkGroup.group());
49 |
50 | });
51 |
52 | }
53 |
54 | this.render = (element) => {
55 |
56 | this.style();
57 |
58 | this.populateGroup();
59 |
60 | this.node.bookmark.classList.add('bookmark');
61 |
62 | this.node.group.classList.add('bookmark-group');
63 |
64 | if (!config.bookmark.alwaysVisible) {
65 |
66 | this.node.group.addEventListener('mouseleave', () => {
67 |
68 | config.bookmark.group.forEach(bookmarkGroup => {
69 |
70 | bookmarkGroup.active = false;
71 |
72 | });
73 |
74 | this.node.allGroup.forEach(group => {
75 |
76 | group.renderActive();
77 |
78 | });
79 |
80 | });
81 |
82 | }
83 |
84 | this.node.panel.classList.add('bookmark-panel');
85 |
86 | switch (config.bookmark.direction) {
87 |
88 | case 'left':
89 | this.node.panel.classList.add('bookmark-panel-left');
90 | break;
91 |
92 | case 'right':
93 | this.node.panel.classList.add('bookmark-panel-right');
94 | break;
95 |
96 | }
97 |
98 | this.node.bookmark.appendChild(this.node.panel);
99 |
100 | this.node.bookmark.appendChild(this.node.group);
101 |
102 | element.appendChild(this.node.bookmark);
103 |
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/component/utility/wordNumber.js:
--------------------------------------------------------------------------------
1 | export const wordNumber = (number) => {
2 |
3 | const ten = 10;
4 |
5 | const oneHundred = 100;
6 |
7 | const oneThousand = 1000;
8 |
9 | const oneMillion = 1000000;
10 |
11 | const oneBillion = 1000000000; // 1,000,000,000 (9)
12 |
13 | const oneTrillion = 1000000000000; // 1,000,000,000,000 (12)
14 |
15 | const oneQuadrillion = 1000000000000000; // 1,000,000,000,000,000 (15)
16 |
17 | const max = 9007199254740992; // 9,007,199,254,740,992 (15)
18 |
19 | const lessThanTwenty = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen'];
20 |
21 | const tenthsLessThanHundred = ['Zero', 'Ten', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety'];
22 |
23 |
24 | const generateWords = function(number) {
25 |
26 | let remainder;
27 |
28 | let word;
29 |
30 | let words = arguments[1];
31 |
32 | // We’re done
33 | if (number === 0) {
34 | return !words ? 'Zero' : words.join(' ').replace(/,$/, '');
35 | }
36 |
37 | // First run
38 | if (!words) {
39 | words = [];
40 | }
41 |
42 | // If negative, prepend “minus”
43 | if (number < 0) {
44 | words.push('minus');
45 | number = Math.abs(number);
46 | }
47 |
48 | if (number < 20) {
49 | remainder = 0;
50 | word = lessThanTwenty[number];
51 | } else if (number < oneHundred) {
52 | remainder = number % ten;
53 | word = tenthsLessThanHundred[Math.floor(number / ten)];
54 | // In case of remainder, we need to handle it here to be able to add the “-”
55 | if (remainder) {
56 | word += '-' + lessThanTwenty[remainder];
57 | remainder = 0;
58 | }
59 | } else if (number < oneThousand) {
60 | remainder = number % oneHundred;
61 | word = generateWords(Math.floor(number / oneHundred)) + ' Hundred';
62 | } else if (number < oneMillion) {
63 | remainder = number % oneThousand;
64 | word = generateWords(Math.floor(number / oneThousand)) + ' Thousand,';
65 | } else if (number < oneBillion) {
66 | remainder = number % oneMillion;
67 | word = generateWords(Math.floor(number / oneMillion)) + ' Million,';
68 | } else if (number < oneTrillion) {
69 | remainder = number % oneBillion;
70 | word = generateWords(Math.floor(number / oneBillion)) + ' Billion,';
71 | } else if (number < oneQuadrillion) {
72 | remainder = number % oneTrillion;
73 | word = generateWords(Math.floor(number / oneTrillion)) + ' Trillion,';
74 | } else if (number <= max) {
75 | remainder = number % oneQuadrillion;
76 | word = generateWords(Math.floor(number / oneQuadrillion)) + ' Quadrillion,';
77 | }
78 |
79 | words.push(word);
80 |
81 | return generateWords(remainder, words);
82 | };
83 |
84 | var num = parseInt(number, 10);
85 |
86 | return generateWords(num);
87 | };
88 |
--------------------------------------------------------------------------------
/src/component/BookmarkOpenAll/index.css:
--------------------------------------------------------------------------------
1 | .bookmark-open-all {
2 | align-self: stretch;
3 | position: relative;
4 | }
5 |
6 | .bookmark-open-all-button {
7 | background-color: transparent;
8 | border: 0;
9 | position: absolute;
10 | top: 50%;
11 | left: 50%;
12 | font-size: 1.5em;
13 | color: hsl(var(--bookmark-group-color-secondary-hsl-h),
14 | calc(var(--bookmark-group-color-secondary-hsl-s) * 1%),
15 | calc(var(--bookmark-group-color-secondary-hsl-l) * 1%));
16 | cursor: pointer;
17 | transform-origin: center;
18 | transform: translate(-50%, -50%) scale(0);
19 | transition: color var(--theme-transition-xfast-ease), transform var(--theme-transition-fast-ease) calc(var(--theme-transition-speed-fast) * 1s);
20 | }
21 |
22 | .bookmark-group-item:focus-within .bookmark-open-all-button,
23 | .bookmark-group-active .bookmark-open-all-button {
24 | transform: translate(-50%, -50%) scale(1);
25 | transition: color var(--theme-transition-xfast-ease), transform var(--theme-transition-xslow-bounce) 0s;
26 | }
27 |
28 | .bookmark-group-item:focus-within .bookmark-open-all-button:hover,
29 | .bookmark-group-item:focus-within .bookmark-open-all-button:focus,
30 | .bookmark-group-active .bookmark-open-all-button:hover,
31 | .bookmark-group-active .bookmark-open-all-button:focus {
32 | outline: none;
33 | transform: translate(-50%, -50%) scale(1.4);
34 | transition: transform var(--theme-transition-fast-bounce);
35 | }
36 |
37 | .bookmark-group-item:focus-within .bookmark-open-all-button:active,
38 | .bookmark-group-active .bookmark-open-all-button:active {
39 | transform: translate(-50%, -50%) scale(1);
40 | transition: transform var(--theme-transition-fast-bounce);
41 | }
42 |
43 | .bookmark-open-all-line {
44 | flex-shrink: 0;
45 | position: relative;
46 | display: block;
47 | width: 0.25em;
48 | height: 100%;
49 | }
50 |
51 | .bookmark-open-all-line-top,
52 | .bookmark-open-all-line-bottom {
53 | width: 100%;
54 | height: 0;
55 | display: block;
56 | position: absolute;
57 | left: 50%;
58 | border-radius: 1em;
59 | transform: translateX(-50%);
60 | background-color: hsl(var(--bookmark-group-color-secondary-hsl-h),
61 | calc((var(--bookmark-group-color-secondary-hsl-s) - 30) * 1%),
62 | calc((var(--bookmark-group-color-secondary-hsl-l) - 20) * 1%));
63 | transition: height var(--theme-transition-fast-ease), background-color var(--theme-transition-medium-ease);
64 | }
65 |
66 | .bookmark-open-all-line-top {
67 | bottom: calc(50% + 1.5em);
68 | }
69 |
70 | .bookmark-open-all-line-bottom {
71 | top: calc(50% + 1.5em);
72 | }
73 |
74 | .bookmark-group-item:focus-within .bookmark-open-all-line-top,
75 | .bookmark-group-item:focus-within .bookmark-open-all-line-bottom,
76 | .bookmark-group-active .bookmark-open-all-line-top,
77 | .bookmark-group-active .bookmark-open-all-line-bottom {
78 | background-color: hsl(var(--bookmark-group-color-secondary-hsl-h),
79 | calc(var(--bookmark-group-color-secondary-hsl-s) * 1%),
80 | calc(var(--bookmark-group-color-secondary-hsl-l) * 1%));
81 | height: 40%;
82 | transition: height var(--theme-transition-medium-bounce) calc((var(--theme-transition-speed-xfast) / 2) * 1s), background-color var(--theme-transition-medium-ease);
83 | }
84 |
--------------------------------------------------------------------------------
/src/component/utility/convertColor.js:
--------------------------------------------------------------------------------
1 | export const convertColor = {
2 | rgb: {},
3 | hsl: {},
4 | hex: {}
5 | };
6 |
7 | convertColor.rgb.hsl = (rgb) => {
8 | var r = rgb.r / 255;
9 | var g = rgb.g / 255;
10 | var b = rgb.b / 255;
11 | var min = Math.min(r, g, b);
12 | var max = Math.max(r, g, b);
13 | var delta = max - min;
14 | var h;
15 | var s;
16 |
17 | if (max === min) {
18 | h = 0;
19 | } else if (r === max) {
20 | h = (g - b) / delta;
21 | } else if (g === max) {
22 | h = 2 + (b - r) / delta;
23 | } else if (b === max) {
24 | h = 4 + (r - g) / delta;
25 | }
26 |
27 | h = Math.min(h * 60, 360);
28 |
29 | if (h < 0) {
30 | h += 360;
31 | }
32 |
33 | var l = (min + max) / 2;
34 |
35 | if (max === min) {
36 | s = 0;
37 | } else if (l <= 0.5) {
38 | s = delta / (max + min);
39 | } else {
40 | s = delta / (2 - max - min);
41 | }
42 |
43 | return {
44 | h: Math.round(h),
45 | s: Math.round(s * 100),
46 | l: Math.round(l * 100)
47 | };
48 | };
49 |
50 | convertColor.rgb.hex = (args) => {
51 | var integer = ((Math.round(args.r) & 0xFF) << 16) +
52 | ((Math.round(args.g) & 0xFF) << 8) +
53 | (Math.round(args.b) & 0xFF);
54 |
55 | var string = integer.toString(16);
56 |
57 | return '#' + '000000'.substring(string.length) + string;
58 | };
59 |
60 | convertColor.hsl.rgb = (hsl) => {
61 | var h = hsl.h / 360;
62 | var s = hsl.s / 100;
63 | var l = hsl.l / 100;
64 | var t2;
65 | var t3;
66 | var val;
67 |
68 | if (s === 0) {
69 | val = l * 255;
70 | return {
71 | r: Math.round(val),
72 | g: Math.round(val),
73 | b: Math.round(val)
74 | };
75 | }
76 |
77 | if (l < 0.5) {
78 | t2 = l * (1 + s);
79 | } else {
80 | t2 = l + s - l * s;
81 | }
82 |
83 | var t1 = 2 * l - t2;
84 |
85 | var rgb = [0, 0, 0];
86 |
87 | for (var i = 0; i < 3; i++) {
88 | t3 = h + 1 / 3 * -(i - 1);
89 |
90 | if (t3 < 0) {
91 | t3++;
92 | }
93 |
94 | if (t3 > 1) {
95 | t3--;
96 | }
97 |
98 | if (6 * t3 < 1) {
99 | val = t1 + (t2 - t1) * 6 * t3;
100 | } else if (2 * t3 < 1) {
101 | val = t2;
102 | } else if (3 * t3 < 2) {
103 | val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
104 | } else {
105 | val = t1;
106 | }
107 |
108 | rgb[i] = val * 255;
109 | }
110 |
111 | return {
112 | r: Math.round(rgb[0]),
113 | g: Math.round(rgb[1]),
114 | b: Math.round(rgb[2])
115 | };
116 | };
117 |
118 | convertColor.hex.rgb = (args) => {
119 | var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
120 |
121 | if (!match) {
122 | return {
123 | r: 0,
124 | g: 0,
125 | b: 0
126 | };
127 | }
128 |
129 | var colorString = match[0];
130 |
131 | if (match[0].length === 3) {
132 | colorString = colorString.split('').map((char) => {
133 | return char + char;
134 | }).join('');
135 | }
136 |
137 | var integer = parseInt(colorString, 16);
138 | var r = (integer >> 16) & 0xFF;
139 | var g = (integer >> 8) & 0xFF;
140 | var b = integer & 0xFF;
141 |
142 | return {
143 | r: r,
144 | g: g,
145 | b: b
146 | };
147 | };
148 |
--------------------------------------------------------------------------------
/src/component/BookmarkGroup/index.css:
--------------------------------------------------------------------------------
1 | .bookmark-group {
2 | grid-column: 1 / -1;
3 | grid-row: 1 / -1;
4 | display: flex;
5 | flex-direction: column;
6 | gap: calc((var(--bookmark-group-spacing) / 10) * 1em);
7 | padding: calc((var(--bookmark-group-spacing) / 10) * 1em) 0;
8 | z-index: 2;
9 | }
10 |
11 | .bookmark-group-item {
12 | display: grid;
13 | grid-template-columns: calc(var(--bookmark-panel) * 1%) 1fr calc(var(--bookmark-panel) * 1%);
14 | position: relative;
15 | }
16 |
17 | .bookmark-group-tab {
18 | grid-row: 1 / 2;
19 | display: flex;
20 | align-items: center;
21 | justify-content: flex-end;
22 | color: hsl(var(--bookmark-group-color-primary-hsl-h),
23 | calc(var(--bookmark-group-color-primary-hsl-s) * 1%),
24 | calc(var(--bookmark-group-color-primary-hsl-l) * 1%));
25 | gap: calc((var(--bookmark-tab-spacing) / 10) * 1em);
26 | padding: 0 calc((var(--bookmark-tab-spacing) / 10) * 1em);
27 | }
28 |
29 | .bookmark-group-item-left .bookmark-group-tab {
30 | grid-column: 1 / 2;
31 | flex-direction: row;
32 | text-align: right;
33 | }
34 |
35 | .bookmark-group-item-right .bookmark-group-tab {
36 | grid-column: 3 / 4;
37 | flex-direction: row-reverse;
38 | text-align: left;
39 | }
40 |
41 | .bookmark-group-label {
42 | display: flex;
43 | flex-direction: column;
44 | justify-content: center;
45 | overflow: hidden;
46 | user-select: none;
47 | }
48 |
49 | .bookmark-group-name {
50 | font-size: 2em;
51 | margin: 0;
52 | padding: 0;
53 | font-family: var(--theme-font), sans-serif;
54 | font-weight: var(--font-weight-light);
55 | color: hsl(var(--bookmark-group-color-primary-hsl-h),
56 | calc((var(--bookmark-group-color-primary-hsl-s) - 30) * 1%),
57 | calc((var(--bookmark-group-color-primary-hsl-l) - 20) * 1%));
58 | overflow: hidden;
59 | text-overflow: ellipsis;
60 | transition: color var(--theme-transition-fast-ease);
61 | }
62 |
63 | .bookmark-group-active .bookmark-group-name {
64 | color: hsl(var(--bookmark-group-color-primary-hsl-h),
65 | calc(var(--bookmark-group-color-primary-hsl-s) * 1%),
66 | calc(var(--bookmark-group-color-primary-hsl-l) * 1%));
67 | }
68 |
69 | .bookmark-group-description {
70 | font-size: 1em;
71 | margin: 0;
72 | padding: 0;
73 | font-family: var(--theme-font), sans-serif;
74 | font-weight: var(--font-weight-medium);
75 | color: hsl(var(--bookmark-group-color-secondary-hsl-h),
76 | calc((var(--bookmark-group-color-secondary-hsl-s) - 30) * 1%),
77 | calc((var(--bookmark-group-color-secondary-hsl-l) - 20) * 1%));
78 | overflow: hidden;
79 | text-overflow: ellipsis;
80 | transition: color var(--theme-transition-fast-ease);
81 | }
82 |
83 | .bookmark-group-active .bookmark-group-description {
84 | color: hsl(var(--bookmark-group-color-secondary-hsl-h),
85 | calc(var(--bookmark-group-color-secondary-hsl-s) * 1%),
86 | calc(var(--bookmark-group-color-secondary-hsl-l) * 1%));
87 | }
88 |
89 | .bookmark-group-list {
90 | grid-row: 1 / 2;
91 | display: flex;
92 | flex-wrap: wrap;
93 | flex-direction: row;
94 | gap: calc((var(--bookmark-icon-spacing) / 10) * 1em);
95 | padding: 0 calc((var(--bookmark-icon-spacing) / 10) * 1em);
96 | align-items: center;
97 | }
98 |
99 | .bookmark-group-item-left .bookmark-group-list {
100 | grid-column: 2 / 4;
101 | justify-content: flex-start;
102 | }
103 |
104 | .bookmark-group-item-right .bookmark-group-list {
105 | grid-column: 1 / 3;
106 | justify-content: flex-end;
107 | }
108 |
--------------------------------------------------------------------------------
/src/style/typography/index.css:
--------------------------------------------------------------------------------
1 | h1,
2 | h2,
3 | h3,
4 | h4,
5 | h5,
6 | h6 {
7 | margin: 0 0 1em 0;
8 | font-weight: normal;
9 | line-height: 1.6;
10 | color: hsl(var(--theme-text));
11 | }
12 |
13 | h1 {
14 | font-size: 1.5em;
15 | font-family: var(--theme-font);
16 | font-weight: var(--font-weight-light);
17 | font-style: normal;
18 | }
19 |
20 | h2 {
21 | font-size: 1.3em;
22 | font-family: var(--theme-font);
23 | font-weight: var(--font-weight-light);
24 | font-style: normal;
25 | }
26 |
27 | h3 {
28 | font-size: 1.1em;
29 | font-family: var(--theme-font);
30 | font-weight: var(--font-weight-light);
31 | font-style: normal;
32 | }
33 |
34 | h4 {
35 | font-size: 1em;
36 | font-family: var(--theme-font);
37 | font-weight: var(--font-weight-medium);
38 | font-style: normal;
39 | }
40 |
41 | h5 {
42 | font-size: 1em;
43 | font-family: var(--theme-font);
44 | font-weight: var(--font-weight-medium);
45 | font-style: normal;
46 | font-weight: var(--font-weight-bold);
47 | }
48 |
49 | h6 {
50 | font-size: 0.75em;
51 | font-family: var(--theme-font);
52 | font-weight: var(--font-weight-medium);
53 | font-style: normal;
54 | font-weight: var(--font-weight-bold);
55 | }
56 |
57 | p {
58 | color: hsl(var(--theme-text));
59 | margin: 0;
60 | line-height: 1.6;
61 | }
62 |
63 | hr {
64 | border: 0;
65 | border-top: 2px;
66 | border-radius: 0.5em;
67 | margin: 1em;
68 | clear: both;
69 | transition: border-color var(--theme-transition-xfast-ease);
70 | }
71 |
72 | b,
73 | caption,
74 | strong {
75 | color: hsl(var(--theme-text));
76 | font-family: var(--theme-font);
77 | font-weight: var(--font-weight-bold);
78 | }
79 |
80 | i {
81 | font-style: italic;
82 | }
83 |
84 | a {
85 | color: hsl(var(--theme-text));
86 | text-decoration: underline;
87 | transition: text-decoration var(--theme-transition-xfast-ease);
88 | }
89 |
90 | a:link,
91 | a:visited {
92 | color: hsl(var(--theme-text));
93 | }
94 |
95 | a:focus {
96 | text-decoration-color: hsl(var(--theme-text));
97 | outline: none;
98 | }
99 |
100 | a:hover {
101 | color: hsl(var(--theme-text));
102 | text-decoration-color: rgb(var(--theme-accent));
103 | }
104 |
105 | a:active {
106 | color: hsl(var(--theme-text));
107 | text-decoration-color: hsl(var(--theme-text));
108 | }
109 |
110 | ol,
111 | ul {
112 | margin: 0;
113 | padding: 0 0 0 1.5em;
114 | }
115 |
116 | ol:not(:last-child),
117 | ul:not(:last-child) {
118 | margin-bottom: 1em;
119 | }
120 |
121 | li {
122 | margin: 0;
123 | }
124 |
125 | li>ul,
126 | li>ol {
127 | margin: 0;
128 | }
129 |
130 | li:not(:last-child) {
131 | margin-bottom: 0.5em;
132 | }
133 |
134 | li>ul:not(:last-child),
135 | li>ol:not(:last-child) {
136 | margin-bottom: 0.5em;
137 | }
138 |
139 | table {
140 | border: 0;
141 | margin: 0 0 1em;
142 | padding: 0;
143 | width: 100%;
144 | border-spacing: 0;
145 | }
146 |
147 | table thead tr td,
148 | table thead tr th {
149 | background-color: hsl(var(--theme-text));
150 | border: 0;
151 | border-bottom: 1px solid hsl(var(--theme-text));
152 | padding: 0.5em;
153 | margin: 0;
154 | text-align: left;
155 | font-family: var(--theme-font);
156 | font-weight: var(--font-weight-bold);
157 | font-style: normal;
158 | box-sizing: border-box;
159 | }
160 |
161 | table tr:nth-child(odd) {
162 | background-color: hsl(var(--theme-text));
163 | }
164 |
165 | table tbody tr td,
166 | table tbody tr th {
167 | padding: 0.25em 0.5em;
168 | margin: 0;
169 | border: 0;
170 | text-align: left;
171 | box-sizing: border-box;
172 | }
173 |
174 | code {
175 | background-color: hsl(var(--theme-text));
176 | padding: 0.2em 0.5em;
177 | border-radius: 0.5em;
178 | }
179 |
--------------------------------------------------------------------------------
/src/component/BookmarkGroup/index.js:
--------------------------------------------------------------------------------
1 | import { config } from '../../config';
2 | import { BookmarkLink } from '../BookmarkLink';
3 | import { BookmarkOpenAll } from '../BookmarkOpenAll';
4 |
5 | import './index.css';
6 |
7 | export const BookmarkGroup = function(bookmarkGroupData, allBookmarkGroup) {
8 |
9 | this.node = {
10 | groupItem: document.createElement('div'),
11 | tab: document.createElement('div'),
12 | label: document.createElement('div'),
13 | name: document.createElement('p'),
14 | description: document.createElement('p'),
15 | list: document.createElement('div'),
16 | openAll: null,
17 | allList: []
18 | }
19 |
20 | this.style = () => {
21 |
22 | this.node.groupItem.style.setProperty('--bookmark-group-color-primary-hsl-h', bookmarkGroupData.color.primary.hsl[0]);
23 | this.node.groupItem.style.setProperty('--bookmark-group-color-primary-hsl-s', bookmarkGroupData.color.primary.hsl[1]);
24 | this.node.groupItem.style.setProperty('--bookmark-group-color-primary-hsl-l', bookmarkGroupData.color.primary.hsl[2]);
25 |
26 | this.node.groupItem.style.setProperty('--bookmark-group-color-secondary-hsl-h', bookmarkGroupData.color.secondary.hsl[0]);
27 | this.node.groupItem.style.setProperty('--bookmark-group-color-secondary-hsl-s', bookmarkGroupData.color.secondary.hsl[1]);
28 | this.node.groupItem.style.setProperty('--bookmark-group-color-secondary-hsl-l', bookmarkGroupData.color.secondary.hsl[2]);
29 |
30 | }
31 |
32 | this.toggleActiveState = () => {
33 |
34 | config.bookmark.group.forEach(bookmarkGroup => {
35 |
36 | bookmarkGroup.active = false;
37 |
38 | });
39 |
40 | bookmarkGroupData.active = true;
41 |
42 | }
43 |
44 | this.renderActive = () => {
45 |
46 | bookmarkGroupData.active ? this.active() : this.inactive();
47 |
48 | }
49 |
50 | this.active = () => this.node.groupItem.classList.add('bookmark-group-active')
51 |
52 | this.inactive = () => this.node.groupItem.classList.remove('bookmark-group-active')
53 |
54 | this.render = () => {
55 |
56 | this.node.groupItem.classList.add('bookmark-group-item');
57 |
58 | switch (config.bookmark.direction) {
59 |
60 | case 'left':
61 | this.node.groupItem.classList.add('bookmark-group-item-left');
62 | break;
63 |
64 | case 'right':
65 | this.node.groupItem.classList.add('bookmark-group-item-right');
66 | break;
67 |
68 | }
69 |
70 | if (config.bookmark.alwaysVisible) {
71 |
72 | this.node.groupItem.classList.add('bookmark-group-active');
73 |
74 | } else {
75 |
76 | this.node.groupItem.addEventListener('mouseenter', () => {
77 |
78 | this.toggleActiveState();
79 |
80 | allBookmarkGroup.forEach(group => {
81 |
82 | group.renderActive();
83 |
84 | });
85 |
86 | });
87 |
88 | this.renderActive();
89 |
90 | };
91 |
92 | this.node.tab.classList.add('bookmark-group-tab');
93 |
94 | this.node.list.classList.add('bookmark-group-list');
95 |
96 | this.node.tab.href = "#";
97 |
98 | this.style();
99 |
100 | this.node.label.classList.add('bookmark-group-label');
101 |
102 | this.node.name.classList.add('bookmark-group-name');
103 |
104 | this.node.description.classList.add('bookmark-group-description');
105 |
106 | this.node.name.textContent = bookmarkGroupData.name;
107 |
108 | this.node.description.textContent = bookmarkGroupData.description;
109 |
110 | bookmarkGroupData.list.forEach((listItem, index) => {
111 |
112 | const bookmarkLink = new BookmarkLink(listItem, bookmarkGroupData.list.length, index);
113 |
114 | bookmarkLink.render();
115 |
116 | this.node.allList.push(bookmarkLink.link());
117 |
118 | this.node.list.appendChild(bookmarkLink.link());
119 |
120 | });
121 |
122 | this.node.openAll = new BookmarkOpenAll(bookmarkGroupData);
123 |
124 | this.node.openAll.render();
125 |
126 | this.node.label.appendChild(this.node.name);
127 |
128 | this.node.label.appendChild(this.node.description);
129 |
130 | this.node.tab.appendChild(this.node.label);
131 |
132 | this.node.tab.appendChild(this.node.openAll.openAll());
133 |
134 | this.node.groupItem.appendChild(this.node.tab);
135 |
136 | this.node.groupItem.appendChild(this.node.list);
137 |
138 | }
139 |
140 | this.group = () => this.node.groupItem;
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/src/style/reset/index.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::before,
3 | *::after {
4 | box-sizing: border-box;
5 | }
6 |
7 | html {
8 | font-family: sans-serif;
9 | line-height: 1.15;
10 | -webkit-text-size-adjust: 100%;
11 | -ms-overflow-style: scrollbar;
12 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
13 | }
14 |
15 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
16 | display: block;
17 | }
18 |
19 | body {
20 | margin: 0;
21 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, Noto Sans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
22 | font-size: 1rem;
23 | font-weight: 400;
24 | line-height: 1.5;
25 | color: #212529;
26 | text-align: left;
27 | background-color: #000;
28 | }
29 |
30 | [tabindex="-1"]:focus {
31 | outline: none !important;
32 | }
33 |
34 | hr {
35 | box-sizing: content-box;
36 | height: 0;
37 | overflow: visible;
38 | }
39 |
40 | h1, h2, h3, h4, h5, h6 {
41 | margin-top: 0;
42 | margin-bottom: 0.5rem;
43 | }
44 |
45 | p {
46 | margin-top: 0;
47 | margin-bottom: 1rem;
48 | }
49 |
50 | abbr[title],
51 | abbr[data-original-title] {
52 | text-decoration: underline;
53 | -webkit-text-decoration: underline dotted;
54 | text-decoration: underline dotted;
55 | cursor: help;
56 | border-bottom: 0;
57 | text-decoration-skip-ink: none;
58 | }
59 |
60 | address {
61 | margin-bottom: 1rem;
62 | font-style: normal;
63 | line-height: inherit;
64 | }
65 |
66 | ol,
67 | ul,
68 | dl {
69 | margin-top: 0;
70 | margin-bottom: 1rem;
71 | }
72 |
73 | ol ol,
74 | ul ul,
75 | ol ul,
76 | ul ol {
77 | margin-bottom: 0;
78 | }
79 |
80 | dt {
81 | font-weight: 700;
82 | }
83 |
84 | dd {
85 | margin-bottom: .5rem;
86 | margin-left: 0;
87 | }
88 |
89 | blockquote {
90 | margin: 0 0 1rem;
91 | }
92 |
93 | b,
94 | strong {
95 | font-weight: bolder;
96 | }
97 |
98 | small {
99 | font-size: 80%;
100 | }
101 |
102 | sub,
103 | sup {
104 | position: relative;
105 | font-size: 75%;
106 | line-height: 0;
107 | vertical-align: baseline;
108 | }
109 |
110 | sub {
111 | bottom: -.25em;
112 | }
113 |
114 | sup {
115 | top: -.5em;
116 | }
117 |
118 | a {
119 | color: #007bff;
120 | text-decoration: none;
121 | background-color: transparent;
122 | }
123 |
124 | a:hover {
125 | color: #0056b3;
126 | text-decoration: underline;
127 | }
128 |
129 | a:not([href]):not([tabindex]) {
130 | color: inherit;
131 | text-decoration: none;
132 | }
133 |
134 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
135 | color: inherit;
136 | text-decoration: none;
137 | }
138 |
139 | a:not([href]):not([tabindex]):focus {
140 | outline: none;
141 | }
142 |
143 | pre,
144 | code,
145 | kbd,
146 | samp {
147 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
148 | font-size: 1em;
149 | }
150 |
151 | pre {
152 | margin-top: 0;
153 | margin-bottom: 1rem;
154 | overflow: auto;
155 | -ms-overflow-style: scrollbar;
156 | }
157 |
158 | figure {
159 | margin: 0 0 1rem;
160 | }
161 |
162 | img {
163 | vertical-align: middle;
164 | border-style: none;
165 | }
166 |
167 | svg {
168 | overflow: hidden;
169 | vertical-align: middle;
170 | }
171 |
172 | table {
173 | border-collapse: collapse;
174 | }
175 |
176 | caption {
177 | padding-top: 0.75rem;
178 | padding-bottom: 0.75rem;
179 | color: #6c757d;
180 | text-align: left;
181 | caption-side: bottom;
182 | }
183 |
184 | th {
185 | text-align: inherit;
186 | }
187 |
188 | label {
189 | display: inline-block;
190 | margin-bottom: 0;
191 | }
192 |
193 | button {
194 | border-radius: 0;
195 | }
196 |
197 | button:focus {
198 | outline: 1px dotted;
199 | outline: 5px auto -webkit-focus-ring-color;
200 | }
201 |
202 | input,
203 | button,
204 | select,
205 | optgroup,
206 | textarea {
207 | margin: 0;
208 | font-family: inherit;
209 | font-size: inherit;
210 | line-height: inherit;
211 | }
212 |
213 | button,
214 | input {
215 | overflow: visible;
216 | }
217 |
218 | button,
219 | select {
220 | text-transform: none;
221 | }
222 |
223 | button,
224 | [type="button"],
225 | [type="reset"],
226 | [type="submit"] {
227 | -webkit-appearance: button;
228 | }
229 |
230 | button::-moz-focus-inner,
231 | [type="button"]::-moz-focus-inner,
232 | [type="reset"]::-moz-focus-inner,
233 | [type="submit"]::-moz-focus-inner {
234 | padding: 0;
235 | border-style: none;
236 | }
237 |
238 | input[type="radio"],
239 | input[type="checkbox"] {
240 | box-sizing: border-box;
241 | padding: 0;
242 | }
243 |
244 | input[type="date"],
245 | input[type="time"],
246 | input[type="datetime-local"],
247 | input[type="month"] {
248 | -webkit-appearance: listbox;
249 | }
250 |
251 | textarea {
252 | overflow: auto;
253 | resize: vertical;
254 | }
255 |
256 | fieldset {
257 | min-width: 0;
258 | padding: 0;
259 | margin: 0;
260 | border: 0;
261 | }
262 |
263 | legend {
264 | display: block;
265 | width: 100%;
266 | max-width: 100%;
267 | padding: 0;
268 | margin-bottom: .5rem;
269 | font-size: 1.5rem;
270 | line-height: inherit;
271 | color: inherit;
272 | white-space: normal;
273 | }
274 |
275 | progress {
276 | vertical-align: baseline;
277 | }
278 |
279 | [type="number"]::-webkit-inner-spin-button,
280 | [type="number"]::-webkit-outer-spin-button {
281 | height: auto;
282 | }
283 |
284 | [type="search"] {
285 | outline-offset: -2px;
286 | -webkit-appearance: none;
287 | }
288 |
289 | [type="search"]::-webkit-search-decoration {
290 | -webkit-appearance: none;
291 | }
292 |
293 | ::-webkit-file-upload-button {
294 | font: inherit;
295 | -webkit-appearance: button;
296 | }
297 |
298 | output {
299 | display: inline-block;
300 | }
301 |
302 | summary {
303 | display: list-item;
304 | cursor: pointer;
305 | }
306 |
307 | template {
308 | display: none;
309 | }
310 |
311 | [hidden] {
312 | display: none !important;
313 | }
314 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | export const config = {
2 |
3 | theme: {
4 |
5 | // global scale
6 | scale: 38, // range: min:0|max:100
7 |
8 | // accent colours
9 | // not used for much yet, this should be developed as the project grows
10 | // colour defined with HSL: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl
11 | accent: { hsl: [204, 100, 72] },
12 |
13 | // text colours
14 | // not used for much yet, this should be developed as the project grows
15 | // colour defined with HSL: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl
16 | text: { hsl: [0, 0, 0] },
17 |
18 | // text font
19 | // works with Google Fonts: https://fonts.google.com/
20 | // enter the full name of a font as it is displayed on Google Fonts
21 | font: 'Ubuntu',
22 |
23 | // the speed of the different transitions
24 | transition: { speed: { xfast: 10, fast: 20, medium: 30, slow: 40, xslow: 50 } }, // range: min:0|max:*
25 |
26 | // the bounce transition easing bezier curve
27 | easing: { bounce: [0.8, 0.5, 0.2, 2] }, // range: min:0|max:*
28 |
29 | bookmark: {
30 | background: {
31 |
32 | // bookmark left side panel background blur amount
33 | blur: 14, // range: min:0|max:*
34 |
35 | // bookmark left side panel background colour
36 | // colour defined with HSLA: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsla
37 | color: { hsla: [200, 180, 25, 0.2] }
38 | }
39 | }
40 |
41 | },
42 |
43 | bookmark: {
44 |
45 | // open the bookmarks in a new tab: true or false
46 | newTab: true, // boolean: true|false
47 |
48 | // the bookmark panel and group name position and direction the bookmarks flow
49 | direction: 'left', // string: left|right
50 |
51 | // bookmark icons show when a bookmark group gains focus or on cursor hover
52 | // if set to true bookmarks are always shown
53 | alwaysVisible: false, // boolean: true|false
54 |
55 | // bookmark left side panel size
56 | // this area contains bookmark group name, description and open all button
57 | panel: 35, // range: min:0|max:100
58 |
59 | // bookmark size
60 | scale: 20, // range: min:0|max:*
61 |
62 | // bookmark icon size the bookmark group is in focus
63 | iconScaleVisible: 1, // range: min:*|max:*
64 |
65 | // bookmark icon size when the bookmark group is not in focus
66 | // if alwaysVisible is set to true this setting will not have an effect
67 | iconScaleHidden: 0, // range: min:*|max:*
68 |
69 | // bookmark icon size when the bookmark icon is hovered (:hover)
70 | iconScaleHover: 1.4, // range: min:*|max:*
71 |
72 | // bookmark icon size when the bookmark icon is clicked (:active)
73 | iconScaleActive: 1, // range: min:*|max:*
74 |
75 | // spacing between bookmarks
76 | iconSpacing: 10, // range: min:0|max:*
77 |
78 | // spacing between bookmark group rows
79 | groupSpacing: 30, // range: min:0|max:*
80 |
81 | // spacing between bookmark group name, description and open all button
82 | tabSpacing: 30, // range: min:0|max:*
83 |
84 | // bookmark grouping
85 | // each object in this array definesa group
86 | // add as many objects as needed
87 | // each group contains any number of bookmarks
88 | group: [{
89 |
90 | // bookmark group name
91 | name: 'Productivity',
92 |
93 | // bookmark group description
94 | description: 'Daily apps',
95 |
96 | // bookmark colours
97 | // colour defined with HSL: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl
98 | color: {
99 | primary: { hsl: [0, 0, 100] }, // colour for group name and icons
100 | secondary: { hsl: [200, 60, 70] } // colour for group description and the open all button
101 | },
102 |
103 | list: [
104 | // list of bookmarks in this group
105 | // add as many objects as needed
106 | // the first key defines the visual element of the bookmark
107 | // string: icon|letter|image
108 |
109 | // icon eg:
110 | // use a Font Awesome class name found on https://fontawesome.com/icons
111 | // { icon: 'fa-solid fa-envelope', url: 'https://website.com/' },
112 |
113 | // letter eg:
114 | // { letter: 'ABC', url: 'https://website.com/' },
115 |
116 | // image eg:
117 | // { image: 'https://example.com/image.jpg', url: 'https://website.com/' },
118 |
119 | { icon: 'fa-solid fa-envelope', url: 'https://mail.google.com/' },
120 | { icon: 'fa-brands fa-slack', url: 'https://slack.com/signin/' },
121 | { icon: 'fa-brands fa-github', url: 'https://github.com/' },
122 | { icon: 'fa-brands fa-codepen', url: 'https://codepen.io/' },
123 | { icon: 'fa-solid fa-diamond', url: 'https://whimsical.com/login/' },
124 | { icon: 'fa-brands fa-figma', url: 'https://figma.com/' },
125 | { icon: 'fa-brands fa-dropbox', url: 'https://dropbox.com/' },
126 | { icon: 'fa-brands fa-google-drive', url: 'https://drive.google.com/' },
127 | { icon: 'fa-solid fa-calendar-day', url: 'https://calendar.google.com/calendar/' },
128 | ]
129 |
130 | }, {
131 | name: 'Cool stuff',
132 | description: 'Downtime and media',
133 | color: { primary: { hsl: [0, 0, 100] }, secondary: { hsl: [250, 60, 70] } },
134 | list: [
135 | { icon: 'fa-brands fa-reddit-alien', url: 'https://reddit.com/' },
136 | { icon: 'fa-brands fa-artstation', url: 'https://www.artstation.com/' },
137 | { icon: 'fa-brands fa-discord', url: 'https://discord.com/' },
138 | { icon: 'fa-solid fa-paperclip', url: 'https://www.decisionproblem.com/paperclips/' },
139 | { icon: 'fa-solid fa-dice-d20', url: 'https://zombiefox.github.io/diceRoller/' },
140 | { icon: 'fa-brands fa-dribbble', url: 'https://dribbble.com/' },
141 | ]
142 | }, {
143 | name: 'Entertainment',
144 | description: 'Films, videos, streams',
145 | color: { primary: { hsl: [0, 0, 100] }, secondary: { hsl: [0, 60, 70] } },
146 | list: [
147 | { icon: 'fa-brands fa-vimeo', url: 'https://vimeo.com/' },
148 | { icon: 'fa-brands fa-youtube', url: 'https://youtube.com/' },
149 | { icon: 'fa-solid fa-clapperboard', url: 'https://netflix.com/' },
150 | { icon: 'fa-brands fa-twitch', url: 'https://www.twitch.tv/' },
151 | ]
152 | }, {
153 | name: 'Ref',
154 | description: 'Docs, code + specs',
155 | color: { primary: { hsl: [0, 0, 100] }, secondary: { hsl: [40, 60, 70] } },
156 | list: [
157 | { icon: 'fa-solid fa-code', url: 'https://devdocs.io/' },
158 | { icon: 'fa-brands fa-css3-alt', url: 'https://developer.mozilla.org/en-US/docs/Learn/CSS/' },
159 | { icon: 'fa-brands fa-stack-overflow', url: 'https://stackoverflow.com/' },
160 | { icon: 'fa-brands fa-bootstrap', url: 'https://getbootstrap.com/docs/5.2/getting-started/introduction/' },
161 | { icon: 'fa-brands fa-npm', url: 'https://www.npmjs.com/' },
162 | ]
163 | }]
164 |
165 | },
166 |
167 | background: {
168 |
169 | // the background is made of three layers
170 | // colour at the bottom
171 | // an image above the colour layer
172 | // a gradient above the image layer
173 |
174 | // background layer 1 (bottom)
175 | // colour defined with HSL: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl
176 | color: {
177 | hsl: [220, 30, 50]
178 | },
179 |
180 | // background layer 2 (middle)
181 | image: {
182 |
183 | url: 'https://i.redd.it/niecy4jmlmh81.png', // background image url
184 |
185 | // opacity of background image
186 | opacity: 1, // range: min:0|max:1
187 |
188 | // grayscale of background image
189 | grayscale: 0.1, // range: min:0|max:1
190 |
191 | // blur of background image
192 | blur: 0 // range: min:0|max:*
193 |
194 | },
195 |
196 | // background layer 3 (top)
197 | gradient: {
198 |
199 | // gradient angle
200 | degree: 90, // range: min:0|max:360
201 |
202 | color: [
203 | // gradient colours
204 | // each object is a single colour and position in the gradient layer
205 | // the position value defines the location of the colour stop
206 | // each stop should not be a lower value than the previous stop
207 | // eg: {..., position: 40}, {..., position: 60}, {..., position: 90},
208 | // add as many objects as needed
209 | {
210 | // colour defined with HSLA: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsla
211 | hsla: [220, 80, 18, 0.6],
212 | position: 30 // range: min:0|max:100
213 | },
214 | { hsla: [240, 85, 25, 0.4], position: 40 },
215 | { hsla: [280, 40, 25, 0.1], position: 100 }
216 | ]
217 |
218 | }
219 |
220 | }
221 |
222 | };
223 |
--------------------------------------------------------------------------------
/src/component/utility/randomString.js:
--------------------------------------------------------------------------------
1 | export const randomString = ({
2 | letter = false,
3 | adjectivesCount = false
4 | } = {}) => {
5 |
6 | const alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
7 |
8 | const adjectives = {
9 | a: ['Aback', 'Abaft', 'Abandoned', 'Abashed', 'Aberrant', 'Abhorrent', 'Abiding', 'Abject', 'Ablaze', 'Able', 'Abnormal', 'Aboriginal', 'Abortive', 'Abounding', 'Abrasive', 'Abrupt', 'Absent', 'Absorbed', 'Absorbing', 'Abstracted', 'Absurd', 'Abundant', 'Abusive', 'Acceptable', 'Accessible', 'Accidental', 'Accurate', 'Acid', 'Acidic', 'Acoustic', 'Acrid', 'Adamant', 'Adaptable', 'Adhesive', 'Adjoining', 'Adorable', 'Adventurous', 'Afraid', 'Aggressive', 'Agonizing', 'Agreeable', 'Ahead', 'Ajar', 'Alert', 'Alike', 'Alive', 'Alleged', 'Alluring', 'Aloof', 'Amazing', 'Ambiguous', 'Ambitious', 'Amuck', 'Amused', 'Amusing', 'Ancient', 'Angry', 'Animated', 'Annoyed', 'Annoying', 'Anxious', 'Apathetic', 'Aquatic', 'Aromatic', 'Arrogant', 'Ashamed', 'Aspiring', 'Assorted', 'Astonishing', 'Attractive', 'Auspicious', 'Automatic', 'Available', 'Average', 'Aware', 'Awesome', 'Axiomatic'],
10 | b: ['Bad', 'Barbarous', 'Bashful', 'Bawdy', 'Beautiful', 'Befitting', 'Belligerent', 'Beneficial', 'Bent', 'Berserk', 'Bewildered', 'Big', 'Billowy', 'Bitter', 'Bizarre', 'Black', 'Bloody', 'Blue', 'Blushing', 'Boiling', 'Boorish', 'Bored', 'Boring', 'Bouncy', 'Boundless', 'Brainy', 'Brash', 'Brave', 'Brawny', 'Breakable', 'Breezy', 'Brief', 'Bright', 'Broad', 'Broken', 'Brown', 'Bumpy', 'Burly', 'Bustling', 'Busy'],
11 | c: ['Cagey', 'Calculating', 'Callous', 'Calm', 'Capable', 'Capricious', 'Careful', 'Careless', 'Caring', 'Cautious', 'Ceaseless', 'Certain', 'Changeable', 'Charming', 'Cheap', 'Cheerful', 'Chemical', 'Chief', 'Childlike', 'Chilly', 'Chivalrous', 'Chubby', 'Chunky', 'Clammy', 'Classy', 'Clean', 'Clear', 'Clever', 'Cloistered', 'Cloudy', 'Closed', 'Clumsy', 'Cluttered', 'Coherent', 'Cold', 'Colorful', 'Colossal', 'Combative', 'Comfortable', 'Common', 'Complete', 'Complex', 'Concerned', 'Condemned', 'Confused', 'Conscious', 'Cooing', 'Cool', 'Cooperative', 'Coordinated', 'Courageous', 'Cowardly', 'Crabby', 'Craven', 'Crazy', 'Creepy', 'Crooked', 'Crowded', 'Cruel', 'Cuddly', 'Cultured', 'Cumbersome', 'Curious', 'Curly', 'Curved', 'Curvy', 'Cut', 'Cute', 'Cynical'],
12 | d: ['Daffy', 'Daily', 'Damaged', 'Damaging', 'Damp', 'Dangerous', 'Dapper', 'Dark', 'Dashing', 'Dazzling', 'Deadpan', 'Deafening', 'Dear', 'Debonair', 'Decisive', 'Decorous', 'Deep', 'Deeply', 'Defeated', 'Defective', 'Defiant', 'Delicate', 'Delicious', 'Delightful', 'Demonic', 'Delirious', 'Dependent', 'Depressed', 'Deranged', 'Descriptive', 'Deserted', 'Detailed', 'Determined', 'Devilish', 'Didactic', 'Different', 'Difficult', 'Diligent', 'Direful', 'Dirty', 'Disagreeable', 'Disastrous', 'Discreet', 'Disgusted', 'Disgusting', 'Disillusioned', 'Dispensable', 'Distinct', 'Disturbed', 'Divergent', 'Dizzy', 'Domineering', 'Doubtful', 'Drab', 'Draconian', 'Dramatic', 'Dreary', 'Drunk', 'Dry', 'Dull', 'Dusty', 'Dynamic', 'Dysfunctional'],
13 | e: ['Eager', 'Early', 'Earsplitting', 'Earthy', 'Easy', 'Eatable', 'Economic', 'Educated', 'Efficacious', 'Efficient', 'Elastic', 'Elated', 'Elderly', 'Electric', 'Elegant', 'Elfin', 'Elite', 'Embarrassed', 'Eminent', 'Empty', 'Enchanted', 'Enchanting', 'Encouraging', 'Endurable', 'Energetic', 'Enormous', 'Entertaining', 'Enthusiastic', 'Envious', 'Equable', 'Equal', 'Erratic', 'Ethereal', 'Evanescent', 'Evasive', 'Even', 'Excellent', 'Excited', 'Exciting', 'Exclusive', 'Exotic', 'Expensive', 'Exuberant', 'Exultant'],
14 | f: ['Fabulous', 'Faded', 'Faint', 'Fair', 'Faithful', 'Fallacious', 'False', 'Familiar', 'Famous', 'Fanatical', 'Fancy', 'Fantastic', 'Far', 'Fascinated', 'Fast', 'Fat', 'Faulty', 'Fearful', 'Fearless', 'Feeble', 'Feigned', 'Fertile', 'Festive', 'Few', 'Fierce', 'Filthy', 'Fine', 'Finicky', 'First', 'Fixed', 'Flagrant', 'Flaky', 'Flashy', 'Flat', 'Flawless', 'Flimsy', 'Flippant', 'Flowery', 'Fluffy', 'Fluttering', 'Foamy', 'Foolish', 'Foregoing', 'Forgetful', 'Fortunate', 'Frail', 'Fragile', 'Frantic', 'Free', 'Freezing', 'Frequent', 'Fresh', 'Fretful', 'Friendly', 'Frightened', 'Frightening', 'Full', 'Fumbling', 'Functional', 'Funny', 'Furry', 'Furtive', 'Future', 'Futuristic', 'Fuzzy'],
15 | g: ['Gabby', 'Gainful', 'Gamy', 'Garrulous', 'Gaudy', 'General', 'Gentle', 'Giant', 'Giddy', 'Gifted', 'Gigantic', 'Glamorous', 'Gleaming', 'Glib', 'Glistening', 'Glorious', 'Glossy', 'Good', 'Goofy', 'Gorgeous', 'Graceful', 'Grandiose', 'Grateful', 'Gratis', 'Gray', 'Greasy', 'Great', 'Greedy', 'Green', 'Grey', 'Grieving', 'Groovy', 'Grotesque', 'Grouchy', 'Grubby', 'Gruesome', 'Grumpy', 'Guarded', 'Guiltless', 'Gullible', 'Gusty', 'Guttural'],
16 | h: ['Habitual', 'Half', 'Hallowed', 'Halting', 'Handsome', 'Handy', 'Hapless', 'Happy', 'Hard', 'Harmonious', 'Harsh', 'Hateful', 'Heady', 'Healthy', 'Heartbreaking', 'Heavenly', 'Heavy', 'Hellish', 'Helpful', 'Helpless', 'Hesitant', 'Hideous', 'High', 'Highfalutin', 'Hilarious', 'Hissing', 'Historical', 'Holistic', 'Hollow', 'Homeless', 'Homely', 'Honorable', 'Horrible', 'Hospitable', 'Hot', 'Huge', 'Hulking', 'Humdrum', 'Humorous', 'Hungry', 'Hurried', 'Hurt', 'Hushed', 'Husky', 'Hypnotic', 'Hysterical'],
17 | i: ['Icky', 'Icy', 'Idiotic', 'Ignorant', 'Ill', 'Illegal', 'Illustrious', 'Imaginary', 'Immense', 'Imminent', 'Impartial', 'Imperfect', 'Impolite', 'Important', 'Imported', 'Impossible', 'Incandescent', 'Incompetent', 'Inconclusive', 'Industrious', 'Incredible', 'Inexpensive', 'Infamous', 'Innate', 'Innocent', 'Inquisitive', 'Insidious', 'Instinctive', 'Intelligent', 'Interesting', 'Internal', 'Invincible', 'Irate', 'Irritating', 'Itchy'],
18 | j: ['Jaded', 'Jagged', 'Jazzy', 'Jealous', 'Jesting', 'Jinxed', 'Jittery', 'Jobless', 'Jolly', 'Joyous', 'Judicious', 'Juicy', 'Jumbled', 'Jumpy', 'Juvenile'],
19 | k: ['Keen', 'Kind', 'Kindhearted', 'Kindly', 'Knotty', 'Knowing', 'Knowledgeable', 'Known'],
20 | l: ['Labored', 'Lackadaisical', 'Lacking', 'Lame', 'Lamentable', 'Languid', 'Large', 'Last', 'Late', 'Laughable', 'Lavish', 'Lazy', 'Lean', 'Learned', 'Left', 'Legal', 'Lethal', 'Level', 'Lewd', 'Light', 'Like', 'Likeable', 'Limping', 'Literate', 'Little', 'Lively', 'Living', 'Lonely', 'Long', 'Longing', 'Loose', 'Lopsided', 'Loud', 'Loutish', 'Lovely', 'Loving', 'Low', 'Lowly', 'Lucky', 'Ludicrous', 'Lumpy', 'Lush', 'Luxuriant', 'Lying', 'Lyrical'],
21 | m: ['Macabre', 'Macho', 'Maddening', 'Madly', 'Magenta', 'Magical', 'Magnificent', 'Majestic', 'Makeshift', 'Malicious', 'Mammoth', 'Maniacal', 'Many', 'Marked', 'Massive', 'Married', 'Marvelous', 'Material', 'Materialistic', 'Mature', 'Mean', 'Measly', 'Meaty', 'Medical', 'Meek', 'Mellow', 'Melodic', 'Melted', 'Merciful', 'Mere', 'Messy', 'Mighty', 'Military', 'Milky', 'Mindless', 'Miniature', 'Minor', 'Miscreant', 'Misty', 'Mixed', 'Moaning', 'Modern', 'Moldy', 'Momentous', 'Motionless', 'Mountainous', 'Muddled', 'Mundane', 'Murky', 'Mushy', 'Mute', 'Mysterious'],
22 | n: ['Naive', 'Nappy', 'Narrow', 'Nasty', 'Natural', 'Naughty', 'Nauseating', 'Near', 'Neat', 'Nebulous', 'Necessary', 'Needless', 'Needy', 'Neighborly', 'Nervous', 'New', 'Next', 'Nice', 'Nifty', 'Nimble', 'Nippy', 'Noiseless', 'Noisy', 'Nonchalant', 'Nondescript', 'Nonstop', 'Normal', 'Nostalgic', 'Nosy', 'Noxious', 'Numberless', 'Numerous', 'Nutritious', 'Nutty'],
23 | o: ['Oafish', 'Obedient', 'Obeisant', 'Obese', 'Obnoxious', 'Obscene', 'Obsequious', 'Observant', 'Obsolete', 'Obtainable', 'Oceanic', 'Odd', 'Offbeat', 'Old', 'Omniscient', 'Onerous', 'Open', 'Opposite', 'Optimal', 'Orange', 'Ordinary', 'Organic', 'Ossified', 'Outgoing', 'Outrageous', 'Outstanding', 'Oval', 'Overconfident', 'Overjoyed', 'Overrated', 'Overt', 'Overwrought'],
24 | p: ['Painful', 'Painstaking', 'Pale', 'Paltry', 'Panicky', 'Panoramic', 'Parallel', 'Parched', 'Parsimonious', 'Past', 'Pastoral', 'Pathetic', 'Peaceful', 'Penitent', 'Perfect', 'Periodic', 'Permissible', 'Perpetual', 'Petite', 'Phobic', 'Physical', 'Picayune', 'Pink', 'Piquant', 'Placid', 'Plain', 'Plant', 'Plastic', 'Plausible', 'Pleasant', 'Plucky', 'Pointless', 'Poised', 'Polite', 'Political', 'Poor', 'Possessive', 'Possible', 'Powerful', 'Precious', 'Premium', 'Present', 'Pretty', 'Previous', 'Pricey', 'Prickly', 'Private', 'Probable', 'Productive', 'Profuse', 'Protective', 'Proud', 'Psychedelic', 'Psychotic', 'Public', 'Puffy', 'Pumped', 'Puny', 'Purple', 'Purring', 'Pushy', 'Puzzled', 'Puzzling'],
25 | q: ['Quaint', 'Quality', 'Quarrelsome', 'Questionable', 'Questioning', 'Quick', 'Quiet', 'Quirky', 'Quixotic', 'Quizzical'],
26 | r: ['Rabid', 'Ragged', 'Rainy', 'Rambunctious', 'Rampant', 'Rapid', 'Rare', 'Raspy', 'Ratty', 'Ready', 'Real', 'Rebel', 'Receptive', 'Recondite', 'Red', 'Redundant', 'Reflective', 'Regular', 'Relieved', 'Remarkable', 'Reminiscent', 'Repulsive', 'Resolute', 'Resonant', 'Responsible', 'Rhetorical', 'Rich', 'Right', 'Righteous', 'Rightful', 'Rigid', 'Ripe', 'Ritzy', 'Roasted', 'Robust', 'Romantic', 'Roomy', 'Rotten', 'Rough', 'Round', 'Royal', 'Ruddy', 'Rude', 'Rural', 'Rustic', 'Ruthless'],
27 | s: ['Sable', 'Sad', 'Safe', 'Salty', 'Same', 'Sassy', 'Satisfying', 'Savory', 'Scandalous', 'Scarce', 'Scared', 'Scary', 'Scattered', 'Scientific', 'Scintillating', 'Scrawny', 'Screeching', 'Second', 'Secret', 'Secretive', 'Sedate', 'Seemly', 'Selective', 'Selfish', 'Separate', 'Serious', 'Shaggy', 'Shaky', 'Shallow', 'Sharp', 'Shiny', 'Shivering', 'Shocking', 'Short', 'Shrill', 'Shut', 'Shy', 'Sick', 'Silent', 'Silky', 'Silly', 'Simple', 'Simplistic', 'Sincere', 'Skillful', 'Skinny', 'Sleepy', 'Slim', 'Slimy', 'Slippery', 'Sloppy', 'Slow', 'Small', 'Smart', 'Smelly', 'Smiling', 'Smoggy', 'Smooth', 'Sneaky', 'Snobbish', 'Snotty', 'Soft', 'Soggy', 'Solid', 'Somber', 'Sophisticated', 'Sordid', 'Sore', 'Sour', 'Sparkling', 'Special', 'Spectacular', 'Spicy', 'Spiffy', 'Spiky', 'Spiritual', 'Spiteful', 'Splendid', 'Spooky', 'Spotless', 'Spotted', 'Spotty', 'Spurious', 'Squalid', 'Square', 'Squealing', 'Squeamish', 'Staking', 'Stale', 'Standing', 'Statuesque', 'Steadfast', 'Steady', 'Steep', 'Stereotyped', 'Sticky', 'Stiff', 'Stimulating', 'Stingy', 'Stormy', 'Straight', 'Strange', 'Striped', 'Strong', 'Stupendous', 'Sturdy', 'Subdued', 'Subsequent', 'Substantial', 'Successful', 'Succinct', 'Sudden', 'Sulky', 'Super', 'Superb', 'Superficial', 'Supreme', 'Swanky', 'Sweet', 'Sweltering', 'Swift', 'Symptomatic', 'Synonymous'],
28 | t: ['Taboo', 'Tacit', 'Tacky', 'Talented', 'Tall', 'Tame', 'Tan', 'Tangible', 'Tangy', 'Tart', 'Tasteful', 'Tasteless', 'Tasty', 'Tawdry', 'Tearful', 'Tedious', 'Teeny', 'Telling', 'Temporary', 'Ten', 'Tender', 'Tense', 'Tenuous', 'Terrific', 'Tested', 'Testy', 'Thankful', 'Therapeutic', 'Thick', 'Thin', 'Thinkable', 'Third', 'Thirsty', 'Thoughtful', 'Thoughtless', 'Threatening', 'Thundering', 'Tidy', 'Tight', 'Tightfisted', 'Tiny', 'Tired', 'Tiresome', 'Toothsome', 'Torpid', 'Tough', 'Towering', 'Tranquil', 'Trashy', 'Tremendous', 'Tricky', 'Trite', 'Troubled', 'Truculent', 'True', 'Truthful', 'Typical'],
29 | u: ['Ubiquitous', 'Ultra', 'Unable', 'Unaccountable', 'Unadvised', 'Unarmed', 'Unbecoming', 'Unbiased', 'Uncovered', 'Understood', 'Undesirable', 'Unequal', 'Unequaled', 'Uneven', 'Unhealthy', 'Uninterested', 'Unique', 'Unkempt', 'Unknown', 'Unnatural', 'Unruly', 'Unsightly', 'Unsuitable', 'Untidy', 'Unused', 'Unusual', 'Unwieldy', 'Unwritten', 'Upbeat', 'Uppity', 'Upset', 'Uptight', 'Used', 'Useful', 'Useless', 'Utopian'],
30 | v: ['Vacuous', 'Vagabond', 'Vague', 'Valuable', 'Various', 'Vast', 'Vengeful', 'Venomous', 'Verdant', 'Versed', 'Victorious', 'Vigorous', 'Violent', 'Violet', 'Vivacious', 'Voiceless', 'Volatile', 'Voracious', 'Vulgar'],
31 | w: ['Wacky', 'Waggish', 'Waiting', 'Wakeful', 'Wandering', 'Wanting', 'Warlike', 'Warm', 'Wary', 'Wasteful', 'Watery', 'Weak', 'Wealthy', 'Weary', 'Wet', 'Whimsical', 'Whispering', 'White', 'Whole', 'Wholesale', 'Wicked', 'Wide', 'Wiggly', 'Wild', 'Willing', 'Windy', 'Wiry', 'Wise', 'Wistful', 'Witty', 'Woebegone', 'Wonderful', 'Wooden', 'Woozy', 'Workable', 'Worried', 'Worthless', 'Wrathful', 'Wretched', 'Wrong', 'Wry'],
32 | x: ['Xenial', 'Xenodochial', 'Xenophobic'],
33 | y: ['Yellow', 'Yielding', 'Young', 'Youthful', 'Yummy'],
34 | z: ['Zany', 'Zealous', 'Zesty', 'Zippy', 'Zombiesque', 'Zombie', 'Zonked']
35 | };
36 |
37 | const animals = {
38 | a: ['Aardvark', 'Albatross', 'Alligator', 'Alpaca', 'Ant', 'Anteater', 'Antelope', 'Ape', 'Armadillo'],
39 | b: ['Baboon', 'Badger', 'Barracuda', 'Bat', 'Bear', 'Beaver', 'Bee', 'Bison', 'Boar', 'Buffalo', 'Butterfly'],
40 | c: ['Camel', 'Capybara', 'Caribou', 'Cassowary', 'Cat', 'Caterpillar', 'Cattle', 'Chamois', 'Cheetah', 'Chicken', 'Chimpanzee', 'Chinchilla', 'Chough', 'Clam', 'Cobra', 'Cockroach', 'Cod', 'Cormorant', 'Coyote', 'Crab', 'Crane', 'Crocodile', 'Crow', 'Curlew'],
41 | d: ['Deer', 'Dinosaur', 'Dog', 'Dogfish', 'Dolphin', 'Donkey', 'Dotterel', 'Dove', 'Dragonfly', 'Duck', 'Dugong', 'Dunlin'],
42 | e: ['Eagle', 'Echidna', 'Eel', 'Eland', 'Elephant', 'Elephant Seal', 'Elk', 'Emu'],
43 | f: ['Falcon', 'Ferret', 'Finch', 'Fish', 'Flamingo', 'Fly', 'Fox', 'Frog'],
44 | g: ['Gaur', 'Gazelle', 'Gerbil', 'Giant Panda', 'Giraffe', 'Gnat', 'Gnu', 'Goat', 'Goose', 'Goldfinch', 'Goldfish', 'Gorilla', 'Goshawk', 'Grasshopper', 'Grouse', 'Guanaco', 'Guinea Fowl', 'Guinea Pig', 'Gull'],
45 | h: ['Hamster', 'Hare', 'Hawk', 'Hedgehog', 'Heron', 'Herring', 'Hippopotamus', 'Hornet', 'Horse', 'Human', 'Hummingbird', 'Hyena'],
46 | i: ['Ibex', 'Ibis', 'Iguana', 'Impala', 'Isopod'],
47 | j: ['Jackal', 'Jaguar', 'Jay', 'Jellyfish'],
48 | k: ['Kangaroo', 'Kingfisher', 'Koala', 'Komodo Dragon', 'Kookabura', 'Kouprey', 'Kudu'],
49 | l: ['Lapwing', 'Lark', 'Lemur', 'Leopard', 'Lima', 'Lion', 'Llama', 'Lobster', 'Locust', 'Loris', 'Louse', 'Lyrebird'],
50 | m: ['Magpie', 'Mallard', 'Manatee', 'Mandrill', 'Mantis', 'Marten', 'Meerkat', 'Mink', 'Mole', 'Mongoose', 'Monkey', 'Moose', 'Mouse', 'Mosquito', 'Mule'],
51 | n: ['Narwhal', 'Newt', 'Nightingale', 'Nyala'],
52 | o: ['Octopus', 'Okapi', 'Opossum', 'Oryx', 'Ostrich', 'Otter', 'Owl', 'Ox', 'Oyster'],
53 | p: ['Panther', 'Parrot', 'Partridge', 'Peafowl', 'Pelican', 'Penguin', 'Pheasant', 'Pig', 'Pigeon', 'Polar Bear', 'Pony', 'Porcupine', 'Porpoise'],
54 | q: ['Quail', 'Quelea', 'Quetzal'],
55 | r: ['Rabbit', 'Raccoon', 'Rail', 'Ram', 'Rat', 'Raven', 'Red Deer', 'Red Panda', 'Reindeer', 'Rhinoceros', 'Rook'],
56 | s: ['Salamander', 'Salmon', 'Sand Dollar', 'Sandpiper', 'Sardine', 'Scorpion', 'Sea Lion', 'Sea Urchin', 'Seahorse', 'Seal', 'Shark', 'Sheep', 'Shrew', 'Skunk', 'Snail', 'Snake', 'Sparrow', 'Spider', 'Spoonbill', 'Squid', 'Squirrel', 'Starling', 'Stingray', 'Stinkbug', 'Stork', 'Swallow', 'Swan'],
57 | t: ['Tapir', 'Tarsier', 'Termite', 'Tiger', 'Toad', 'Trout', 'Turkey', 'Turtle'],
58 | u: ['Uakari', 'Unau', 'Urial', 'Urchin', 'Umbrellabird', 'Unicornfish', 'Uromastyx', 'Uguisu'],
59 | v: ['Vampire Bat', 'Viper', 'Vole', 'Vulture'],
60 | w: ['Wallaby', 'Walrus', 'Wasp', 'Weasel', 'Whale', 'Wolf', 'Wolverine', 'Wombat', 'Woodcock', 'Woodpecker', 'Worm', 'Wren'],
61 | x: ['Xaviers Greenbul', 'Xeme', 'Xingu Corydoras', 'Xolo'],
62 | y: ['Yabby', 'Yak', 'Yellowhammer', 'Yellowjacket'],
63 | z: ['Zebra', 'Zebu', 'Zokor', 'Zorilla']
64 | };
65 |
66 | const action = {
67 | alliteration: {
68 | short: () => {
69 |
70 | const randomAdjective = adjectives[letter.toLowerCase()][Math.floor(Math.random() * adjectives[letter.toLowerCase()].length)];
71 |
72 | const randomAnimal = animals[letter.toLowerCase()][Math.floor(Math.random() * animals[letter.toLowerCase()].length)];
73 |
74 | return randomAdjective + ' ' + randomAnimal;
75 |
76 | },
77 | long: () => {
78 |
79 | let randomAdjective = '';
80 |
81 | for (let i = 1; i <= adjectivesCount; i++) {
82 |
83 | if (adjectives[letter.toLowerCase()].length > 0) {
84 | if (randomAdjective.length > 0) {
85 | randomAdjective = randomAdjective + ' ';
86 | }
87 | randomAdjective = randomAdjective + adjectives[letter.toLowerCase()].splice(Math.floor(Math.random() * adjectives[letter.toLowerCase()].length), 1);
88 | }
89 |
90 | }
91 |
92 | const randomAnimal = animals[letter.toLowerCase()][Math.floor(Math.random() * animals[letter.toLowerCase()].length)];
93 |
94 | return randomAdjective + ' ' + randomAnimal;
95 | }
96 | },
97 | mix: {
98 | short: () => {
99 |
100 | const adjectivesSeed = alphabet[Math.floor(Math.random() * (alphabet.length - 1))];
101 |
102 | const animalsSeed = alphabet[Math.floor(Math.random() * (alphabet.length - 1))];
103 |
104 | const randomAdjective = adjectives[adjectivesSeed][Math.floor(Math.random() * adjectives[adjectivesSeed].length)];
105 |
106 | const randomAnimal = animals[animalsSeed][Math.floor(Math.random() * animals[animalsSeed].length)];
107 |
108 | return randomAdjective + ' ' + randomAnimal;
109 |
110 | },
111 | long: () => {
112 |
113 | var randomAdjective = '';
114 |
115 | for (let i = 1; i <= adjectivesCount; i++) {
116 |
117 | var adjectiveLetter = alphabet[Math.floor(Math.random() * (alphabet.length - 1))];
118 |
119 | if (adjectiveLetter in adjectives && adjectives[adjectiveLetter].length > 0) {
120 |
121 | if (randomAdjective.length > 0) {
122 | randomAdjective = randomAdjective + ' ';
123 | }
124 |
125 | randomAdjective = randomAdjective + adjectives[adjectiveLetter].splice(Math.floor(Math.random() * adjectives[adjectiveLetter].length), 1);
126 |
127 | if (adjectives[adjectiveLetter].length == 0) {
128 | delete adjectives[adjectiveLetter];
129 | }
130 |
131 | }
132 | }
133 |
134 | var randomAnimalArray = animals[alphabet[Math.floor(Math.random() * (alphabet.length - 1))]];
135 |
136 | var randomAnimal = randomAnimalArray[Math.floor(Math.random() * (randomAnimalArray.length - 1))];
137 |
138 | return randomAdjective + ' ' + randomAnimal;
139 |
140 | }
141 | }
142 | };
143 |
144 | if (letter && alphabet.includes(letter.toLowerCase())) {
145 |
146 | if (adjectivesCount && adjectivesCount > 0) {
147 | return action.alliteration.long();
148 | } else {
149 | return action.alliteration.short();
150 | }
151 |
152 | } else {
153 |
154 | if (adjectivesCount && adjectivesCount > 0) {
155 | return action.mix.long();
156 | } else {
157 | return action.mix.short();
158 | }
159 |
160 | }
161 |
162 | };
163 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------