├── .eslintrc
├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── README.md
├── README_AMO.md
├── extension
├── _locales
│ ├── de
│ │ └── messages.json
│ ├── en
│ │ └── messages.json
│ ├── fr
│ │ └── messages.json
│ └── it
│ │ └── messages.json
├── background
│ └── background.wp.js
├── content
│ ├── addon-page
│ │ ├── addon-page.css
│ │ ├── addon-page.html
│ │ └── addon-page.wp.js
│ ├── detail-view
│ │ ├── detail-view.css
│ │ ├── detail-view.html
│ │ └── detail-view.wp.js
│ ├── options-ui
│ │ ├── options.css
│ │ ├── options.html
│ │ └── options.wp.js
│ ├── page-injections
│ │ ├── injection.css
│ │ └── injection.wp.js
│ ├── sidebar
│ │ ├── sidebar.css
│ │ ├── sidebar.html
│ │ └── sidebar.wp.js
│ └── tbb-menu
│ │ ├── tbb-menu.css
│ │ ├── tbb-menu.html
│ │ └── tbb-menu.wp.js
├── icons
│ ├── arrow-down.png
│ ├── bin.png
│ ├── bm.png
│ ├── bm2.png
│ ├── bm3.png
│ ├── book.png
│ ├── clock.png
│ ├── copy.png
│ ├── data.png
│ ├── debug.png
│ ├── del-bm.png
│ ├── dna.png
│ ├── donate.png
│ ├── double-arrow.png
│ ├── down.png
│ ├── download.png
│ ├── edit.png
│ ├── email.png
│ ├── export.png
│ ├── folder.png
│ ├── forklift.png
│ ├── gear.png
│ ├── ghost.png
│ ├── github.png
│ ├── highlight.png
│ ├── m.png
│ ├── menu-blue.png
│ ├── note.png
│ ├── note2.png
│ ├── note4.png
│ ├── off16.png
│ ├── off18.png
│ ├── on16.png
│ ├── on18.png
│ ├── on32.png
│ ├── on36.png
│ ├── on64.png
│ ├── pageaction16.png
│ ├── pageaction32.png
│ ├── pause.png
│ ├── redo.png
│ ├── retry.png
│ ├── save.png
│ ├── save2.png
│ ├── search.png
│ ├── sel.jpg
│ ├── start.png
│ ├── submit.png
│ ├── sync.png
│ ├── sync2.png
│ ├── tm48.png
│ ├── to-bm.png
│ ├── toggle-notes.png
│ ├── tools.png
│ ├── undo.png
│ ├── up.png
│ ├── view.png
│ └── view2.png
├── manifest.json
└── web-ext-artifacts
│ └── textmarker-5.3.3.zip
├── package-lock.json
├── package.json
├── src
├── background
│ ├── index.js
│ ├── modules
│ │ ├── context-menu.js
│ │ ├── error-logging.js
│ │ ├── injection-manager.js
│ │ ├── namer.js
│ │ ├── notifications.js
│ │ ├── page-action.js
│ │ ├── sidebars.js
│ │ ├── store-manager.js
│ │ ├── tabs.js
│ │ ├── version-manager.js
│ │ ├── web-navigation.js
│ │ └── windows.js
│ ├── port.js
│ ├── storage.js
│ └── utils.js
├── content
│ ├── _shared
│ │ ├── sass
│ │ │ ├── _buttons.scss
│ │ │ ├── _forms.scss
│ │ │ ├── _general.scss
│ │ │ ├── _helpers.scss
│ │ │ ├── _icons.scss
│ │ │ ├── _infos.scss
│ │ │ ├── _links.scss
│ │ │ ├── _notes.scss
│ │ │ ├── _reset.scss
│ │ │ ├── _shadows.scss
│ │ │ ├── _switches.scss
│ │ │ └── _vars.scss
│ │ └── utils.js
│ ├── addon-page
│ │ ├── _store.js
│ │ ├── bootstrap.js
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── contact.js
│ │ │ ├── history-pagination.js
│ │ │ ├── history-sort.js
│ │ │ ├── history.js
│ │ │ ├── import.js
│ │ │ ├── logs.js
│ │ │ ├── marker.js
│ │ │ ├── nav.js
│ │ │ ├── permissions.js
│ │ │ ├── settings.js
│ │ │ ├── syncing.js
│ │ │ ├── toggler.js
│ │ │ └── troubleshooting.js
│ │ ├── port.js
│ │ └── sass
│ │ │ ├── _buttons.scss
│ │ │ ├── _general.scss
│ │ │ ├── _helpers.scss
│ │ │ ├── _navs.scss
│ │ │ ├── components
│ │ │ ├── _alerts.scss
│ │ │ ├── _contact.scss
│ │ │ ├── _data-management.scss
│ │ │ ├── _history.scss
│ │ │ ├── _logs.scss
│ │ │ ├── _news.scss
│ │ │ ├── _settings.scss
│ │ │ └── _troubleshooting.scss
│ │ │ └── index.scss
│ ├── detail-view
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── header.js
│ │ │ ├── marks.js
│ │ │ └── meta.js
│ │ └── sass
│ │ │ ├── _general.scss
│ │ │ ├── _tables.scss
│ │ │ └── index.scss
│ ├── options-ui
│ │ ├── index.js
│ │ └── sass
│ │ │ └── index.scss
│ ├── page-injections
│ │ ├── _store.js
│ │ ├── index.js
│ │ ├── main.js
│ │ ├── modules
│ │ │ ├── auto-marker.js
│ │ │ ├── bookmark.js
│ │ │ ├── contextmenu.js
│ │ │ ├── mark-item.js
│ │ │ ├── marker-popup.js
│ │ │ ├── marker.js
│ │ │ ├── note.js
│ │ │ ├── notes.js
│ │ │ ├── page.js
│ │ │ ├── restorer.js
│ │ │ ├── selection.js
│ │ │ └── tmui.js
│ │ ├── port.js
│ │ └── sass
│ │ │ ├── components
│ │ │ ├── _copyshop.scss
│ │ │ ├── _marker-popup.scss
│ │ │ ├── _marks.scss
│ │ │ ├── _notes.scss
│ │ │ └── _tmui.scss
│ │ │ └── index.scss
│ ├── sidebar
│ │ ├── _store.js
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── header.js
│ │ │ ├── history-actions.js
│ │ │ ├── links.js
│ │ │ ├── mark-actions.js
│ │ │ ├── markers.js
│ │ │ ├── marks.js
│ │ │ ├── meta-infos.js
│ │ │ ├── page-actions.js
│ │ │ ├── page-notes.js
│ │ │ ├── tabs.js
│ │ │ ├── tags.js
│ │ │ └── themes.js
│ │ ├── port.js
│ │ └── sass
│ │ │ ├── _buttons.scss
│ │ │ ├── _form-elements.scss
│ │ │ ├── _general.scss
│ │ │ ├── components
│ │ │ ├── _actions.scss
│ │ │ ├── _links.scss
│ │ │ ├── _marker.scss
│ │ │ ├── _marks.scss
│ │ │ ├── _page-notes.scss
│ │ │ ├── _tabs.scss
│ │ │ └── _tags.scss
│ │ │ ├── index.scss
│ │ │ └── themes
│ │ │ └── _dark.scss
│ └── tbb-menu
│ │ ├── index.js
│ │ ├── port.js
│ │ └── sass
│ │ └── index.scss
├── data
│ ├── default-storage.js
│ ├── global-settings.js
│ └── log-keys.js
├── icons
│ ├── arrow-down.png
│ ├── bin.png
│ ├── bm.png
│ ├── bm2.png
│ ├── bm3.png
│ ├── book.png
│ ├── clipboard.png
│ ├── clock.png
│ ├── copy.png
│ ├── debug.png
│ ├── del-bm.png
│ ├── dna.png
│ ├── donate.png
│ ├── double-arrow.png
│ ├── down.png
│ ├── download.png
│ ├── edit.png
│ ├── email.png
│ ├── export.png
│ ├── forklift.png
│ ├── ghost.png
│ ├── github.png
│ ├── highlight.png
│ ├── m.png
│ ├── menu-blue.png
│ ├── note.png
│ ├── note2.png
│ ├── note4.png
│ ├── off16.png
│ ├── off18.png
│ ├── on16.png
│ ├── on18.png
│ ├── on32.png
│ ├── on36.png
│ ├── on64.png
│ ├── pageaction16.png
│ ├── pageaction32.png
│ ├── pause.png
│ ├── redo.png
│ ├── retry.png
│ ├── save.png
│ ├── save2.png
│ ├── search.png
│ ├── sel.jpg
│ ├── start.png
│ ├── submit.png
│ ├── sync.png
│ ├── sync2.png
│ ├── tm48.png
│ ├── tm64.png
│ ├── to-bm.png
│ ├── toggle-notes.png
│ ├── tools.png
│ ├── undo.png
│ ├── up.png
│ └── view.png
└── utils
│ ├── copy.js
│ ├── dommodule.js
│ ├── error-tracker.js
│ ├── extend.js
│ ├── getActiveTab.js
│ ├── hashless.js
│ ├── l10n.js
│ ├── mediator.js
│ ├── module.js
│ ├── port.js
│ └── store.js
└── webpack.config.js
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "commonjs": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: unflybi
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[bug] add title here"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 |
15 | **Expected behavior**
16 | A clear and concise description of what you expected to happen (leave blank if obvious from bug description)
17 |
18 | **Screenshots**
19 | If applicable, add screenshots to help explain your problem.
20 |
21 | **Which OS, Firefox edition, Textmarker version**
22 | These infos could be helpful in order to reproduce the buggy behavior
23 | OS (i.e. Linux, MacOS, Win7, Win10, Android ...):
24 | FF edition (i.e. Regular, Nightly, ESR, ...):
25 | Textmarker version (i.e. 5.1.2):
26 |
27 | **Additional context**
28 | Add any other context about the problem here.
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[feature] add title here"
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Textmaker
2 | ========
3 |
4 | [](https://addons.mozilla.org/firefox/addon/textmarkerpro/)
5 |
6 | Highly customizable text highlighter with different colors and save option for later visits.
7 |
--------------------------------------------------------------------------------
/README_AMO.md:
--------------------------------------------------------------------------------
1 | # Textmarker - build instructions for AMO review team
2 |
3 | ## Summarized
4 |
5 | __Source code:__
6 | The _src/_ folder contains the JavaScript and SASS source files.
7 |
8 | _webpack_ is used for precompiling and bundling.
9 |
10 | __Production code:__
11 | The _extension/_ folder contains the addon code.
12 |
13 | ## Prerequisites
14 |
15 | * __node.js__ v16.15.1
16 | * __npm__ 8.11.0
17 |
18 | ## Installing modules
19 |
20 | __Run the following from console/terminal (root folder):__
21 | for installing all required node modules (see: _package.json_)
22 | ```
23 | npm install
24 | ```
25 |
26 | ## Webpack
27 | __Run the following from console/terminal (root folder):__
28 | for bundling the source code
29 | ```
30 | npm run prod
31 | ```
32 |
--------------------------------------------------------------------------------
/extension/content/detail-view/detail-view.css:
--------------------------------------------------------------------------------
1 | html, body, object, iframe,
2 | div, h1, h2, h3, h4, h5, h6, p,
3 | span, a, abbr, img,
4 | em, small, sub, sup,
5 | dl, dt, dd, ol, ul, li,
6 | fieldset, form, label,
7 | table, caption, tbody, thead, tr, th, td {
8 | margin: 0;
9 | padding: 0;
10 | border: 0;
11 | outline: 0;
12 | font-weight: inherit;
13 | font-style: inherit;
14 | font-family: inherit;
15 | line-height: normal;
16 | vertical-align: baseline;
17 | color: inherit; }
18 |
19 | :focus {
20 | outline: none; }
21 |
22 | ::-moz-focus-inner {
23 | border: 0; }
24 |
25 | ::-moz-color-swatch {
26 | border: 0; }
27 |
28 | * {
29 | box-sizing: border-box; }
30 |
31 | body {
32 | font-family: Verdana, Arial, sans-serif;
33 | color: #37414b;
34 | cursor: default;
35 | font-size: 15px; }
36 |
37 | body {
38 | background: #fbfbfb; }
39 |
40 | p {
41 | margin-bottom: 5px; }
42 |
43 | header {
44 | background: #37414b;
45 | padding: 10px 20px; }
46 |
47 | header p {
48 | color: #2ba5b7; }
49 |
50 | section {
51 | margin-bottom: 30px; }
52 |
53 | section:last-child {
54 | margin: 0; }
55 |
56 | h1 {
57 | color: #fbfbfb;
58 | font-size: 20px; }
59 |
60 | h2 {
61 | font-size: 17px;
62 | margin-bottom: 15px; }
63 |
64 | table {
65 | border-collapse: collapse; }
66 | .disabled table {
67 | display: none; }
68 | .folded table {
69 | display: none; }
70 | table td {
71 | padding: 2px 15px 2px 0; }
72 | table.table--bordered {
73 | width: 100%; }
74 | table.table--bordered col {
75 | width: 50%; }
76 | table.table--bordered td, table.table--bordered th {
77 | border: 1px solid #c8c8c8; }
78 | table.table--bordered th {
79 | padding: 5px 10px;
80 | text-align: left; }
81 | table.table--bordered td {
82 | padding: 10px; }
83 | .no-notes table col:first-child {
84 | width: 100%; }
85 | .no-notes table col:last-child {
86 | width: 0; }
87 | .no-notes table thead {
88 | display: none; }
89 | .no-notes table td:last-child {
90 | display: none; }
91 | .hide-notes table col:first-child {
92 | width: 100%; }
93 | .hide-notes table col:last-child {
94 | width: 0; }
95 | .hide-notes table th:last-child {
96 | text-indent: -1000px;
97 | overflow: hidden; }
98 | .hide-notes table td:last-child {
99 | border: 0; }
100 | .hide-notes table .mark-note {
101 | display: none; }
102 |
103 | .col-toggle {
104 | background-image: url(../../icons/double-arrow.png);
105 | background-repeat: no-repeat;
106 | background-position: 0 center, 0 0;
107 | background-size: 18px, 100%;
108 | width: 18px;
109 | height: 20px;
110 | float: right;
111 | cursor: pointer; }
112 | .hide-notes .col-toggle {
113 | transform: rotate(180deg); }
114 |
115 | a, .link {
116 | color: #2ba5b7;
117 | text-decoration: none;
118 | cursor: pointer; }
119 | a:hover, .link:hover {
120 | text-decoration: underline; }
121 |
122 | #main {
123 | padding: 20px; }
124 |
125 | .table-toggle {
126 | background-image: url(../../icons/down.png);
127 | background-repeat: no-repeat;
128 | background-position: 0 center, 0 0;
129 | background-size: 14px, 100%;
130 | width: 14px;
131 | height: 12px;
132 | display: inline-block;
133 | cursor: pointer;
134 | opacity: 0.25;
135 | margin-right: 8px; }
136 | .folded .table-toggle {
137 | transform: rotate(-90deg); }
138 | .disabled .table-toggle {
139 | display: none; }
140 |
141 | .count {
142 | color: #c8c8c8; }
143 |
--------------------------------------------------------------------------------
/extension/content/detail-view/detail-view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Details
7 |
8 |
9 |
10 |
14 |
15 |
27 |
28 |
29 |
30 |
31 |
32 | | |
33 |
34 |
35 | | |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/extension/content/options-ui/options.css:
--------------------------------------------------------------------------------
1 | button {
2 | border: 1px solid #c8c8c8;
3 | background-image: linear-gradient(transparent 50%, rgba(43, 165, 183, 0.1) 50%);
4 | background-repeat: no-repeat;
5 | background-color: transparent;
6 | color: #777;
7 | cursor: pointer;
8 | font-size: 0.8rem;
9 | font-family: inherit;
10 | padding: 3px 7px 5px;
11 | min-height: 22px;
12 | transition: background 0.5s; }
13 | button:hover {
14 | background-image: linear-gradient(rgba(43, 165, 183, 0.1) 50%, transparent 50%);
15 | color: #37414b; }
16 |
17 | @keyframes btn {
18 | from {
19 | color: #2ba5b7; }
20 | to {
21 | color: #fbfbfb; } }
22 |
23 | button {
24 | width: 120px;
25 | font-size: inherit;
26 | margin-bottom: 12px;
27 | background: #fff; }
28 | button:hover {
29 | background: #2ba5b7;
30 | animation: 1s forwards btn; }
31 |
--------------------------------------------------------------------------------
/extension/content/options-ui/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Textmarker
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/extension/content/tbb-menu/tbb-menu.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Textmarker Toolbar Menu
7 |
8 |
9 |
10 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/extension/icons/arrow-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/arrow-down.png
--------------------------------------------------------------------------------
/extension/icons/bin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/bin.png
--------------------------------------------------------------------------------
/extension/icons/bm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/bm.png
--------------------------------------------------------------------------------
/extension/icons/bm2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/bm2.png
--------------------------------------------------------------------------------
/extension/icons/bm3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/bm3.png
--------------------------------------------------------------------------------
/extension/icons/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/book.png
--------------------------------------------------------------------------------
/extension/icons/clock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/clock.png
--------------------------------------------------------------------------------
/extension/icons/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/copy.png
--------------------------------------------------------------------------------
/extension/icons/data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/data.png
--------------------------------------------------------------------------------
/extension/icons/debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/debug.png
--------------------------------------------------------------------------------
/extension/icons/del-bm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/del-bm.png
--------------------------------------------------------------------------------
/extension/icons/dna.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/dna.png
--------------------------------------------------------------------------------
/extension/icons/donate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/donate.png
--------------------------------------------------------------------------------
/extension/icons/double-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/double-arrow.png
--------------------------------------------------------------------------------
/extension/icons/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/down.png
--------------------------------------------------------------------------------
/extension/icons/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/download.png
--------------------------------------------------------------------------------
/extension/icons/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/edit.png
--------------------------------------------------------------------------------
/extension/icons/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/email.png
--------------------------------------------------------------------------------
/extension/icons/export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/export.png
--------------------------------------------------------------------------------
/extension/icons/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/folder.png
--------------------------------------------------------------------------------
/extension/icons/forklift.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/forklift.png
--------------------------------------------------------------------------------
/extension/icons/gear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/gear.png
--------------------------------------------------------------------------------
/extension/icons/ghost.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/ghost.png
--------------------------------------------------------------------------------
/extension/icons/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/github.png
--------------------------------------------------------------------------------
/extension/icons/highlight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/highlight.png
--------------------------------------------------------------------------------
/extension/icons/m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/m.png
--------------------------------------------------------------------------------
/extension/icons/menu-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/menu-blue.png
--------------------------------------------------------------------------------
/extension/icons/note.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/note.png
--------------------------------------------------------------------------------
/extension/icons/note2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/note2.png
--------------------------------------------------------------------------------
/extension/icons/note4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/note4.png
--------------------------------------------------------------------------------
/extension/icons/off16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/off16.png
--------------------------------------------------------------------------------
/extension/icons/off18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/off18.png
--------------------------------------------------------------------------------
/extension/icons/on16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/on16.png
--------------------------------------------------------------------------------
/extension/icons/on18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/on18.png
--------------------------------------------------------------------------------
/extension/icons/on32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/on32.png
--------------------------------------------------------------------------------
/extension/icons/on36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/on36.png
--------------------------------------------------------------------------------
/extension/icons/on64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/on64.png
--------------------------------------------------------------------------------
/extension/icons/pageaction16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/pageaction16.png
--------------------------------------------------------------------------------
/extension/icons/pageaction32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/pageaction32.png
--------------------------------------------------------------------------------
/extension/icons/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/pause.png
--------------------------------------------------------------------------------
/extension/icons/redo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/redo.png
--------------------------------------------------------------------------------
/extension/icons/retry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/retry.png
--------------------------------------------------------------------------------
/extension/icons/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/save.png
--------------------------------------------------------------------------------
/extension/icons/save2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/save2.png
--------------------------------------------------------------------------------
/extension/icons/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/search.png
--------------------------------------------------------------------------------
/extension/icons/sel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/sel.jpg
--------------------------------------------------------------------------------
/extension/icons/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/start.png
--------------------------------------------------------------------------------
/extension/icons/submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/submit.png
--------------------------------------------------------------------------------
/extension/icons/sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/sync.png
--------------------------------------------------------------------------------
/extension/icons/sync2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/sync2.png
--------------------------------------------------------------------------------
/extension/icons/tm48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/tm48.png
--------------------------------------------------------------------------------
/extension/icons/to-bm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/to-bm.png
--------------------------------------------------------------------------------
/extension/icons/toggle-notes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/toggle-notes.png
--------------------------------------------------------------------------------
/extension/icons/tools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/tools.png
--------------------------------------------------------------------------------
/extension/icons/undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/undo.png
--------------------------------------------------------------------------------
/extension/icons/up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/up.png
--------------------------------------------------------------------------------
/extension/icons/view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/view.png
--------------------------------------------------------------------------------
/extension/icons/view2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/icons/view2.png
--------------------------------------------------------------------------------
/extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Textmarker",
4 | "version": "5.3.3",
5 |
6 | "browser_specific_settings": {
7 | "gecko": {
8 | "id": "textMarker@underFlyingBirches.org",
9 | "strict_min_version": "57.0"
10 | }
11 | },
12 |
13 | "default_locale": "en",
14 |
15 | "background": {
16 | "scripts": ["background/background.wp.js"]
17 | },
18 |
19 | "permissions": [
20 | "",
21 | "storage",
22 | "activeTab",
23 | "tabs",
24 | "notifications",
25 | "menus",
26 | "clipboardWrite",
27 | "webNavigation"
28 | ],
29 |
30 | "optional_permissions": [
31 | "webNavigation"
32 | ],
33 |
34 | "icons": {
35 | "16": "icons/on16.png",
36 | "32": "icons/on32.png"
37 | },
38 |
39 | "browser_action": {
40 | "default_title": "Textmarker",
41 | "default_icon": {
42 | "16": "icons/on16.png",
43 | "18": "icons/on18.png",
44 | "32": "icons/on32.png"
45 | },
46 | "default_popup": "content/tbb-menu/tbb-menu.html",
47 | "browser_style": true
48 | },
49 |
50 | "page_action": {
51 | "browser_style": true,
52 | "default_icon": {
53 | "16": "icons/pageaction16.png",
54 | "32": "icons/pageaction32.png"
55 | }
56 | },
57 |
58 | "sidebar_action": {
59 | "default_title": "Textmarker",
60 | "default_panel": "content/sidebar/sidebar.html",
61 | "default_icon": {
62 | "16": "icons/on16.png",
63 | "18": "icons/on18.png",
64 | "32": "icons/on32.png"
65 | }
66 | },
67 |
68 | "options_ui": {
69 | "page": "content/options-ui/options.html",
70 | "browser_style": true
71 | },
72 |
73 | "commands": {
74 | "_execute_sidebar_action": {
75 | "suggested_key": {
76 | "default": "Ctrl+Alt+T",
77 | "mac": "MacCtrl+Shift+T"
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/extension/web-ext-artifacts/textmarker-5.3.3.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/extension/web-ext-artifacts/textmarker-5.3.3.zip
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "textmarker",
3 | "author": "underflyingbirches",
4 | "description": "webextensions-addon-textmarker",
5 | "main": "src/main.js",
6 | "devDependencies": {
7 | "@babel/core": "^7.20.5",
8 | "@babel/preset-env": "^7.20.2",
9 | "babel-loader": "^9.1.0",
10 | "cross-env": "^5.2.1",
11 | "css-loader": "^6.7.3",
12 | "file-loader": "^6.2.0",
13 | "mini-css-extract-plugin": "^2.7.2",
14 | "sass": "^1.56.2",
15 | "sass-loader": "^13.2.0",
16 | "style-loader": "^3.3.1",
17 | "web-ext": "^7.4.0",
18 | "webpack": "^5.75.0",
19 | "webpack-cli": "^5.0.0"
20 | },
21 | "scripts": {
22 | "__webpack": "webpack --progress --config=webpack.config.js --hide-modules --display=minimal",
23 | "dev": "cross-env NODE_ENV=DEV npm run __webpack",
24 | "prod": "cross-env NODE_ENV=PROD npm run __webpack"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/background/modules/context-menu.js:
--------------------------------------------------------------------------------
1 | import _STORAGE from './../storage'
2 | import { _MODULE } from './../utils'
3 |
4 | export default function() {
5 | return new _MODULE({
6 | events: {
7 | ENV: {
8 | 'toggled:addon': 'toggle',
9 | 'updated:ctm-settings': 'update'
10 | }
11 | },
12 |
13 | items: {
14 | m: { contexts: ['selection'] },
15 | w: { contexts: ['selection'] },
16 | d: { contexts: ['frame', 'link', 'page'] },
17 | b: { contexts: ['frame', 'link', 'page'] },
18 | '-b': { contexts: ['frame', 'link', 'page'] },
19 | n: { contexts: ['frame', 'link', 'page'] },
20 | sb: { contexts: ['all', 'tab'] },
21 | c: { contexts: ['frame', 'link', 'page'] }
22 | },
23 | created: [],
24 |
25 | autoinit() {
26 | let item;
27 | for (let i in this.items) {
28 | item = this.items[i];
29 | item.id = i;
30 | item.title = browser.i18n.getMessage('ctx_' + (i === '-b' ? 'db' : i));
31 | item.onclick = (infos, tab) => this.onClick(infos, tab);
32 | }
33 | this.update();
34 | },
35 |
36 | create(id) {
37 | if (!this.created.includes(id)) {
38 | browser.menus.create(this.items[id]);
39 | this.created.push(id);
40 | }
41 | },
42 | remove(id) {
43 | if (this.created.includes(id)) {
44 | browser.menus.remove(id);
45 | this.created.splice(this.created.indexOf(id), 1);
46 | }
47 | },
48 | removeAll() {
49 | if (this.created.length) {
50 | browser.menus.removeAll();
51 | this.created = [];
52 | }
53 | },
54 | toggle(on) {
55 | if (on) this.update();
56 | else this.removeAll();
57 | },
58 | update() {
59 | const created = this.created;
60 | _STORAGE.get('shortcuts').then(shortcuts => {
61 | for (let i in this.items) {
62 | if (shortcuts[i][2]) this.create(i);
63 | else this.remove(i);
64 | }
65 | });
66 | },
67 | onClick(infos, tab) {
68 | const id = infos.menuItemId;
69 |
70 | if (id === 'w') this.emit('lookup:word', infos.selectionText);
71 | else if (id === 'sb') browser.sidebarAction.open();
72 | else if (id === 'c') {
73 | browser.permissions.contains({ permissions: ['clipboardWrite'] }).then(granted => {
74 | this.emit('ctx:' + id, granted, null, { tab: tab.id });
75 | });
76 | }
77 | else this.emit('ctx:' + id, null, null, { tab: tab.id });
78 | }
79 | });
80 | }
81 |
--------------------------------------------------------------------------------
/src/background/modules/error-logging.js:
--------------------------------------------------------------------------------
1 | import _STORAGE from './../storage'
2 | import { _MODULE } from './../utils'
3 | import _LOG_KEYS from './../../data/log-keys'
4 |
5 | new _MODULE({
6 | events: {
7 | ENV: {
8 | 'error': 'log',
9 | 'warning': 'log',
10 | 'failed:save-entry': 'log',
11 | 'failed:update-entry': 'log',
12 | 'failed:delete-entry': 'log',
13 | 'failed:restoration': 'onFailedRestoration',
14 | 'warn:mixed-entry-types': 'onMixedEntryTypes',
15 | 'warn:multiple-unlocked-entries': 'onMultipleUnlockedEntries',
16 | 'failed:pbm': 'onFailedPBM',
17 | 'failed:open-tab': 'onOpenTabFailure',
18 | 'error:import': 'log',
19 | 'error:browser-console': 'log',
20 | 'clear:logs': 'clear',
21 | 'failed:restore-range': 'log',
22 | 'failed:inject-content-script': 'onScriptInjectionFailure',
23 | 'failed:inject-stylesheet': 'onCSSInjectionFailure',
24 | 'missing-permission:webNavigation': 'onMissingWebNavigationPermission'
25 | }
26 | },
27 |
28 | log(error, info) {
29 | let log, msg;
30 | if (error.time) {
31 | log = [error.time, error.message + ' [' + error.location + ']'];
32 | } else {
33 | log = [(new Date().getTime()), _LOG_KEYS[error] || error];
34 | if (info) {
35 | if (info.report && typeof info.report === 'string') {
36 | log.push(info.report);
37 | if (info.attempt) log.push(info.attempt);
38 | if (info.url) log.push(info.url);
39 | }
40 | else if (typeof info === 'string') {
41 | log.push(info);
42 | }
43 | }
44 | }
45 | _STORAGE.set('log', log).then(() => this.emit('updated:logs logged:error', log));
46 | },
47 | clear() {
48 | _STORAGE.set('log', { clear: true }).then(() => this.emit('updated:logs'));
49 | },
50 | onMixedEntryTypes() {
51 | this.log('note_restoration_warning_1');
52 | },
53 | onMultipleUnlockedEntries() {
54 | this.log('note_restoration_warning_2');
55 | },
56 | onFailedRestoration(info) {
57 | this.log('note_restoration_failure', info);
58 | },
59 | onOpenTabFailure() {
60 | this.log('note_url');
61 | },
62 | onFailedPBM() {
63 | this.log('note_pbm');
64 | },
65 | onScriptInjectionFailure(err) {
66 | this.log('js_injection_failure', err);
67 | },
68 | onCSSInjectionFailure() {
69 | this.log('css_injection_failure');
70 | },
71 | onMissingWebNavigationPermission() {
72 | this.log('missing_permission_wn');
73 | },
74 | });
75 |
--------------------------------------------------------------------------------
/src/background/modules/namer.js:
--------------------------------------------------------------------------------
1 | import _STORAGE from './../storage'
2 | import { _MODULE } from './../utils'
3 | import _GLOBAL_SETTINGS from './../../data/global-settings'
4 |
5 | export default function() {
6 | return new _MODULE({
7 | events: {
8 | ENV: {
9 | 'granted:save-entry': 'name',
10 | 'rename:entry': 'rename'
11 | }
12 | },
13 |
14 | name(entry) {
15 | if (entry.name) return this.adjustName(entry.name, entry);
16 |
17 | _STORAGE.get('naming').then(naming => this.adjustName(null, entry, naming))
18 | .catch(() => this.emit('error', 'error_naming'));
19 | },
20 | rename(oldName, newName, area) {
21 | newName = newName.substring(0, _GLOBAL_SETTINGS.MAX_ENTRY_NAME_CHARS - 1);
22 |
23 | _STORAGE.get('history').then(history => {
24 | let counter = this.getDoubleNameCount(history, newName);
25 | if (counter) newName += ' (' + (counter + 1) + ')';
26 | this.emit('renamed:entry', oldName, newName, area);
27 | })
28 | .catch(() => this.emit('error', 'error_naming'));
29 | },
30 | adjustName(name, entry, method) {
31 | name = name ? name :
32 | method === 'title' ? entry.title.trim() ? entry.title.trim() : entry.url :
33 | method === 'date' ? (new Date(entry.first).toLocaleString()) : '';
34 |
35 | name = name.substring(0, _GLOBAL_SETTINGS.MAX_ENTRY_NAME_CHARS - 1);
36 |
37 | _STORAGE.get('history').then(history => {
38 | let counter = this.getDoubleNameCount(history, name);
39 |
40 | if (counter && entry.locked) {
41 | this.emit('failed:save-entry-double-locked', 'error_double_locked_name', name);
42 | } else {
43 | if (counter) name += ' (' + (counter + 1) + ')';
44 | entry.name = name;
45 | this.emit('named:entry', entry);
46 | }
47 | })
48 | .catch(() => this.emit('error', 'error_naming'));
49 | },
50 | getDoubleNameCount(history, name) {
51 | let existingNames = Object.keys(history.entries),
52 | l = existingNames.length,
53 | counter = 0,
54 | checkpoint;
55 |
56 | while (l--) {
57 | checkpoint = existingNames[l];
58 | if (this.isDoubleName(name, checkpoint)) counter++;
59 | }
60 | return counter;
61 | },
62 | isDoubleName(name, checkpoint) {
63 | if (name === checkpoint) return true;
64 |
65 | let l = name.length,
66 | checkpoint_start = checkpoint.substring(0, l),
67 | checkpoint_end;
68 |
69 | if (name !== checkpoint_start) return false;
70 |
71 | checkpoint_end = checkpoint.substring(l, checkpoint.length);
72 |
73 | if (/^\s*\(\d+\)$/.test(checkpoint_end)) return true;
74 |
75 | return false;
76 | }
77 | });
78 | }
79 |
--------------------------------------------------------------------------------
/src/background/modules/page-action.js:
--------------------------------------------------------------------------------
1 | import _STORAGE from './../storage'
2 | import { _MODULE } from './../utils'
3 |
4 | export default function() {
5 | return new _MODULE({
6 | events: {
7 | ENV: {
8 | 'dom:loaded': 'show',
9 | 'updated:autocs-settings': 'update'
10 | }
11 | },
12 | active: false,
13 |
14 | autoinit() {
15 | this.update();
16 | browser.pageAction.onClicked.addListener(tab => {
17 | this.emit('clicked:page-action', tab.id, tab.url);
18 | browser.pageAction.hide(tab.id);
19 | });
20 | },
21 |
22 | show(infos) {
23 | if (this.active) {
24 | browser.pageAction.show(infos.tabId);
25 | }
26 | },
27 |
28 | update() {
29 | _STORAGE.get('settings').then(settings => {
30 | this.active = !settings || settings.addon.autocs ? false : true;
31 | });
32 | }
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/src/background/modules/sidebars.js:
--------------------------------------------------------------------------------
1 | import { _MODULE, _GET_ACTIVE_TAB, _HASHLESS } from './../utils'
2 |
3 | export default function() {
4 | return new _MODULE({
5 | events: {
6 | ENV: {
7 | 'activated:tab': 'setPanel',
8 | 'changed:url': 'setPanel',
9 | 'entry:found': 'storeEntry',
10 | 'saved:entry': 'storeEntry',
11 | 'updated:entry': 'updateEntry',
12 | 'deleted:entry': 'removeEntry',
13 | 'opened:sidebar':'sendEntry',
14 | 'visually-ordered:marks': 'sendOrderedMarks'
15 | }
16 | },
17 |
18 | entries: {},
19 |
20 | setPanel(tabId, tabInfos) {
21 | this.isOpen().then(open => {
22 | if (open) {
23 | const url = tabInfos.url || 'blank';
24 | browser.sidebarAction.setPanel({
25 | panel: browser.runtime.getURL(`content/sidebar/sidebar.html#${tabId}_${url}`),
26 | tabId
27 | });
28 | }
29 | });
30 | },
31 | isOpen() {
32 | return browser.sidebarAction.isOpen({});
33 | },
34 | storeEntry(entry) {
35 | const ignoreHash = Array.isArray(entry) ? !entry[0].hashSensitive : !entry.hashSensitive;
36 | const entries = this.entries;
37 |
38 | _GET_ACTIVE_TAB().then(tab => {
39 | const id = tab.id;
40 | const url = ignoreHash ? _HASHLESS(tab.url) : tab.url;
41 |
42 | entries[id] = entries[id] || [];
43 | entries[id][url] = entry;
44 | });
45 | },
46 | updateEntry(entry) {
47 | const ignoreHash = !entry.hashSensitive;
48 | const entries = this.entries;
49 | const entryUrl = ignoreHash ? _HASHLESS(entry.url) : entry.url;
50 |
51 | for (let id in entries) {
52 | for (let url in entries[id]) {
53 | if (url === entryUrl) {
54 | entries[id][url] = entry;
55 | }
56 | }
57 | }
58 | _GET_ACTIVE_TAB().then(tab => {
59 | const tabUrl = ignoreHash ? _HASHLESS(tab.url) : tab.url;
60 |
61 | if (tabUrl === entryUrl) {
62 | this.emit('entry:found-for-tab', entry);
63 | }
64 | });
65 | },
66 | removeEntry(name, url, hashSensitive) {
67 | const entries = this.entries;
68 | const entryUrl = hashSensitive ? url : _HASHLESS(url);
69 |
70 | for (let id in entries) {
71 | for (let savedUrl in entries[id]) {
72 | if (savedUrl === entryUrl) {
73 | delete entries[id][savedUrl];
74 | }
75 | }
76 | }
77 | _GET_ACTIVE_TAB().then(tab => {
78 | const tabUrl = hashSensitive ? tab.url : _HASHLESS(tab.url);
79 | if (tabUrl === entryUrl) {
80 | this.emit('entry:deleted-for-tab');
81 | }
82 | });
83 | },
84 | sendEntry() {
85 | _GET_ACTIVE_TAB().then(tab => {
86 | const hashlessUrl = _HASHLESS(tab.url);
87 | const entriesForThisTab = this.entries[tab.id];
88 | let entry = null;
89 | if (entriesForThisTab) {
90 | entry = entriesForThisTab[tab.url] || entriesForThisTab[hashlessUrl];
91 | }
92 | this.emit('entry:found-for-tab', entry);
93 | });
94 | },
95 | sendOrderedMarks(marks) {
96 | this.emit('entry:ordered-marks', marks);
97 | }
98 | });
99 | }
100 |
--------------------------------------------------------------------------------
/src/background/modules/tabs.js:
--------------------------------------------------------------------------------
1 | import _STORAGE from './../storage'
2 | import { _MODULE } from './../utils'
3 |
4 | export default function() {
5 | return new _MODULE({
6 | events: {
7 | ENV: {
8 | 'toggled:addon': 'toggleTabEventHandlers',
9 | 'started:app': 'openInitPage',
10 | 'open:addon-page(sb)': 'openAddonPage',
11 | 'open:addon-page(tbb)': 'openAddonPage',
12 | 'open:addon-page(am)': 'openAddonPage',
13 | 'lookup:word': 'openSearch',
14 | 'open:entries': 'open'
15 | }
16 | },
17 | urls: {
18 | news: 'content/addon-page/addon-page.html#page=news',
19 | settings: 'content/addon-page/addon-page.html#page=settings',
20 | history: 'content/addon-page/addon-page.html#page=history',
21 | info: 'content/addon-page/addon-page.html#page=new',
22 | help: 'content/addon-page/addon-page.html#page=manual',
23 | contact: 'content/addon-page/addon-page.html#page=contact',
24 | troubleshooting: 'content/addon-page/addon-page.html#page=troubleshooting',
25 | logs: 'content/addon-page/addon-page.html#page=logs',
26 | export: 'content/addon-page/addon-page.html#page=export',
27 | sync: 'content/addon-page/addon-page.html#page=sync'
28 | },
29 | tabEventHandlers: {
30 | onActivated: null,
31 | onUpdated: null
32 | },
33 |
34 | autoinit() {
35 | _STORAGE.get('mode').then(mode => this.toggleTabEventHandlers(mode));
36 | },
37 |
38 | addTabEventHandlers() {
39 | if (!this.tabEventHandlers.onActivated) {
40 | const onActivated = this.tabEventHandlers.onActivated = this.onActivated.bind(this);
41 | browser.tabs.onActivated.addListener(onActivated);
42 | }
43 |
44 | if (!this.tabEventHandlers.onUpdated) {
45 | const onUpdated = this.tabEventHandlers.onUpdated = this.onUpdated.bind(this);
46 | browser.tabs.onUpdated.addListener(onUpdated/*, { properties: ['status'] }*/); // ESR throws wrong argument type error when using filters
47 | }
48 | },
49 | removeTabEventHandlers() {
50 | ['onActivated', 'onUpdated'].forEach(ev => {
51 | if (this.tabEventHandlers[ev]) {
52 | browser.tabs[ev].removeListener(this.tabEventHandlers[ev]);
53 | }
54 | });
55 | this.tabEventHandlers = {};
56 | },
57 | toggleTabEventHandlers(on) {
58 | if (on) this.addTabEventHandlers();
59 | else this.removeTabEventHandlers();
60 | },
61 | onActivated(tab) {
62 | this.emit('activated:tab', tab.tabId, (tab.url || ''));
63 | },
64 | onUpdated(tabId, changed, tab) {
65 | if (changed.url) {
66 | this.emit('changed:url', tabId, changed, tab);
67 | }
68 | },
69 |
70 | open(urls, names) {
71 | urls = typeof urls === 'string' ? [urls] : urls;
72 | names = typeof names === 'string' ? [names] : names;
73 | let l = urls.length,
74 | securityWarning = false,
75 | url;
76 | while (l--) {
77 | (function(self, l) {
78 | url = urls[l];
79 | browser.tabs.create({ url: urls[l] })
80 | .catch(() => {
81 | if (!securityWarning) self.emit('failed:open-tab');
82 | securityWarning = true;
83 | })
84 | .then(() => {
85 | if (names) self.emit('opened:entry', { url: url, name: names[l] });
86 | });
87 | })(this, l);
88 | }
89 | },
90 | openAddonPage(id) {
91 | this.open(this.urls[id]);
92 | },
93 | openInitPage(version, loadReason) {
94 | if (loadReason) {
95 | if (loadReason === 'install' || loadReason === 'update') this.openAddonPage('help');
96 | //else if (loadReason === 'update') this.openAddonPage('news');
97 | }
98 | },
99 | openSearch(word) {
100 | _STORAGE.get('settings').then(settings => {
101 | let custom = settings.misc.customSearch, url;
102 | if (custom) url = 'https://' + custom[0] + word + custom[1];
103 | else url = 'https://' + browser.i18n.getMessage('lng') + '.wikipedia.org/wiki/' + word;
104 | this.open(url);
105 | });
106 | }
107 | });
108 | }
109 |
--------------------------------------------------------------------------------
/src/background/modules/web-navigation.js:
--------------------------------------------------------------------------------
1 | import { _MODULE } from './../utils'
2 |
3 | export default function() {
4 | return new _MODULE({
5 | events: {
6 | ENV: {
7 | 'granted-permission:webNavigation': 'addListener'
8 | }
9 | },
10 | autoinit() {
11 | const permission = { permissions: ['webNavigation'] };
12 |
13 | browser.permissions.contains(permission).then(granted => {
14 | if (granted) {
15 | this.addListener();
16 | } else {
17 | this.emit('missing-permission:webNavigation');
18 | }
19 | });
20 | },
21 | addListener() {
22 | browser.webNavigation.onDOMContentLoaded.addListener(infos => this.emit('dom:loaded', infos));
23 | }
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/src/background/modules/windows.js:
--------------------------------------------------------------------------------
1 | import _STORAGE from './../storage'
2 | import { _MODULE } from './../utils'
3 |
4 | export default function() {
5 | return new _MODULE({
6 | events: {
7 | ENV: {
8 | 'view:entry': 'openEntryDetailPage'
9 | }
10 | },
11 |
12 | openEntryDetailPage(name) {
13 | const popupURL = browser.runtime.getURL('content/detail-view/detail-view.html');
14 |
15 | browser.windows.getCurrent().then(currentWindow => {
16 |
17 | browser.windows.create({
18 | url: popupURL + '#' + encodeURIComponent(name),
19 | type: 'panel',
20 | height: currentWindow.height - 22,
21 | width: Math.min(currentWindow.width, 980),
22 | incognito: currentWindow.incognito
23 | });
24 | });
25 | }
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/src/background/port.js:
--------------------------------------------------------------------------------
1 | import { _BGPORT } from './utils'
2 |
3 | new _BGPORT({
4 | name: 'background',
5 | type: 'background',
6 | postponeConnection: true,
7 | events: {
8 | ONEOFF: [
9 | 'started:app',
10 | 'toggled:addon',
11 | 'toggled:sync',
12 | 'updated:settings',
13 | 'updated:history',
14 | 'updated:history-on-restoration',
15 | 'updated:entry-sync',
16 | 'updated:entry-name',
17 | 'updated:logs',
18 | 'updated:ctm-settings',
19 | 'updated:misc-settings',
20 | 'updated:naming-settings',
21 | 'updated:bg-color-settings',
22 | 'updated:custom-search-settings',
23 | 'updated:saveopt-settings',
24 | 'updated:mark-method-settings',
25 | 'updated:marker-settings',
26 | 'entries:found',
27 | 'saved:entry',
28 | 'deleted:entry',
29 | 'deleted:entries',
30 | 'imported:settings',
31 | 'imported:history',
32 | 'ctx:m',
33 | 'ctx:d',
34 | 'ctx:b',
35 | 'ctx:-b',
36 | 'ctx:n',
37 | 'ctx:c',
38 | 'sidebar:highlight',
39 | 'sidebar:delete-highlight',
40 | 'sidebar:bookmark',
41 | 'sidebar:delete-bookmark',
42 | 'sidebar:note',
43 | 'sidebar:immut',
44 | 'sidebar:save-changes',
45 | 'sidebar:undo',
46 | 'sidebar:redo',
47 | 'sidebar:copy',
48 | 'sidebar:scroll-to-bookmark',
49 | 'sidebar:toggle-notes',
50 | 'sidebar:next-mark',
51 | 'sidebar:retry-restoration',
52 | 'sidebar:selected-marker',
53 | 'opened:sidebar',
54 | 'changed:url',
55 | 'injected?'
56 | ],
57 | CONNECTION: [
58 | 'started:app',
59 | 'toggled:addon',
60 | 'toggled:sync',
61 | 'updated:settings',
62 | 'updated:entry-on-save',
63 | 'saved:entry',
64 | 'updated:pagenotes',
65 | 'changed:selection',
66 | 'unsaved-changes',
67 | 'clicked:mark',
68 | 'added:bookmark',
69 | 'removed:bookmark',
70 | 'added:note',
71 | 'removed:last-note',
72 | 'page-state',
73 | 'notes-state',
74 | 'entry:found',
75 | 'entry:found-for-tab',
76 | 'entry:deleted-for-tab',
77 | 'entry:ordered-marks'
78 | ]
79 | }
80 | });
81 |
--------------------------------------------------------------------------------
/src/background/utils.js:
--------------------------------------------------------------------------------
1 | import { _COPY } from './../utils/copy'
2 | import _EXTEND from './../utils/extend'
3 | import { _GET_ACTIVE_TAB } from './../utils/getActiveTab'
4 | import { _MODULE } from './../utils/module'
5 | import { _STORE } from './../utils/store'
6 | import { _BGPORT } from './../utils/port'
7 | import { _HASHLESS } from './../utils/hashless'
8 | import _ERRORTRACKER from './../utils/error-tracker'
9 |
10 | export { _COPY, _EXTEND, _GET_ACTIVE_TAB, _MODULE, _STORE, _BGPORT, _ERRORTRACKER, _HASHLESS }
11 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_buttons.scss:
--------------------------------------------------------------------------------
1 | %button {
2 | border: 1px solid $color-border;
3 | background-image: $gradient--button;
4 | background-repeat: no-repeat;
5 | background-color: transparent;
6 | color: $color-text--grey;
7 | cursor: pointer;
8 | font-size: $font-size--s3;
9 | font-family: inherit;
10 | padding: 3px 7px 5px;
11 | min-height: 22px;
12 | transition: background 0.5s;
13 |
14 | &:hover {
15 | background-image: $gradient--button-hover;
16 | color: $color-text--dark;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_forms.scss:
--------------------------------------------------------------------------------
1 | ::-moz-placeholder,
2 | :placeholder-shown {
3 | color: $color-text--disabled;
4 | }
5 | select, input {
6 | font-family: inherit !important;
7 | }
8 | label {
9 | width: 224px;
10 | display: inline-block;
11 | }
12 | label span {
13 | display: inline-block;
14 | line-height: 1.2;
15 | height: 22px;
16 | width: 22px;
17 | text-align: center;
18 | color: transparent;
19 | background-color: transparent;
20 | text-indent: -1000px;
21 | overflow: hidden;
22 | border: 1px solid $color-border;
23 | border-radius: 2px;
24 | vertical-align: -5px;
25 | margin-right: 8px;
26 |
27 | &:active {
28 | background-color: $color-bg--turquoise;
29 | color: $color-text--light !important;
30 | }
31 | }
32 | select {
33 | @include icon('arrow-down', right 7px);
34 | background-color: transparent;
35 | -moz-appearance: none;
36 | border: 1px solid $color-border;
37 | height: 22px;
38 | min-width: 34px;
39 | padding-right: 20px;
40 | }
41 | select.small {
42 | width: 68px;
43 | }
44 | select.big {
45 | width: 140px;
46 | }
47 | .cb-sub-select {
48 | margin: 10px 0 0 35px;
49 | }
50 | input:disabled {
51 | opacity: 0.25;
52 | }
53 | input[type="search"] {
54 | float: right;
55 | border: 1px solid $color-border--light;
56 | color: inherit;
57 | text-align: left;
58 | padding: 0 0 0 5px;
59 | height: 27px;
60 | }
61 | input[type="color"] {
62 | padding: 0;
63 | }
64 | input[type="color"] {
65 | vertical-align: middle;
66 | width: 36px;
67 | text-align: center;
68 | }
69 | input[type="text"] {
70 | border: 1px solid $color-border;
71 | color: inherit;
72 | padding: 0;
73 | height: 22px;
74 | width: 190px;
75 | padding: 0 4px;
76 |
77 | @include mq('mobile') {
78 | width: 126px;
79 | }
80 | }
81 | input[type="file"] {
82 | display: none;
83 | }
84 | /* custom inputs */
85 | input[type="checkbox"],
86 | input[type="radio"] {
87 | display: none;
88 | }
89 | input[type="radio"] + label span {
90 | border-radius: 50%;
91 | }
92 | label.fake-cb,
93 | label.fake-rb {
94 | width: auto;
95 | }
96 | label sup {
97 | font-size: inherit;
98 | vertical-align: 0;
99 | width: calc(100% - 40px);
100 | }
101 | label.fake-cb:not(.pull-right) sup,
102 | label.fake-rb:not(.pull-right) sup {
103 | display: inline-block;
104 | vertical-align: middle;
105 | }
106 | label.pull-right span {
107 | margin: 0 0 0 8px;
108 | }
109 | label.fake-cb + label {
110 | width: 188px;
111 | }
112 | input[type="checkbox"]:checked + label span,
113 | input[type="radio"]:checked + label span {
114 | text-indent: 0;
115 | color: $color-text--turquoise;
116 | }
117 | input[type="number"] {
118 | width: 45px;
119 | border: 1px solid $color-border;
120 | -moz-appearance: none;
121 | height: 22px;
122 | padding-left: 5px;
123 | -moz-user-select: none;
124 | }
125 |
126 | label span,
127 | input[type="number"],
128 | select {
129 | transition: color .3s ease-in;
130 |
131 | &:hover {
132 | border-color: $color-grey--medium;
133 | background-color: #fff;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_general.scss:
--------------------------------------------------------------------------------
1 | :focus {
2 | outline: none;
3 | }
4 | ::-moz-focus-inner {
5 | border: 0;
6 | }
7 | ::-moz-color-swatch {
8 | border: 0;
9 | }
10 | * {
11 | box-sizing: border-box;
12 | }
13 | body {
14 | font-family: Verdana, Arial, sans-serif;
15 | color: $color-text--dark;
16 | cursor: default;
17 | font-size: $font-size--base;
18 | }
19 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_icons.scss:
--------------------------------------------------------------------------------
1 | @mixin button_icon($name, $gradient: true, $important: '') {
2 | @if ($gradient) {
3 | background-image: url(../../../icons/#{$name}.png), $gradient--button;
4 | &:hover {
5 | background-image: url(../../../icons/#{$name}.png), $gradient--button-hover;
6 | }
7 | } @else {
8 | background-image: url(../../../icons/#{$name}.png) #{$important};
9 | }
10 | }
11 |
12 | @mixin icon($name: '', $position: 0 0, $size: unset, $repeat: no-repeat, $gradient: false, $important: '') {
13 | @if ($name) {
14 | @include button_icon($name, $gradient, $important);
15 | }
16 | background-repeat: $repeat;
17 | background-position: $position, 0 0;
18 | background-size: $size, 100%;
19 | }
20 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_infos.scss:
--------------------------------------------------------------------------------
1 | .i {
2 | cursor: pointer;
3 | color: $color-text--turquoise;
4 | text-decoration: underline;
5 | border-radius: 50%;
6 | display: inline-block;
7 | line-height: 1;
8 | width: 15px;
9 | height: 15px;
10 | text-align: center;
11 | font-size: 11px;
12 | padding: 2px;
13 | font-family: Georgia, serif;
14 | margin-top: 3px;
15 |
16 | &:hover {
17 | text-decoration: none;
18 | color: #fff;
19 | background: $color-bg--turquoise;
20 | border: 0;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_links.scss:
--------------------------------------------------------------------------------
1 | @mixin links($color: $color-text--turquoise) {
2 | a, .link {
3 | color: $color;
4 | text-decoration: none;
5 | cursor: pointer;
6 |
7 | &:hover {
8 | text-decoration: underline;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_notes.scss:
--------------------------------------------------------------------------------
1 | $colors:
2 | (green, #dfd, #efe, #cfc),
3 | (white, #eee, #fff, #eee),
4 | (yellow, #ffd, #ffe, #ffc),
5 | (orange, #fec, #fed, #feb),
6 | (red, #fdd, #fee, #fcc),
7 | (purple, #edf, #eef, #ecf),
8 | (blue, #cef, #def, #bef),
9 | (turquoise, #c1e9f2, #d5f0f7, #b9e4ec);
10 |
11 | @each $name, $val, $val2, $val3 in $colors {
12 | .tmnote--#{$name} {
13 | tmnotepalette,
14 | tmnoteheader {
15 | background: $val3;
16 | }
17 | tmnotecustomize,
18 | tmnoteminimize,
19 | tmnotedelete {
20 | background: $val2;
21 | &:hover {
22 | background: $val;
23 | }
24 | }
25 | textarea {
26 | background-image: linear-gradient($val2, $val) !important;
27 | }
28 | }
29 | .tmnotecolor--#{$name} {
30 | border-color: $val;
31 | background-image: linear-gradient($val2, $val);
32 | }
33 | }
34 |
35 | tmnote {
36 | font-family: Verdana, sans-serif;
37 | font-size: 12px;
38 | line-height: 1.4;
39 | }
40 | tmnoteheader {
41 | height: 22px;
42 | position: relative;
43 | z-index: 1;
44 | display: block;
45 | }
46 | tmnotepalette {
47 | z-index: 1;
48 | width: 100%;
49 | text-align: center;
50 | border-bottom: 1px solid $color-border--light;
51 | }
52 | tmnotecolor {
53 | position: relative;
54 | display: inline-block;
55 | border-top: 2px solid;
56 | cursor: pointer;
57 | box-shadow: 0 1px 3px $color-shadow--lighter;
58 | &:hover {
59 | top: -1px;
60 | box-shadow: 0 1px 3px $color-shadow--light;
61 | filter: brightness(1.02);
62 | }
63 | }
64 | tmnotecustomize,
65 | tmnoteminimize,
66 | tmnotedelete {
67 | font-size: 14px;
68 | cursor: pointer;
69 | color: $color-text--disabled;
70 | line-height: 1;
71 | height: 20px;
72 |
73 | &:hover {
74 | color: $color-text--dark;
75 | }
76 | }
77 | tmnotedelete {
78 | font-size: 10px;
79 | font-weight: bold;
80 | }
81 | tmnoteminimize {
82 | font-size: 18px;
83 | }
84 | tmnotecustomize {
85 | font-size: 16px;
86 | }
87 | .__note_textarea {
88 | display: block !important;
89 | position: relative !important;
90 | font-family: Verdana, sans-serif !important;
91 | font-size: 12px !important;
92 | min-height: 100px !important;
93 | border: 0 !important;
94 | }
95 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_reset.scss:
--------------------------------------------------------------------------------
1 | html, body, object, iframe,
2 | div, h1, h2, h3, h4, h5, h6, p,
3 | span, a, abbr, img,
4 | em, small, sub, sup,
5 | dl, dt, dd, ol, ul, li,
6 | fieldset, form, label,
7 | table, caption, tbody, thead, tr, th, td {
8 | margin: 0;
9 | padding: 0;
10 | border: 0;
11 | outline: 0;
12 | font-weight: inherit;
13 | font-style: inherit;
14 | font-family: inherit;
15 | line-height: normal;
16 | vertical-align: baseline;
17 | color: inherit;
18 | }
19 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_shadows.scss:
--------------------------------------------------------------------------------
1 | @mixin shadow-switch($color) {
2 | box-shadow: 0 0 2px $color inset;
3 | }
4 |
5 | %shadow-switch {
6 | @include shadow-switch($color-shadow--light);
7 | }
8 | %shadow-switch--hover {
9 | @include shadow-switch($color-shadow--dark);
10 | }
11 | %shadow-switch--active {
12 | @include shadow-switch($color-shadow--medium);
13 | }
14 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_switches.scss:
--------------------------------------------------------------------------------
1 | .switch {
2 | @extend %shadow-switch;
3 | position: relative;
4 | width: 44px;
5 | height: 22px;
6 | padding: 2px;
7 | cursor: pointer;
8 |
9 | &:not(.switch__indicator):not(.switch--immut) {
10 | &:before {
11 | content: 'O';
12 | position: absolute;
13 | font-weight: bold;
14 | top: 3px;
15 | font-size: 14px;
16 | left: 27px;
17 | color: $color-grey--lighter;
18 | font-family: Arial;
19 | }
20 |
21 | &.active {
22 | &:before {
23 | content: 'I';
24 | left: 8px;
25 | }
26 | }
27 | }
28 |
29 | &--sync {
30 | margin-right: 10px;
31 | }
32 | &--save {
33 | .autosave {
34 | display: none;
35 | }
36 | }
37 | &:hover {
38 | @extend %shadow-switch--active;
39 |
40 | &.active {
41 | @extend %shadow-switch--hover;
42 | }
43 | }
44 | &.active {
45 | @extend %shadow-switch--active;
46 | }
47 | span {
48 | padding: 0 4px;
49 | }
50 | }
51 |
52 | .switch__indicator {
53 | width: 18px;
54 | height: 18px;
55 | background-color: $color-grey--light;
56 | display: inline-block;
57 | margin-left: 0;
58 | text-align: center;
59 | transition: margin 0.1s;
60 |
61 | .switch--sync & {
62 | @include icon('sync', center, 67%);
63 | }
64 | .switch--save & {
65 | @include icon('save2', center, 67%);
66 | }
67 | .switch--immut & {
68 | &:before {
69 | content: '\221E';
70 | display: block;
71 | font-size: 16px;
72 | position: relative;
73 | top: -5px;
74 | color: #fff;
75 | }
76 | }
77 | .switch.active & {
78 | background-color: transparentize($color-bg--turquoise, .25);
79 | margin-left: 22px;
80 | }
81 | .switch.active:hover & {
82 | background-color: $color-bg--turquoise;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/content/_shared/sass/_vars.scss:
--------------------------------------------------------------------------------
1 | // colors
2 | $color-turquoise: #2ba5b7;
3 | $color-grey--darker: #37414b;
4 | $color-grey--dark-ff: #38383d;
5 | $color-grey--dark: #555;
6 | $color-grey--medium: #777;
7 | $color-grey--medium-light: #aaa;
8 | $color-grey--light: #c8c8c8;
9 | $color-grey--lighter: #e1e2e3;
10 | $color-red: #ad3a3a;
11 | $color-green: #3aad74;
12 | $color-black: #000;
13 | $color-white: #fbfbfb;
14 |
15 | $color-bg--dark: $color-grey--darker;
16 | $color-bg--dark-ff: $color-grey--dark-ff;
17 | $color-bg--light: $color-white;
18 | $color-bg--turquoise: $color-turquoise;
19 |
20 | $color-border: $color-grey--light;
21 | $color-border--light: $color-grey--lighter;
22 | $color-border--dark: $color-grey--darker;
23 |
24 | $color-shadow--darker: $color-grey--darker;
25 | $color-shadow--dark: $color-grey--dark;
26 | $color-shadow--medium: $color-grey--medium;
27 | $color-shadow--light: $color-grey--medium-light;
28 | $color-shadow--lighter: $color-grey--light;
29 |
30 | $color-text--dark: $color-grey--darker;
31 | $color-text--light: $color-white;
32 | $color-text--light-grey: $color-grey--lighter;
33 | $color-text--grey: $color-grey--medium;
34 | $color-text--turquoise: $color-turquoise;
35 | $color-text--red: $color-red;
36 | $color-text--green: $color-green;
37 | $color-text--disabled: $color-grey--light;
38 |
39 | // media queries
40 | $mq-desk--small: 940px;
41 | $mq-mobile: 768px;
42 |
43 | // font-sizes
44 | $font-size--base: 15px;
45 | $font-size--base-small: 13px;
46 |
47 | $font-size--s5: 0.6667rem; // 10/15px
48 | $font-size--s4: 0.733rem; // 11/15px
49 | $font-size--s3: 0.8rem; // 12/15px
50 | $font-size--s2: 0.8667rem; // 13/15px
51 | $font-size--s1: 0.9333rem; // 14/15px
52 | $font-size--default: 1rem;
53 | $font-size--l1: 1.133rem; // 17/15px
54 | $font-size--l2: 1.333rem; // 20/15px
55 | $font-size--l3: 1.467rem; // 22/15px
56 |
57 | // spacing
58 | $spacing--tiny: 5px;
59 | $spacing--small: 10px;
60 | $spacing--medium: 15px;
61 | $spacing--large: 20px;
62 | $spacing--huge: 30px;
63 |
64 | // layers
65 | $layer--sub: -1;
66 | $layer--ground: 0;
67 | $layer--up: 1;
68 | $layer--upper: 2;
69 | $layer--sky: 1000;
70 | $layer--sun: 1001;
71 | $layer--top: 1002;
72 | $layer--max: 2147483646;
73 |
74 | // gradients
75 | $gradient--button-hover: linear-gradient(transparentize($color-bg--turquoise, .9) 50%, transparent 50%);
76 | $gradient--button: linear-gradient(transparent 50%, transparentize($color-bg--turquoise, .9) 50%);
77 |
--------------------------------------------------------------------------------
/src/content/_shared/utils.js:
--------------------------------------------------------------------------------
1 | import { _COPY } from './../../utils/copy'
2 | import { _GET_ACTIVE_TAB } from './../../utils/getActiveTab'
3 | import _EXTEND from './../../utils/extend'
4 | import { _MODULE } from './../../utils/module'
5 | import { _DOMMODULE } from './../../utils/dommodule'
6 | import { _STORE } from './../../utils/store'
7 | import { _PORT, _PRIVPORT } from './../../utils/port'
8 | import { _HASHLESS } from './../../utils/hashless'
9 | import _L10N from './../../utils/l10n'
10 | import _ERRORTRACKER from './../../utils/error-tracker'
11 |
12 | export { _COPY, _GET_ACTIVE_TAB, _EXTEND, _MODULE, _DOMMODULE, _STORE, _PORT, _PRIVPORT, _L10N, _ERRORTRACKER, _HASHLESS }
13 |
--------------------------------------------------------------------------------
/src/content/addon-page/_store.js:
--------------------------------------------------------------------------------
1 | import { _STORE } from './../_shared/utils'
2 |
3 | export default new _STORE({
4 | events: {
5 | ENV: {
6 | 'toggled:sync': 'onToggledSync'
7 | }
8 | },
9 | env: 'addon-page',
10 |
11 | _get_download_option() {
12 | return browser.storage[this.area_settings].get().then(storage => {
13 | if (!storage || !storage.settings) return 'text';
14 | return storage.settings.history.download;
15 | });
16 | },
17 | _get_markers() {
18 | return browser.storage[this.area_settings].get().then(storage => storage.settings.markers);
19 | },
20 | _get_shortcuts() {
21 | return browser.storage[this.area_settings].get().then(storage => storage.settings.shortcuts);
22 | }
23 | });
24 |
--------------------------------------------------------------------------------
/src/content/addon-page/bootstrap.js:
--------------------------------------------------------------------------------
1 | import _STORE from './_store'
2 | import { _MODULE } from './../_shared/utils'
3 |
4 | import _HISTORY from './modules/history'
5 | import _SETTINGS from './modules/settings'
6 | import _LOGGING from './modules/logs'
7 | import _SYNCING from './modules/syncing'
8 | import _IMPORT from './modules/import'
9 | import _CONTACT from './modules/contact'
10 | import _TROUBLESHOOTING from './modules/troubleshooting'
11 | import _PAGINATOR from './modules/history-pagination'
12 | import _PERMISSIONS from './modules/permissions'
13 |
14 | import _NAV from './modules/nav'
15 |
16 | new _MODULE({
17 | events: {
18 | ENV: {
19 | 'started:app': 'start',
20 | 'updated:settings': 'start',
21 | 'updated:history': 'start'
22 | }
23 | },
24 | allowedHashes: [/*'news', */'manual', 'settings', 'history', 'contact', 'sync', 'export', 'troubleshooting', 'logs'],
25 | bootstrapped: false,
26 | autoinit() {
27 | _STORE.get().then(storage => {
28 | if (storage.settings && storage.history) this.start();
29 | });
30 | },
31 | start() {
32 | if (!this.bootstrapped) {
33 | this.bootstrapped = true;
34 | _HISTORY();
35 | _SETTINGS();
36 | _LOGGING();
37 | _SYNCING();
38 | _IMPORT();
39 | _CONTACT();
40 | _TROUBLESHOOTING();
41 | _PAGINATOR();
42 | _PERMISSIONS();
43 | this.initMainNav();
44 | }
45 | },
46 | initMainNav() {
47 | const tab = window.location.hash.split('=')[1];
48 |
49 | if (this.allowedHashes.includes(tab)) {
50 | window.document.getElementById('mainnav-' + tab).classList.add('active');
51 | }
52 | new _NAV(window.document.getElementById('mainnav'));
53 | }
54 | });
55 |
--------------------------------------------------------------------------------
/src/content/addon-page/index.js:
--------------------------------------------------------------------------------
1 | import './sass/index.scss'
2 |
3 | import _ERRORTRACKER from './../_shared/utils'
4 | import { _L10N } from './../_shared/utils'
5 | import _NAV from './modules/nav'
6 | import _TOGGLER from './modules/toggler'
7 | import './port'
8 | import './_store'
9 | import './bootstrap'
10 |
11 | _L10N();
12 |
13 | /* auto-insert current version number */
14 | document.getElementById('version-number').innerText = browser.runtime.getManifest().version;
15 | /* end: auto-insert current version number */
16 |
17 | /* configure subnavs */
18 | const subnavs = document.getElementsByClassName('subnav');
19 | let n = subnavs.length;
20 |
21 | while(n--) new _NAV(subnavs[n]);
22 | /* end: configure navs */
23 |
24 | /* configure toggle elements */
25 | let toggleButtons = document.getElementsByClassName('toggle-button'),
26 | t = toggleButtons.length;
27 |
28 | while(t--) new _TOGGLER(toggleButtons[t]);
29 | /* end: configure toggle elements */
30 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/contact.js:
--------------------------------------------------------------------------------
1 | import { _MODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | export default function() {
5 | return new _MODULE({
6 | events: {
7 | ENV: {
8 | 'updated:logs': 'setLogLink'
9 | }
10 | },
11 | autoinit() {
12 | this.setLogLink();
13 | },
14 | setLogLink() {
15 | const logLink = document.getElementById('log-mail');
16 |
17 | _STORE.get('logs').then(logs => {
18 | let l = logs ? logs.length : 0,
19 | href =
20 | 'mailto:undflybir@gmx.de?subject=Textmarker' +
21 | encodeURIComponent(' : Error Logs') +
22 | '&body='+
23 | encodeURIComponent('- ' + browser.i18n.getMessage('your_msg') + ' -\n\n\nLOGS:\n\n'),
24 | log;
25 |
26 | while(l--) {
27 | log = logs[l];
28 | href += log[1] + (log[2] ? ' (' + log[2] + ')' : '') + ' - ' + encodeURIComponent((new Date(log[0]).toUTCString()) + '\n');
29 | }
30 | logLink.href = href;
31 | });
32 | }
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/history-pagination.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 | import _TOGGLER from './toggler'
4 |
5 | export default function() {
6 | return new _DOMMODULE({
7 | el: document.getElementById('paginator'),
8 | events: {
9 | ENV: {
10 | 'saved:entry': 'add',
11 | 'deleted:entries': 'updateFromStorage',
12 | 'filtered:history': 'reset',
13 | 'imported:history': 'updateFromStorage',
14 | 'changed:per-page-count': 'changeCountPerPage'
15 | },
16 | DOM: {
17 | click: {
18 | '.page': 'goto',
19 | '.prev': 'prev',
20 | '.next': 'next'
21 | }
22 | }
23 | },
24 | numberPages: 1,
25 | numberEntries: 0,
26 | currentPage: 1,
27 | perPage: 10,
28 |
29 | autoinit() {
30 | this.updateFromStorage();
31 | },
32 | goto(e, el) {
33 | const newPage = el.getAttribute('data-page') * 1;
34 | if (this.currentPage !== newPage) {
35 | this.emit('paginate:history', newPage);
36 | this.currentPage = newPage;
37 | this.render();
38 | }
39 | },
40 | prev() {
41 | if (this.currentPage === 1) return;
42 | this.emit('paginate:history', --this.currentPage);
43 | this.render();
44 | },
45 | next() {
46 | if (this.currentPage === this.numberPages) return;
47 | this.emit('paginate:history', ++this.currentPage);
48 | this.render();
49 | },
50 | add() {
51 | this.update(++this.numberEntries);
52 | },
53 | remove() {
54 | this.update(--this.numberEntries);
55 | },
56 | updateFromStorage() {
57 | _STORE.get('settings').then(settings => this.perPage = settings.history.pp || 10)
58 | .then(() => _STORE.get('history').then(history => this.update(Object.keys(history.entries).length)));
59 | },
60 | update(l, silent) {
61 | this.numberEntries = l;
62 | this.numberPages = l ? Math.ceil(l / this.perPage) : 1;
63 | if (this.currentPage > this.numberPages) {
64 | this.currentPage = this.numberPages;
65 | if (!silent) this.emit('paginate:history', this.currentPage);
66 | }
67 | this.render();
68 | },
69 | reset(l) {
70 | this.currentPage = 1;
71 | this.update(l);
72 | },
73 | render() {
74 | const ul = document.getElementById('paginator-list');
75 | const l = this.numberEntries;
76 | Array.from(ul.getElementsByClassName('page')).forEach(li => li.remove());
77 |
78 | if (l < this.perPage + 1) {
79 | this.el.classList.add('u-display--none');
80 | return;
81 | } else {
82 | this.el.classList.remove('u-display--none');
83 | }
84 | const p = this.currentPage;
85 |
86 | const pages = this.numberPages;
87 | const r = 7 - (pages - 1 - p);
88 | const frag = document.createDocumentFragment();
89 | const next = ul.getElementsByClassName('next')[0];
90 | let i = Math.max(2, p - 7);
91 | if (r > 0) i = Math.max(2, Math.min(i, i - r));
92 | const j = Math.min(pages, i + 14) + 1;
93 |
94 | this.appendButton(frag, 1);
95 | for (; i < j; i++) this.appendButton(frag, i);
96 | if (pages > j - 1) this.appendButton(frag, pages);
97 |
98 | ul.insertBefore(frag, next);
99 | },
100 | appendButton(frag, b) {
101 | const btn = document.createElement('li');
102 | btn.setAttribute('data-page', b);
103 | btn.appendChild(document.createTextNode(b));
104 | btn.classList.add('page');
105 | if (b === this.currentPage) btn.classList.add('active');
106 | frag.appendChild(btn);
107 | },
108 | changeCountPerPage(p) {
109 | this.perPage = p;
110 | this.update(this.numberEntries, true);
111 | }
112 | });
113 | }
114 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/history-sort.js:
--------------------------------------------------------------------------------
1 | export default {
2 | by: {
3 | date: {
4 | created(object) {
5 | return this._sort(object, 'first');
6 | },
7 | last(object) {
8 | return this._sort(object, 'last');
9 | },
10 | _sort(object, field) {
11 | return Object.keys(object).sort((a, b) => (new Date(object[b][field])) - (new Date(object[a][field])));
12 | }
13 | },
14 | name: {
15 | az(object) {
16 | return this._sort(object);
17 | },
18 | za(object) {
19 | return this._sort(object).reverse();
20 | },
21 | _sort(object) {
22 | return Object.keys(object).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/import.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | export default function() {
5 | return new _DOMMODULE({
6 | el: document.getElementById('export'),
7 | events: {
8 | ENV: {
9 | 'updated:settings': 'updateExportLinks',
10 | 'updated:history': 'updateExportLinks',
11 | 'toggled:sync': 'updateExportLinks',
12 | 'synced:entry': 'updateExportLinks'
13 | },
14 | DOM: {
15 | click: {
16 | '.import-button': 'triggerFileInput'
17 | },
18 | change: {
19 | '.import-file': 'handleFile'
20 | },
21 | mousedown: {
22 | '.export-button': 'updateExportName'
23 | }
24 | }
25 | },
26 |
27 | autoinit() {
28 | this.updateExportLinks();
29 | },
30 |
31 | import: function(storeString, type) {
32 | var parsedString;
33 |
34 | try {
35 | parsedString = JSON.parse(storeString);
36 | } catch(e) {
37 | return this.displayFailure(browser.i18n.getMessage('error_file_parse'));
38 | }
39 |
40 | if (parsedString) {
41 | this.emit('import:storage', parsedString, type);
42 | this.displayFailure('');
43 | }
44 | },
45 |
46 | triggerFileInput: function(e, el) {
47 | document.getElementById('import--' + el.getAttribute('data-type')).click();
48 | },
49 | handleFile(e, el) {
50 | let reader = new FileReader(),
51 | file = el.files[0],
52 | size = file.size / 1000000,
53 | type = el.getAttribute('data-type'),
54 | mod = this;
55 |
56 | if (size > 50)
57 | return this.displayFailure(browser.i18n.getMessage('error_file_size'));
58 |
59 | if (type === 'sync' && size > 0.099)
60 | return this.displayFailure(browser.i18n.getMessage('error_file_size_sync'));
61 |
62 | if (file.name.split('.').pop() !== 'json')
63 | return this.displayFailure(browser.i18n.getMessage('error_file_format'));
64 |
65 | reader.onload = (function(file) {
66 | return function(e) {
67 | mod.import(e.target.result, type);
68 | };
69 | })(file);
70 |
71 | reader.readAsText(file);
72 | },
73 | displayFailure(reason) {
74 | document.getElementById('import-error').innerText = reason;
75 | },
76 |
77 | updateExportLinks() {
78 | const localDataLink = document.getElementById('export-local');
79 | const syncedDataLink = document.getElementById('export-synced');
80 |
81 | _STORE.get('local_storage').then(storage => {
82 | localDataLink.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(storage));
83 | })
84 | .then(() => _STORE.get('synced_storage').then(storage => {
85 | syncedDataLink.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(storage));
86 | }));
87 | },
88 | updateExportName(e, el) {
89 | el.parentNode.setAttribute('download',
90 | 'Textmarker-data-' +
91 | el.getAttribute('data-type') + '-' +
92 | (new Date().toLocaleString().replace(/\D/g, '_')) +
93 | '.json'
94 | );
95 | }
96 | });
97 | }
98 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/logs.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 | import _LOG_KEYS from '../../../data/log-keys'
4 |
5 | export default function() {
6 | return new _DOMMODULE({
7 | el: document.getElementById('logs'),
8 | events: {
9 | ENV: {
10 | 'updated:logs': 'log'
11 | },
12 | DOM: {
13 | click: {
14 | '#clear-logs': 'clear',
15 | '.permission': 'askPermission'
16 | }
17 | }
18 | },
19 | autoinit() {
20 | this.logMissingPermissions().then(() => this.log());
21 | },
22 | logMissingPermissions() {
23 | return browser.permissions.contains({ permissions: ['webNavigation'] })
24 | .then(granted => {
25 | if (!granted) {
26 | document.getElementById('no-permission--webNavigation').classList.remove('u-display--none');
27 | }
28 | });
29 | },
30 | log() {
31 | _STORE.get('logs').then(logs => {
32 | logs = logs || [];
33 | let tableBody = this.el.getElementsByTagName('tbody')[0],
34 | l = logs.length,
35 | frag = document.createDocumentFragment(),
36 | tr, td_date, td_msg, node_date, node_msg, node_attempt, url, log, time, msg, reason;
37 |
38 | if (l) {
39 | this.el.classList.remove('nologs');
40 | while(l--) {
41 | log = logs[l];
42 | msg = log[1];
43 | if (typeof msg === 'number') msg = browser.i18n.getMessage(_LOG_KEYS.getKeyByValue(log[1]));
44 | //'nu',{year:'numeric',month:'2-digit',day:'2-digit',hour:'numeric',second:'numeric',minute:'numeric'}
45 | time = this.optimizeDateString((new Date(log[0])).toLocaleString());
46 | tr = document.createElement('tr');
47 | td_date = document.createElement('td');
48 | td_msg = document.createElement('td');
49 | node_date = document.createTextNode(time);
50 | node_msg = document.createTextNode(msg);
51 |
52 | td_date.appendChild(node_date);
53 | td_msg.appendChild(node_msg);
54 |
55 | if (log[2]) {
56 | reason = document.createElement('div');
57 | //reason.appendChild(document.createTextNode(log[2]));
58 | reason.innerText = log[2];
59 | td_msg.appendChild(reason);
60 | }
61 | if (log[3]) {
62 | node_attempt = document.createElement('span');
63 | node_attempt.appendChild(document.createTextNode(`[#${log[3]}]`));
64 | td_msg.insertBefore(node_attempt, node_msg);
65 | }
66 | if (log[4]) {
67 | url = document.createElement('div');
68 | url.appendChild(document.createTextNode(`URL: ${log[4]}`));
69 | td_msg.appendChild(url);
70 | }
71 | tr.appendChild(td_date);
72 | tr.appendChild(td_msg);
73 | frag.appendChild(tr);
74 | }
75 | tableBody.innerText = '';
76 | tableBody.appendChild(frag);
77 | } else {
78 | this.el.classList.add('nologs');
79 | }
80 | });
81 | },
82 | clear() {
83 | this.emit('clear:logs');
84 | },
85 | optimizeDateString(date) {
86 | return (date
87 | .replace(/^(\d{1})(\D{1})/, (m, p, q)=> '0' + p + q)
88 | .replace(/(\D{1})(\d{1}\D{1})/g, (m, p, q) => p + '0' + q));
89 | },
90 | askPermission() {
91 | browser.permissions.request({ permissions: ['webNavigation'] }).then(granted => {
92 | if (granted) {
93 | this.emit('granted-permission:webNavigation');
94 | Array.from(document.getElementsByClassName('permission-alert'))
95 | .forEach(alert => alert.classList.add('u-display--none'));
96 | }
97 | });
98 | }
99 | });
100 | }
101 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/nav.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 |
3 | export default function(el) {
4 |
5 | return new _DOMMODULE({
6 | el,
7 | events: {
8 | DOM: {
9 | click: {
10 | 'li': 'toggle',
11 | '.navitem': 'toggle'
12 | }
13 | }
14 | },
15 | pageNav: null,
16 | current: null,
17 |
18 | autoinit() {
19 | this.pageNav = el.hasAttribute('data-page-nav');
20 | let current = this.current = el.getElementsByClassName('active')[0];
21 | if (current) {
22 | this.open(current);
23 | }
24 | },
25 |
26 | toggle(e, el) {
27 | if (el.classList.contains('disabled') || this.current == el) return false;
28 |
29 | if (this.current) this.close(this.current);
30 |
31 | this.open(el);
32 | },
33 | open(el) {
34 | const targetId = el.getAttribute('data-target');
35 | el.classList.add('active');
36 | document.getElementById(targetId).classList.remove('u-display--none');
37 | this.emit('opened:tab', targetId);
38 | this.current = el;
39 | if (this.pageNav) window.document.title = 'Textmarker - ' + browser.i18n.getMessage(targetId);
40 | },
41 | close(el) {
42 | el.classList.remove('active');
43 | document.getElementById(el.getAttribute('data-target')).classList.add('u-display--none');
44 | }
45 | });
46 | }
47 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/permissions.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 |
3 | export default function() {
4 | return new _DOMMODULE({
5 | el: document.getElementById('permission-alert'),
6 | events: {
7 | DOM: {
8 | click: {
9 | '.permission': 'askPermission'
10 | }
11 | }
12 | },
13 |
14 | autoinit() {
15 | browser.permissions.contains({ permissions: ['webNavigation'] })
16 | .then(granted => {
17 | if (!granted) {
18 | this.el.classList.remove('u-display--none');
19 | }
20 | });
21 | },
22 |
23 | askPermission() {
24 | browser.permissions.request({ permissions: ['webNavigation'] }).then(granted => {
25 | if (granted) {
26 | this.emit('granted-permission:webNavigation');
27 | Array.from(document.getElementsByClassName('permission-alert'))
28 | .forEach(alert => alert.classList.add('u-display--none'));
29 | }
30 | });
31 | }
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/syncing.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | export default function() {
5 | return new _DOMMODULE({
6 | el: document.getElementById('sync'),
7 | events: {
8 | ENV: {
9 | 'failed:toggle-sync': 'undo'
10 | },
11 | DOM: {
12 | click: {
13 | '.switch': 'toggleSwitch'
14 | }
15 | }
16 | },
17 |
18 | autoinit() {
19 | document.getElementById('sync-switch--settings').classList.toggle('active', _STORE.area_settings === 'sync');
20 | document.getElementById('sync-switch--history').classList.toggle('active', _STORE.area_history === 'sync');
21 | document.getElementById('sync-switch--pagenotes').classList.toggle('active', _STORE.area_pagenotes === 'sync');
22 | },
23 |
24 | toggleSwitch(e, el) {
25 | el = el.classList.contains('switch--sync') ? el : el.parentNode;
26 | el.classList.toggle('active');
27 | this.emit('toggle:sync', el.getAttribute('data-type'), el.classList.contains('active'));
28 | },
29 |
30 | undo(field) {
31 | document.getElementById('sync-switch--' + field).classList.toggle('active');
32 | }
33 | });
34 | }
35 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/toggler.js:
--------------------------------------------------------------------------------
1 | export default class _TOGGLER {
2 |
3 | constructor(el) {
4 | this.el = el;
5 | this.init();
6 | }
7 |
8 | init() {
9 | this.el.addEventListener('click', this.toggle, false);
10 | }
11 | toggle(e) {
12 | e.stopPropagation();
13 |
14 | let dataTarget = this.getAttribute('data-target'),
15 | targets = dataTarget ? dataTarget.split(' ') : null,
16 | dataToggle = this.getAttribute('data-toggle'),
17 | roles = dataToggle ? dataToggle.split(' ') : null;
18 |
19 | if (roles) {
20 | roles.forEach((role, i) => document.getElementById(targets[i]).classList[role]('open'));
21 | }
22 | else document.getElementById(targets[0]).disabled = !this.checked;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/content/addon-page/modules/troubleshooting.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | export default function() {
5 | return new _DOMMODULE({
6 | el: document.getElementById('troubleshooting'),
7 | events: {
8 | DOM: {
9 | click: {
10 | 'option': 'toggleTopic',
11 | '.trouble__answer--btn': 'showArticle',
12 | '.trouble__answer--sub': 'showArticle',
13 | '.trouble__cancel': 'cancel',
14 | '.trouble__answer--no-entries': 'scanHistory'
15 | }
16 | }
17 | },
18 |
19 | currentTrouble: null,
20 |
21 | autoinit() {
22 |
23 | },
24 |
25 | toggleTopic(e, el) {
26 | if (this.currentTrouble) this.currentTrouble.classList.remove('open');
27 | const newTrouble = this.currentTrouble = document.getElementById(el.getAttribute('data-target'));
28 | newTrouble.classList.add('open');
29 | },
30 | showArticle(e, el) {
31 | el = el.nodeName === 'BUTTON' ? el : el.parentNode;
32 | const boxClassList = el.parentNode.parentNode.classList;
33 | boxClassList.add('answered');
34 | boxClassList.add(`answered--${el.getAttribute('data-value')}`);
35 | },
36 | cancel() {
37 | Array.from(document.getElementsByClassName('answered')).forEach(el => {
38 | el.classList.remove('answered');
39 | el.classList.remove('answered--no');
40 | el.classList.remove('answered--yes');
41 | });
42 | },
43 | scanHistory() {
44 |
45 | const articles = [
46 | document.getElementById('history-scan__error'),
47 | document.getElementById('history-scan__empty'),
48 | document.getElementById('history-scan__nonempty'),
49 | document.getElementById('history-scan__nomarks')
50 | ];
51 | const [errEl, emptyEl, nonemptyEl, nomarksEl] = articles;
52 |
53 | articles.forEach(a => a.classList.add('u-display--none'));
54 |
55 | _STORE.get('history').then(history => {
56 | if (!history || !history.entries) {
57 | errEl.classList.remove('u-display--none');
58 | } else {
59 | const len = history.entries ? Object.keys(history.entries).length : 0;
60 |
61 | if (len) {
62 | nonemptyEl.classList.remove('u-display--none');
63 | let nomarks = true, entry;
64 | for (let e in history.entries) {
65 | entry = history.entries[e];
66 | if (entry.marks && entry.marks.length) {
67 | document.getElementById('history-scan__count').innerText = len;
68 | nomarks = false;
69 | break;
70 | }
71 | }
72 | if (nomarks) {
73 | nomarksEl.classList.remove('u-display--none');
74 | }
75 | } else {
76 | emptyEl.classList.remove('u-display--none');
77 | }
78 | }
79 | })
80 | .catch(e => {
81 | errEl.classList.remove('u-display--none');
82 | document.getElementById('history-scan__errormsg1').innerText = e.toString();
83 | })
84 | .then(() => {
85 | const articleClasses = document.getElementById('trouble--3').classList;
86 |
87 | articleClasses.add('answered');
88 | articleClasses.add('answered--yes');
89 | });
90 | }
91 | });
92 | }
93 |
--------------------------------------------------------------------------------
/src/content/addon-page/port.js:
--------------------------------------------------------------------------------
1 | import { _PORT } from './../_shared/utils'
2 |
3 | export default new _PORT({
4 | name: 'addon-page',
5 | type: 'content',
6 | events: {
7 | ONEOFF: [
8 | 'change:style-setting',
9 | 'change:autonote-setting',
10 | 'change:mark-method-setting',
11 | 'toggle:shortcut-setting',
12 | 'change:shortcut-setting',
13 | 'toggle:ctm-setting',
14 | 'change:saveopt-setting',
15 | 'toggle:priv-setting',
16 | 'change:immut-setting',
17 | 'change:dropLosses-setting',
18 | 'change:autoRetry-setting',
19 | 'change:namingopt-setting',
20 | 'change:sort-setting',
21 | 'change:view-setting',
22 | 'change:hash-setting',
23 | 'toggle:noteopt-setting',
24 | 'toggle:quickbuttonopt-setting',
25 | 'switch:quickbuttonopt-setting',
26 | 'toggle:notification-setting',
27 | 'toggle:misc-setting',
28 | 'change:misc-setting',
29 | 'toggle:tbbpower-setting',
30 | 'change:autocs-setting',
31 | 'change:iframe-setting',
32 | 'add:custom-marker',
33 | 'remove:custom-marker',
34 | 'delete:entries',
35 | 'clean:entries',
36 | 'open:entries',
37 | 'rename:entry',
38 | 'correct-name:entry',
39 | 'view:entry',
40 | 'sync:entry',
41 | 'sync:history',
42 | 'sync:settings',
43 | 'import:storage',
44 | 'toggle:sync',
45 | 'change:custom-search-setting',
46 | 'changed:per-page-count',
47 | 'error:browser-console',
48 | 'clear:logs',
49 | 'tag:entries',
50 | 'granted-permission:webNavigation'
51 | ]
52 | }
53 | })
54 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/_buttons.scss:
--------------------------------------------------------------------------------
1 | @keyframes btn {
2 | from { color: $color-bg--turquoise; }
3 | to { color: $color-text--light; }
4 | }
5 |
6 | button, .btn {
7 | @extend %button;
8 | &--small {
9 | height: 22px;
10 | width: 22px;
11 | padding: 2px 5px;
12 | background-position: 5px 5px, 0 0;
13 | background-size: 13px, 100%;
14 | margin-left: 5px;
15 | }
16 | &:not(.btn--small) {
17 | background: #fff;
18 |
19 | &:hover {
20 | background: $color-bg--turquoise;
21 | //color: $color-text--light;
22 | animation: 1s forwards btn;
23 | }
24 | }
25 | }
26 |
27 | aside {
28 | position: absolute;
29 | top: 50px;
30 | right: -37px;
31 |
32 | @include mq('desk') {
33 | right: 0;
34 | top: 0;
35 | position: relative;
36 | }
37 |
38 | a {
39 | width: 37px;
40 | height: 24px;
41 | border: 1px solid $color-shadow--light;
42 | border-left: 0;
43 | border-radius: 0 3px 3px 0;
44 | display: block;
45 | margin-bottom: 7px;
46 | opacity: .75;
47 | transition: opacity .3s ease-in;
48 |
49 | &:hover {
50 | opacity: 1;
51 | border-color: $color-shadow--lighter;
52 | }
53 |
54 | @include mq('desk') {
55 | border: 0;
56 | border-radius: 2px;
57 | display: inline-block;
58 | background: $color-bg--turquoise;
59 | float: right;
60 | margin-left: 7px;
61 | opacity: 1;
62 |
63 | &:hover {
64 | border: 0;
65 | opacity: .75;
66 | }
67 |
68 | &.github,
69 | &.email {
70 | background-position: center !important;
71 | }
72 |
73 | img {
74 | margin-top: 6px !important;
75 | }
76 | }
77 |
78 | &.github {
79 | @include icon('github', 9px center);
80 | }
81 |
82 | &.email {
83 | @include icon('email', center center);
84 | }
85 |
86 | img {
87 | width: 20px;
88 | height: 13px;
89 | margin: 4px 0 0 9px;
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/_general.scss:
--------------------------------------------------------------------------------
1 | body, html, body > div {
2 | height: 100%;
3 | }
4 | body {
5 | background: $color-bg--dark;
6 | }
7 | p {
8 | margin-bottom: 1em;
9 | }
10 | h1 {
11 | font-size: 1em;
12 | font-weight: normal;
13 | text-align: right;
14 | margin: 0 -1px 15px 0;
15 | height: 38px;
16 |
17 | a {
18 | color: inherit;
19 | text-decoration: none;
20 |
21 | &:hover {
22 | color: $color-text--turquoise;
23 | text-decoration: none;
24 | }
25 | }
26 | > span {
27 | display: block;
28 | font-size: 0.65em;
29 | color: $color-text--disabled;
30 | }
31 | }
32 | h2 {
33 | font-weight: normal;
34 | padding: 0 0 27px;
35 | color: $color-text--turquoise;
36 |
37 | span {
38 | color: $color-text--disabled;
39 | font-size: 0.7em;
40 | display: block;
41 | margin-top: 10px;
42 | }
43 | }
44 | h3 {
45 | font-size: 1.15em;
46 | font-weight: normal;
47 | padding: 0 0 20px;
48 | color: $color-text--turquoise;
49 | }
50 | h4 {
51 | font-size: 1em;
52 | font-weight: normal;
53 | color: $color-text--turquoise;
54 | padding: 25px 0 15px;
55 | }
56 | ul,
57 | li {
58 | list-style-type: none;
59 | }
60 | li {
61 | -moz-user-select: none;
62 | }
63 | ol {
64 | margin: -5px 0 1em 0;
65 |
66 | li {
67 | margin-top: 5px;
68 |
69 | strong.u-color--turquoise {
70 | font-family: Helvetica, Verdana, Arial, sans-serif;
71 | }
72 | }
73 | }
74 | .unset li {
75 | list-style-type: unset;
76 | margin-left: 20px;
77 | }
78 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/_helpers.scss:
--------------------------------------------------------------------------------
1 | .width--full {
2 | width: 100% !important;
3 | }
4 | .border-top {
5 | border-bottom: 0 !important;
6 | border-left: 0 !important;
7 | border-right: 0 !important;
8 | }
9 | .open {
10 | display: block !important;
11 | }
12 | .toggle-button {
13 | @include mq('mobile') {
14 | position: relative;
15 | }
16 | }
17 | .nowrap {
18 | white-space: nowrap;
19 | }
20 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/_navs.scss:
--------------------------------------------------------------------------------
1 | @keyframes nav {
2 | 0% { padding-left: 37px; }
3 | 20% { padding-left: 12px; }
4 | 100% { padding-left: 37px; }
5 | }
6 |
7 | @keyframes navicon {
8 | from { left: 12px; }
9 | to { left: -25px; }
10 | }
11 |
12 | @keyframes ghost {
13 | 0% { left: 12px; top: 16px; }
14 | 20%, 40% { left: -25px; filter: grayscale(100%); transform: scale(1); }
15 | 90% { left: 12px; top: 12px; transform: scale(1.7); opacity: 1; }
16 | 100% { left: -25px; transform: scale(0); opacity: 0; }
17 | }
18 |
19 | .nav li {
20 | cursor: default;
21 | }
22 | /* main nav */
23 | #mainnav {
24 | float: left;
25 | width: 220px;
26 | color: $color-text--light;
27 | height: 100%;
28 | padding-top: 127px;
29 |
30 | @include mq('mobile') {
31 | position: absolute;
32 | width: 100%;
33 | height: auto;
34 | box-shadow: none;
35 | padding-top: 0;
36 | z-index: 2;
37 | }
38 |
39 | li {
40 | height: 53px;
41 | padding: 14px 20px 20px 37px;
42 | position: relative;
43 |
44 | &:hover {
45 | background-color: rgba(255, 255, 255, 0.1);
46 | }
47 |
48 | &:before {
49 | content: '';
50 | display: block;
51 | width: 16px;
52 | height: 16px;
53 | position: absolute;
54 | top: 16px;
55 | left: 12px;
56 | }
57 |
58 | &.active {
59 | background-color: rgba(43, 165, 183, 0.5);
60 | background-position: 0 6px;
61 | background-image: none;
62 | animation-name: nav;
63 | animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
64 | animation-duration: .5s;
65 | animation-fill-mode: forwards;
66 | transition: background-color .5s ease-out;
67 |
68 | &:before {
69 | animation-name: navicon;
70 | animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
71 | animation-duration: .5s;
72 | animation-fill-mode: forwards;
73 | }
74 |
75 | mainnav-contact {
76 | &:before {
77 | animation-name: ghost;
78 | animation-duration: 1.5s;
79 | }
80 | }
81 | }
82 |
83 | &.deactivated {
84 | color: rgba(255, 255, 255, 0.25);
85 | }
86 |
87 | span.icon {
88 | color: $color-text--turquoise;
89 | margin-right: 10px;
90 | }
91 | }
92 | }
93 | .menu-toggler {
94 | @include mq('mobile') {
95 | width: 48px;
96 | height: 48px;
97 | @include icon('menu-blue');
98 | filter: hue-rotate(331deg) brightness(1.3);
99 | position: absolute;
100 | top: 25px;
101 | left: 21px;
102 | }
103 | }
104 | #mainnav-list {
105 | @include mq('mobile') {
106 | display: none;
107 | background: $color-bg--dark;
108 | padding-top: 125px;
109 | width: 100%;
110 | }
111 | }
112 | $icons: (
113 | 'news': 'forklift',
114 | 'manual': 'book',
115 | 'settings': 'tools',
116 | 'history': 'clock',
117 | 'sync': 'sync2',
118 | 'export': 'export',
119 | 'contact': 'ghost',
120 | 'troubleshooting': 'debug',
121 | 'logs': 'dna'
122 | );
123 |
124 | @each $navitem, $icon in $icons {
125 | #mainnav-#{$navitem} {
126 | &:before {
127 | @include icon(#{$icon});
128 | }
129 | }
130 | }
131 |
132 | /* subnavs */
133 | .subnav {
134 | ul {
135 | border-bottom: 1px solid $color-border--dark;
136 | border-top: 1px solid $color-border--dark;
137 | }
138 |
139 | li {
140 | display: inline-block;
141 | padding: 10px 10px 17px;
142 |
143 | @include mq('desk') {
144 | margin: -1px 0 0 0;
145 | padding: 10px 5px 17px;
146 | float: none;
147 | }
148 |
149 | &.active {
150 | border-bottom: 4px solid $color-turquoise;
151 |
152 | @include mq('desk') {
153 | color: $color-text--turquoise;
154 | border: 0;
155 | text-decoration: underline;
156 | }
157 | }
158 |
159 | &.disabled {
160 | color: $color-text--disabled;
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/components/_alerts.scss:
--------------------------------------------------------------------------------
1 | .tm-alert {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | width: 100%;
6 | padding: 10px 240px;
7 | color: #fbfbfb;
8 | z-index: 2;
9 |
10 | @include mq('mobile') {
11 | position: relative;
12 | padding: 10px;
13 | }
14 |
15 | &--error {
16 | background: transparentize($color-red, .1);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/components/_contact.scss:
--------------------------------------------------------------------------------
1 | a .at {
2 | font-size: 22px;
3 | vertical-align: -2px;
4 | margin-left: 0px;
5 | display: inline-block;
6 | font-family: times;
7 | }
8 | #contact a:hover {
9 | font-style: italic;
10 | }
11 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/components/_data-management.scss:
--------------------------------------------------------------------------------
1 | a.export {
2 | text-decoration: none !important;
3 | }
4 | #export button {
5 | float: none;
6 | margin: 0 0 10px;
7 | }
8 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/components/_logs.scss:
--------------------------------------------------------------------------------
1 | .nologs {
2 | #no-logs {
3 | display: block !important;
4 | }
5 | #clear-logs {
6 | display: none;
7 | }
8 | #logs-table {
9 | display: none;
10 | }
11 | }
12 |
13 | #logs-table {
14 | border-collapse: collapse;
15 |
16 | th {
17 | padding: 3px 5px 15px;
18 | text-align: left;
19 | text-transform: uppercase;
20 |
21 | &:first-child {
22 | width: 190px;
23 | }
24 | }
25 | td {
26 | padding: 4px 5px 5px;
27 | border-top: 1px solid $color-border;
28 | border-bottom: 1px solid $color-border;
29 | font-size: 13px;
30 |
31 | tr:last-child & {
32 | border: 0;
33 | }
34 |
35 | div {
36 | margin-top: 5px;
37 | color: $color-text--grey;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/components/_news.scss:
--------------------------------------------------------------------------------
1 | .news-box {
2 | border: 1px solid $color-border--light;
3 | padding: $spacing--small;
4 | margin-bottom: $spacing--small;
5 | overflow: hidden;
6 | max-height: 43px;
7 | transition: none;
8 |
9 | &.open {
10 | max-height: 1000px;
11 | transition: max-height .5s ease-in;
12 | }
13 |
14 | &--simple {
15 | h3 {
16 | &:after {
17 | display: none !important;
18 | }
19 | }
20 | }
21 |
22 | h3 {
23 | padding-right: $spacing--large;
24 | padding-bottom: $spacing--small;
25 | margin-bottom: $spacing--medium;
26 | border-bottom: 1px solid $color-border--light;
27 | position: relative;
28 | text-overflow: ellipsis;
29 | overflow: hidden;
30 | white-space: nowrap;
31 |
32 | &:after {
33 | content: '';
34 | display: block;
35 | width: 16px;
36 | height: 16px;
37 | position: absolute;
38 | right: 3px;
39 | top: 3px;
40 | @include icon('down');
41 | transform: rotate(90deg);
42 | opacity: .25;
43 | }
44 | &:hover {
45 | &:after {
46 | opacity: .5;
47 | }
48 | }
49 | }
50 |
51 | p {
52 | display: none;
53 | }
54 | &.open {
55 | h3 {
56 | white-space: unset;
57 |
58 | &:after {
59 | transform: none;
60 | }
61 | }
62 |
63 | p {
64 | display: block;
65 | }
66 | }
67 |
68 | small {
69 | display: block;
70 | margin-top: $spacing--tiny;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/components/_settings.scss:
--------------------------------------------------------------------------------
1 | .option-row {
2 | margin-top: 13px;
3 |
4 | button {
5 | float: right;
6 | padding: 0 7px;
7 | }
8 | > div {
9 | display: inline-block;
10 | }
11 | }
12 | .key {
13 | background: $color-bg--dark;
14 | color: $color-text--light;
15 | font-size: inherit;
16 | padding: 3px 7px 5px;
17 | width: 40px;
18 | height: 40px;
19 | float: left;
20 | border-radius: 5px;
21 | margin: 8px 8px 0 0;
22 | }
23 | .accordion {
24 | max-height: 0;
25 | overflow: hidden;
26 | transition: max-height 1s;
27 | clear: both;
28 |
29 | &.open {
30 | max-height: 200px;
31 | }
32 | }
33 | /* markers */
34 | #customized-key {
35 | margin-left: 4px;
36 | }
37 | #shadow-settings > div {
38 | margin-bottom: 5px;
39 | }
40 | #preview {
41 | position: relative;
42 | margin-top: 26px;
43 |
44 | button {
45 | position: absolute;
46 | right: 0;
47 | top: -26px;
48 | }
49 | }
50 | #example {
51 | padding: 0 5px;
52 | overflow: hidden;
53 | float: left;
54 | }
55 | #custom-keys div {
56 | vertical-align: bottom;
57 | }
58 |
59 | /* shortcuts */
60 | #customize-shortcuts {
61 | table, tr {
62 | display: block;
63 | border-collapse: collapse;
64 | td {
65 | display: inline-block;
66 | width: 140px;
67 | padding: 12px 10px 0 0;
68 |
69 | &:first-child {
70 | width: calc(100% - 160px);
71 | }
72 | }
73 | }
74 | label {
75 | width: 100%;
76 |
77 | sup sup {
78 | width: auto;
79 | }
80 | }
81 | }
82 | #overwrite-hint {
83 | text-indent: -9px;
84 | display: inline-block;
85 | margin-left: 9px;
86 | }
87 |
88 | /* history */
89 | #notification {
90 | margin: 0 3px 0 5px;
91 | }
92 | #custom-name-row {
93 | display: none;
94 | }
95 | #quickbutton-download-select {
96 | display: none;
97 | }
98 | #download-text:checked + label + #quickbutton-download-select {
99 | display: inline-block;
100 | }
101 | #drop-losses-box,
102 | #name-mark-box,
103 | #ignore-hash-box {
104 | &:hover small {
105 | display: block !important;
106 | }
107 | }
108 | /* misc */
109 | .tmuipos {
110 | display: none;
111 | border-color: $color-border--light;
112 | }
113 | .misc-cb:checked + label + .tmuipos {
114 | display: inline;
115 | }
116 | #custom-search {
117 | @include mq('desk') {
118 | float: none;
119 | margin-top: 10px;
120 | vertical-align: -2px;
121 | }
122 | }
123 | #addon-autocs:checked + label + small {
124 | display: none !important;
125 | }
126 |
--------------------------------------------------------------------------------
/src/content/addon-page/sass/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../_shared/sass/vars';
2 | @import '../../_shared/sass/reset';
3 | @import '../../_shared/sass/general';
4 | @import '../../_shared/sass/helpers';
5 | @import '../../_shared/sass/buttons';
6 | @import '../../_shared/sass/icons';
7 | @import '../../_shared/sass/forms';
8 | @import '../../_shared/sass/links';
9 | @import '../../_shared/sass/shadows';
10 | @import '../../_shared/sass/switches';
11 | @import '../../_shared/sass/infos';
12 |
13 | @import './general';
14 | @import './helpers';
15 | @import './navs';
16 | @import './buttons';
17 | @import './components/settings';
18 | @import './components/history';
19 | @import './components/data-management';
20 | @import './components/contact';
21 | @import './components/logs';
22 | @import './components/news';
23 | @import './components/troubleshooting';
24 | @import './components/alerts';
25 |
26 | @include links;
27 |
28 | button {
29 | @extend %button;
30 | }
31 |
32 | /* MAIN CONTENT */
33 | #main {
34 | position: relative;
35 | padding: 20px;
36 | max-width: 760px;
37 | width: calc(100% - 220px);
38 | float: left;
39 | min-height: 100%;
40 | box-shadow: 0 0 8px 0px $color-shadow--darker;
41 | background: $color-bg--light;
42 |
43 | @include mq('mobile') {
44 | width: 100%;
45 | }
46 | small {
47 | color: $color-text--grey;
48 | }
49 | }
50 | .content {
51 | padding-bottom: 20px;
52 |
53 | a {
54 | color: $color-text--turquoise;
55 | text-decoration: underline;
56 | }
57 | @include mq('mobile') {
58 | padding-top: 35px;
59 | }
60 | }
61 | .tab {
62 | padding: 40px 0;
63 | }
64 | .block-spacing {
65 | padding: 12px 0 21px;
66 | }
67 | .seperator {
68 | border-top: 1px solid $color-border;
69 | margin: 20px 0;
70 | }
71 |
72 | #deprication-note {
73 | position: fixed;
74 | bottom: 0;
75 | left: 0;
76 | width: 100%;
77 | padding: 10px;
78 | z-index: 1;
79 | background: #3aad74;
80 | color: #fbfbfb;
81 | font-size: 30px;
82 | }
--------------------------------------------------------------------------------
/src/content/detail-view/index.js:
--------------------------------------------------------------------------------
1 | import './sass/index.scss'
2 |
3 | import { _MODULE } from './../_shared/utils'
4 | import { _L10N } from './../_shared/utils'
5 | import './modules/header'
6 | import './modules/meta'
7 | //import './modules/notes'
8 | import './modules/marks'
9 |
10 | _L10N();
11 |
12 | new _MODULE({
13 | autoinit() {
14 | const name = decodeURIComponent(window.location.hash).slice(1);
15 |
16 | browser.storage.sync.get().then(storage => {
17 | if (Object.keys(storage.history.entries).includes(name)) return storage.history.entries[name];
18 | return browser.storage.local.get().then(storage => {
19 | if (Object.keys(storage.history.entries).includes(name)) return storage.history.entries[name];
20 | });
21 | })
22 | .then(entry => {
23 | this.emit('entry', entry);
24 | document.title = entry.name;
25 | });
26 | }
27 | });
28 |
--------------------------------------------------------------------------------
/src/content/detail-view/modules/header.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 |
3 | new _DOMMODULE({
4 | el: document.getElementById('header'),
5 | events: {
6 | ENV: {
7 | 'entry': 'render'
8 | }
9 | },
10 | entry: null,
11 |
12 | setTitle(entry) {
13 | this.el.innerText = entry.name;
14 | },
15 | setTag(entry) {
16 | const tag = entry.tag || browser.i18n.getMessage('detail_notag');
17 | document.getElementById('tag').innerText = tag;
18 | },
19 | render(entry) {
20 | this.entry = entry;
21 | this.setTitle(entry);
22 | this.setTag(entry);
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/src/content/detail-view/modules/marks.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _SETTINGS from './../../../data/global-settings'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('marks'),
6 | events: {
7 | ENV: {
8 | 'entry': 'render'
9 | },
10 | DOM: {
11 | click: {
12 | '.col-toggle': 'toggleNotes',
13 | '.table-toggle': 'toggleTable'
14 | }
15 | }
16 | },
17 | entry: null,
18 | tmpl: null,
19 | tbody: null,
20 | marks: [],
21 | notes: false,
22 | notesShown: true,
23 |
24 | render(entry) {
25 | this.entry = entry;
26 | const tmpl = this.tmpl = document.getElementById('mark-template');
27 | const tbody = this.tbody = document.getElementById('marks-content');
28 | const marks = this.marks = this.sortById(entry.marks);
29 |
30 | if (!marks.length) this.el.classList.add('disabled');
31 |
32 | this.renderCount(entry);
33 | marks.forEach(mark => this.renderMark(mark));
34 | tbody.removeChild(tmpl);
35 | if (!this.notes) this.el.classList.add('no-notes');
36 | },
37 | renderCount(entry) {
38 | document.getElementById('marks-count').innerText = '(' + entry.marks.length + ')';
39 | },
40 | renderMark(mark) {
41 | const markEl = this.tmpl.cloneNode(true);
42 | const td_text = markEl.getElementsByClassName('mark-text')[0];
43 | const td_note = markEl.getElementsByClassName('mark-note')[0];
44 | const text = document.createElement('p');
45 | let noteText, noteColor;
46 | td_text.innerText = mark.text;
47 | td_text.setAttribute('style', mark.style);
48 | if (mark.note) {
49 | if (typeof mark.note === 'string') {
50 | noteText = mark.note;
51 | noteColor = _SETTINGS.NOTE_COLORS.YELLOW;
52 | } else {
53 | noteText = mark.note.text || '';
54 | noteColor = mark.note.color || 'yellow';
55 | noteColor = _SETTINGS.NOTE_COLORS[noteColor.toUpperCase()];
56 | }
57 | if (noteText) {
58 | td_note.innerText = noteText;
59 | td_note.parentNode.style.backgroundColor = noteColor;
60 | this.notes = true;
61 | }
62 | }
63 | markEl.id = 'mark-' + mark.id;
64 | this.tbody.appendChild(markEl);
65 | },
66 | sortById(marks) {
67 | return marks.sort((mark1, mark2) => {
68 | const id1 = mark1.id;
69 | const id2 = mark2.id;
70 | if (id1 === id2) return 0;
71 | return id1 < id2 ? -1 : 1;
72 | });
73 | },
74 | toggleNotes() {
75 | if (this.notesShown) {
76 | this.el.classList.add('hide-notes');
77 | } else {
78 | this.el.classList.remove('hide-notes');
79 | }
80 | this.notesShown = !this.notesShown;
81 | },
82 | toggleTable() {
83 | this.el.classList.toggle('folded');
84 | }
85 | });
86 |
--------------------------------------------------------------------------------
/src/content/detail-view/modules/meta.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 |
3 | new _DOMMODULE({
4 | el: document.getElementById('meta'),
5 | events: {
6 | ENV: {
7 | 'entry': 'render'
8 | }
9 | },
10 | entry: null,
11 |
12 | setDates(entry) {
13 | document.getElementById('created').innerText = this.optimizeDateString(new Date(entry.first).toLocaleString());
14 | document.getElementById('last_modified').innerText = this.optimizeDateString(new Date(entry.last).toLocaleString());
15 | },
16 | setTitle(entry) {
17 | document.getElementById('title').innerText = entry.title;
18 | },
19 | setLink(entry) {
20 | const link = document.getElementById('url');
21 | const url = entry.url;
22 | link.href = url;
23 | link.innerText = url;
24 | },
25 | setSyncMode(entry) {
26 | const val = entry.synced ? browser.i18n.getMessage('yes') : browser.i18n.getMessage('no');
27 | document.getElementById('synced').innerText = val;
28 | },
29 | render(entry) {
30 | this.entry = entry;
31 | this.setDates(entry);
32 | this.setTitle(entry);
33 | this.setLink(entry);
34 | this.setSyncMode(entry);
35 | },
36 | optimizeDateString(date) {
37 | return (date
38 | .replace(/^(\d{1})(\D{1})/, (m, p, q)=> '0' + p + q)
39 | .replace(/(\D{1})(\d{1}\D{1})/g, (m, p, q) => p + '0' + q));
40 | }
41 | });
42 |
--------------------------------------------------------------------------------
/src/content/detail-view/sass/_general.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background: $color-bg--light;
3 | }
4 | p {
5 | margin-bottom: 5px;
6 | }
7 | header {
8 | background: $color-bg--dark;
9 | padding: 10px 20px;
10 | }
11 | header p {
12 | color: $color-text--turquoise;
13 | }
14 | section {
15 | margin-bottom: 30px;
16 | }
17 | section:last-child {
18 | margin: 0;
19 | }
20 | h1 {
21 | color: $color-text--light;
22 | font-size: 20px;
23 | }
24 | h2 {
25 | font-size: 17px;
26 | margin-bottom: 15px;
27 | }
28 |
--------------------------------------------------------------------------------
/src/content/detail-view/sass/_tables.scss:
--------------------------------------------------------------------------------
1 | table {
2 | .disabled & {
3 | display: none;
4 | }
5 |
6 | .folded & {
7 | display: none;
8 | }
9 |
10 | border-collapse: collapse;
11 |
12 | td {
13 | padding: 2px 15px 2px 0;
14 | }
15 |
16 | &.table--bordered {
17 | width: 100%;
18 |
19 | col {
20 | width: 50%;
21 | }
22 | td, th {
23 | border: 1px solid $color-border;
24 | }
25 | th {
26 | padding: 5px 10px;
27 | text-align: left;
28 | }
29 | td {
30 | padding: 10px;
31 | }
32 | }
33 | .no-notes & {
34 | col {
35 | &:first-child {
36 | width: 100%;
37 | }
38 | &:last-child {
39 | width: 0;
40 | }
41 | }
42 | thead {
43 | display: none;
44 | }
45 | td {
46 | &:last-child {
47 | display: none;
48 | }
49 | }
50 | }
51 | .hide-notes & {
52 | col {
53 | &:first-child {
54 | width: 100%;
55 | }
56 | &:last-child {
57 | width: 0;
58 | }
59 | }
60 | th {
61 | &:last-child {
62 | text-indent: -1000px;
63 | overflow: hidden;
64 | }
65 | }
66 | td {
67 | &:last-child {
68 | border: 0;
69 | }
70 | }
71 | .mark-note {
72 | display: none;
73 | }
74 | }
75 | }
76 |
77 | .col-toggle {
78 | @include icon('double-arrow', 0 center, 18px);
79 | width: 18px;
80 | height: 20px;
81 | float: right;
82 | cursor: pointer;
83 |
84 | .hide-notes & {
85 | transform: rotate(180deg);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/content/detail-view/sass/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../_shared/sass/vars';
2 | @import '../../_shared/sass/reset';
3 | @import '../../_shared/sass/general';
4 | @import '../../_shared/sass/links';
5 | @import '../../_shared/sass/icons';
6 |
7 | @import './general';
8 | @import './tables';
9 |
10 | @include links($color-text--turquoise);
11 |
12 | #main {
13 | padding: 20px;
14 | }
15 | .table-toggle {
16 | @include icon('down', 0 center, 14px);
17 | width: 14px;
18 | height: 12px;
19 | display: inline-block;
20 | cursor: pointer;
21 | opacity: 0.25;
22 | margin-right: 8px;
23 |
24 | .folded & {
25 | transform: rotate(-90deg);
26 | }
27 |
28 | .disabled & {
29 | display: none;
30 | }
31 | }
32 | .count {
33 | color: $color-text--disabled;
34 | }
35 |
--------------------------------------------------------------------------------
/src/content/options-ui/index.js:
--------------------------------------------------------------------------------
1 | import './sass/index.scss'
2 |
3 | import { _L10N } from './../_shared/utils'
4 |
5 | _L10N();
6 |
7 | Array.from(document.getElementsByTagName('button')).forEach(button => {
8 | button.addEventListener('click', e => {
9 | browser.runtime.sendMessage({
10 | ev: 'open:addon-page(am)',
11 | args: [e.target.getAttribute('data-id')]
12 | });
13 | }, false);
14 | });
15 |
--------------------------------------------------------------------------------
/src/content/options-ui/sass/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../_shared/sass/vars';
2 | @import '../../_shared/sass/buttons';
3 |
4 | @keyframes btn {
5 | from { color: $color-bg--turquoise; }
6 | to { color: $color-text--light; }
7 | }
8 |
9 | button {
10 | @extend %button;
11 |
12 | width: 120px;
13 | font-size: inherit;
14 | margin-bottom: 12px;
15 | background: #fff;
16 |
17 | &:hover {
18 | background: $color-bg--turquoise;
19 | animation: 1s forwards btn;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/content/page-injections/index.js:
--------------------------------------------------------------------------------
1 | import './sass/index.scss'
2 |
3 | import _ERRORTRACKER from '../_shared/utils'
4 | import './port'
5 |
6 | import './modules/page'
7 | import './modules/contextmenu'
8 | import './modules/marker'
9 | import './modules/notes'
10 | import './modules/tmui'
11 | import './modules/marker-popup'
12 | import './modules/auto-marker'
13 |
14 | import './main'
15 |
--------------------------------------------------------------------------------
/src/content/page-injections/main.js:
--------------------------------------------------------------------------------
1 | import { _MODULE } from '../_shared/utils'
2 | import _STORE from './_store'
3 | import _RESTORER from './modules/restorer'
4 |
5 | new _MODULE({
6 | events: {
7 | ENV: {
8 | 'opened:sidebar': 'sendPageState',
9 |
10 | 'failed:restoration': 'activateRetry',
11 | 'succeeded:restoration': 'deactivateRetry',
12 | 'update:entry?': 'deactivateRetry',
13 |
14 | 'updated:naming-settings': 'updateStatus',
15 | 'updated:hashopt-settings': 'updateStatus',
16 | 'updated:entry-sync': 'setSyncForEntry',
17 | 'updated:entry-name': 'renameEntry',
18 | 'saved:entry': 'onSavedEntry',
19 | 'deleted:entry': 'removeEntry',
20 | 'resumed-on-hashchange': 'onResumed',
21 |
22 | // @RESTORER
23 | 'entries:found': 'onEntriesFound',
24 |
25 | // register at BG
26 | 'injected?': 'notifyBG'
27 | }
28 | },
29 |
30 | retryActive: false,
31 | restorer: null,
32 |
33 | autoinit() {
34 | _STORE.updateLocation();
35 | _STORE.updateStatus();
36 | if (!this.iframe) {
37 | window.addEventListener('hashchange', this.proxy(this, this.onHashChange), false);
38 | }
39 | },
40 |
41 | onHashChange() {
42 | if (_STORE.hashSensitive) {
43 | this.emit('hashchange');
44 | }
45 | },
46 | onResumed() {
47 | _STORE.updateLocation();
48 | _STORE.isNew || _STORE.resume();
49 |
50 | this.emit('fetch:entries', _STORE.url);
51 | },
52 | onSavedEntry(entry) {
53 | const ignoreHash = !entry.hashSensitive;
54 | const url = ignoreHash ? _STORE.hashlessURL : _STORE.url;
55 |
56 | if (entry.url === url) {
57 | _STORE.addEntries([entry]);
58 | }
59 | },
60 | activateRetry() {
61 | this.retryActive = true;
62 | },
63 | deactivateRetry() {
64 | this.retryActive = false;
65 | },
66 | sendPageState(info) {
67 | this.emit('page-state', {
68 | selection: !window.getSelection().isCollapsed,
69 | bookmark: !!document.getElementById('textmarker-bookmark-anchor'),
70 | retryActive: this.retryActive
71 | }, info);
72 | },
73 |
74 | updateStatus() {
75 | _STORE.updateStatus();
76 | },
77 | setSyncForEntry(...args) {
78 | _STORE.setSyncForEntry(...args);
79 | },
80 | renameEntry(...args) {
81 | _STORE.renameEntry(...args);
82 | },
83 | removeEntry(name) {
84 | if (_STORE.name === name) {
85 | _STORE.removeEntry(name);
86 | this.emit('removed:entry');
87 | }
88 | },
89 |
90 | // @RESTORER
91 | onEntriesFound(info) {
92 | if (!_STORE.updated) {
93 | this.on('updated:store-status', this.proxy(this, this.startRestoration, info));
94 | } else {
95 | this.startRestoration(info);
96 | }
97 | },
98 | startRestoration(info) {
99 | let entries = info.entries;
100 | entries = Array.isArray(entries) ? entries : [entries];
101 | _STORE.locked = info.locked;
102 | if (!this.restorer) this.restorer = _RESTORER();
103 | _STORE.addEntries(entries);
104 | this.emit('set:entries', entries);
105 | if (info.recentlyOpenedEntry) this.updateName(entries, info.recentlyOpenedEntry);
106 | this.emit('restore:marks', info.entries);
107 | },
108 | updateName(entries, recentlyOpenedEntry) {
109 | const firstEntry = entries[0];
110 | const ignoreHash = !firstEntry.hashSensitive;
111 | const url = ignoreHash ? _STORE.hashlessURL : _STORE.url;
112 |
113 | if (recentlyOpenedEntry.url === url) {
114 | _STORE.name = recentlyOpenedEntry.name;
115 | }
116 | },
117 |
118 | notifyBG(sender, sendResponse) {
119 | sendResponse(true);
120 | }
121 | });
122 |
--------------------------------------------------------------------------------
/src/content/page-injections/modules/auto-marker.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | events: {
6 | ENV: {
7 | 'updated:mark-method-settings': 'update',
8 | 'changed:selection': 'onSelectionChange',
9 | 'sidebar:selected-marker': 'setMarker'
10 | }
11 | },
12 |
13 | handler: null,
14 | marker: 'm',
15 |
16 | autoinit() {
17 | this.update();
18 | },
19 |
20 | update() {
21 | _STORE.get('settings').then(settings => {
22 | this.active = settings.misc.markmethod === 'auto';
23 | });
24 | },
25 | setMarker(key) {
26 | this.marker = key;
27 | },
28 | onSelectionChange(selected) {
29 | if (this.active) {
30 | if (selected && !this.listening) {
31 | this.startListening();
32 | }
33 | else if (!selected && this.listening) {
34 | this.stopListening();
35 | }
36 | }
37 | else if (this.listening) {
38 | this.stopListening();
39 | }
40 | },
41 | startListening() {
42 | if (!this.listening) {
43 | const handler = this.handler = this.onMouseup.bind(this);
44 | window.document.body.addEventListener('mouseup', handler, false);
45 | this.listening = true;
46 | }
47 | },
48 | stopListening() {
49 | if (this.listening) {
50 | window.document.body.removeEventListener('mouseup', this.handler, false);
51 | this.listening = false;
52 | }
53 | },
54 | onMouseup() {
55 | this.stopListening();
56 | const selection = window.getSelection().toString();
57 | if (selection) this.emit('selection-end', this.marker);
58 | }
59 | });
60 |
--------------------------------------------------------------------------------
/src/content/page-injections/modules/bookmark.js:
--------------------------------------------------------------------------------
1 | import _STORE from './../_store'
2 |
3 | export default class _BOOKMARK {
4 |
5 | constructor() {
6 | this.mark = null;
7 | this.anchor = null;
8 | }
9 |
10 | set(mark) {
11 | mark = mark || this.mark;
12 |
13 | let wrappers = mark.wrappers,
14 | w = wrappers.length,
15 | anchor = wrappers[0];
16 |
17 | mark.keyData.bookmark = true;
18 | anchor.id = 'textmarker-bookmark-anchor';
19 |
20 | while (w--)
21 | wrappers[w].classList.add('textmarker-bookmark');
22 |
23 | this.mark = mark;
24 | this.anchor = anchor;
25 |
26 | return this;
27 | }
28 | remove() {
29 | let mark = this.mark,
30 | anchor = this.anchor,
31 | wrappers = mark.wrappers,
32 | w = wrappers.length;
33 |
34 | anchor.id = '';
35 | mark.keyData.bookmark = false;
36 |
37 | while (w--)
38 | wrappers[w].classList.remove('textmarker-bookmark');
39 | }
40 | scrollIntoView(bm) {
41 | if (bm || (bm = window.document.getElementById('textmarker-bookmark-anchor')))
42 | bm.scrollIntoView({ behavior: 'smooth', block: 'center' });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/content/page-injections/modules/contextmenu.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | events: {
6 | ENV: {
7 | 'toggled:addon': 'activate'
8 | }
9 | },
10 | active: false,
11 |
12 | autoinit() {
13 | this.activate(true);
14 | },
15 |
16 | activate(on) {
17 | if (on && !this.active) {
18 | this.registerHandler();
19 | this.active = true;
20 | }
21 | else if (!on && this.active) {
22 | this.removeListeners();
23 | this.active = false;
24 | }
25 | },
26 | registerHandler() {
27 | this.addListener('mousedown', e => {
28 | if (e.button === 2) {
29 | if (e.target.classList.contains('textmarker-highlight')) {
30 | _STORE.tmid = e.target.getAttribute('data-tm-id');
31 | }
32 | else _STORE.tmid = '';
33 | }
34 | }, window.document);
35 | }
36 | });
37 |
--------------------------------------------------------------------------------
/src/content/page-injections/modules/marker-popup.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | events: {
6 | ENV: {
7 | 'updated:marker-settings': 'recreate',
8 | 'updated:mark-method-settings': 'update',
9 | 'changed:selection': 'onSelectionChange'
10 | }
11 | },
12 |
13 | handler: null,
14 | appended: false,
15 | height: 0,
16 |
17 | autoinit() {
18 | this.update().then(() => this.create());
19 | },
20 |
21 | recreate() {
22 | this.remove().update().then(() => this.create());
23 | },
24 | update() {
25 | return _STORE.get('settings').then(settings => {
26 | this.active = settings.misc.markmethod === 'popup';
27 | this.markers = settings.markers;
28 | });
29 | },
30 | create() {
31 | const popup = this.el = window.document.createElement('tmpopup');
32 | const bgColorRegExp = /background-color:(#[a-f0-9]{6})/;
33 | let bgColor, colorBtn;
34 | for (let m in this.markers) {
35 | bgColor = this.markers[m].style.match(bgColorRegExp);
36 | if (bgColor) {
37 | colorBtn = window.document.createElement('tmpopupcolor');
38 | popup.appendChild(colorBtn);
39 | colorBtn.style.background = bgColor.pop();
40 | colorBtn.id = 'tmpopupcolor--' + m;
41 | }
42 | }
43 | },
44 | remove() {
45 | if (this.appended) {
46 | window.document.body.removeChild(this.el);
47 | this.el.removeEventListener('mousedown', this.handler, false);
48 | this.appended = false;
49 | }
50 | return this;
51 | },
52 | show() {
53 | const popup = this.el;
54 | const style = popup.style;
55 | let popupHeight;
56 |
57 | if (!this.appended) {
58 | window.document.body.appendChild(popup);
59 | const handler = this.handler = this.onMousedown.bind(this);
60 | popup.addEventListener('mousedown', handler, false);
61 | this.appended = true;
62 |
63 | const selectionPosition = window.getSelection().getRangeAt(0).getBoundingClientRect();
64 |
65 | style.top = selectionPosition.top - selectionPosition.height + window.scrollY + 'px';
66 | style.left = selectionPosition.left + window.scrollX + 'px';
67 |
68 | popupHeight = popup.offsetHeight;
69 |
70 | style.top = Math.max(0, parseInt(style.top) - popupHeight) + 'px';
71 | }
72 | else if ((popupHeight = popup.offsetHeight) !== this.height) {
73 | style.top = parseInt(style.top) + popupHeight - this.height + 'px';
74 | }
75 | this.height = popupHeight;
76 | },
77 | onSelectionChange(selected) {
78 | if (this.active) {
79 | if (!selected) this.remove();
80 | else this.show();
81 | }
82 | else if (this.appended) {
83 | this.remove();
84 | }
85 | },
86 | onMousedown(e) {
87 | e.preventDefault();
88 | e.stopPropagation();
89 | const el = e.target;
90 | if (el.nodeName === 'TMPOPUPCOLOR') {
91 | this.emit('clicked:popup-marker', el.id.split('--').pop());
92 | }
93 | }
94 | });
95 |
--------------------------------------------------------------------------------
/src/content/page-injections/modules/notes.js:
--------------------------------------------------------------------------------
1 | import { _MODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 | import _NOTE from './note'
4 |
5 | new _MODULE({
6 | events: {
7 | ENV: {
8 | 'add:note': 'addAndShow',
9 | 'removed:note': 'removeNoteStorage',
10 | 'restore:notes': 'restore',
11 | 'removed:mark': 'removeNote',
12 | 'toggle:notes': 'toggleAll',
13 | 'sidebar:toggle-notes': 'toggleAll',
14 | 'updated:misc-settings': 'updateStyle',
15 | 'start:drag': 'startDraggingNote',
16 | 'opened:sidebar': 'sendNotesState',
17 | 'finished:all-restorations': 'report'
18 | }
19 | },
20 |
21 | notes: {},
22 | toggle: null,
23 | dragHandler: null,
24 | dragStopHandler: null,
25 |
26 | autoinit() {
27 | this.updateStyle();
28 | },
29 | add(mark, color) {
30 | const note = this.notes[mark.id];
31 | if (note) return note;
32 | if (!_STORE.restoring) this.emit('added:note');
33 | return this.notes[mark.id] = new _NOTE(mark, color);
34 | },
35 | restore(marks) {
36 | for (let mark of marks) {
37 | if (mark.keyData.note) {
38 | this.add(mark);
39 | }
40 | }
41 | },
42 | addAndShow(mark, color) {
43 | this.add(mark, color).show();
44 | },
45 | removeNoteStorage(id) {
46 | delete this.notes[id];
47 | if (this.isEmpty(this.notes)) this.emit('removed:last-note');
48 | },
49 | removeNote(id) {
50 | if (this.notes[id]) this.notes[id].remove();
51 | },
52 | toggleAll() {
53 | if (!this.notes) return;
54 | const notes = this.notes;
55 | let meth = window.document.getElementsByTagName('tmnote').length ? 'hide' : 'show',
56 | condition = meth === 'hide' ? true : false,
57 | note;
58 | for (let n in notes) {
59 | note = notes[n];
60 | if (note.visible === condition) {
61 | note[meth]();
62 | }
63 | }
64 | },
65 | updateStyle() {
66 | const bodyClasses = window.document.body.classList;
67 | _STORE.get('settings').then(settings => {
68 | if (settings && settings.misc) {
69 | if (settings.misc.notetransp) bodyClasses.add('tmnotes--0_8');
70 | else bodyClasses.remove('tmnotes--0_8');
71 | if (settings.misc.noteplainview) bodyClasses.add('tmnotes--plain-view');
72 | else bodyClasses.remove('tmnotes--plain-view');
73 | _STORE.noteFontSize = settings.misc.notefontsize || 12;
74 | }
75 | });
76 | },
77 | isEmpty(obj) {
78 | return !Object.keys(obj).length;
79 | },
80 | startDraggingNote(note) {
81 | const dragHandler = this.dragHandler = (e) => this.emitDragEvent(note, e);
82 | const dragStopHandler = this.dragStopHandler = (e) => this.stopDraggingNote(note, e);
83 | const doc = window.document;
84 | doc.addEventListener('mousemove', dragHandler, false);
85 | doc.addEventListener('mouseup', dragStopHandler, false);
86 | doc.addEventListener('touchmove', dragHandler, false);
87 | doc.addEventListener('touchend', dragStopHandler, false);
88 | },
89 | stopDraggingNote(note, e) {
90 | const doc = window.document;
91 | doc.removeEventListener('mousemove', this.dragHandler, false);
92 | doc.removeEventListener('mouseup', this.dragStopHandler, false);
93 | doc.removeEventListener('touchmove', this.dragHandler, false);
94 | doc.removeEventListener('touchend', this.dragStopHandler, false);
95 |
96 | this.emit('dragstop:note', note, e);
97 | },
98 | emitDragEvent(note, e) {
99 | e.preventDefault();
100 | this.emit('drag:note', note, e);
101 | },
102 | sendNotesState(info) {
103 | this.emit('notes-state', !this.isEmpty(this.notes), info);
104 | },
105 | report() {
106 | this.isEmpty(this.notes) || this.emit('added:note');
107 | }
108 | });
109 |
--------------------------------------------------------------------------------
/src/content/page-injections/modules/page.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: window.document,
6 | autoPause: true,
7 | events: {
8 | ENV: {
9 | 'started:restorations': 'removeListeners',
10 | 'completed:restoration-process': 'addListeners'
11 | },
12 | DOM: {
13 | keydown: {
14 | '*': 'delegate'
15 | },
16 | selectionchange: {
17 | '*': 'onSelectionChange'
18 | }
19 | }
20 | },
21 |
22 | selectionCollapsed: true,
23 | shiftSensitiveKeys: [13, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 171, 173],
24 | keyCodeMap: {
25 | '13': 'Enter',
26 | '48': '0', '49': '1', '50': '2', '51': '3', '52': '4',
27 | '53': '5', '54': '6', '55': '7', '56': '8', '57': '9',
28 | '171': '+', '173': '-'
29 | },
30 |
31 | isEditable(el) {
32 | const name = el.tagName;
33 |
34 | return (name === 'TEXTAREA' || name === 'INPUT' || el.contentEditable === 'true');
35 | },
36 | delegate(e) {
37 | let key = e.key.toLowerCase();
38 | const keyCode = e.keyCode,
39 | modKey = (e.metaKey || e.ctrlKey || e.altKey || e.shiftKey),
40 | arrowKeys = ['arrowdown', 'arrowup'],
41 | lockedActions = ['b', 's', 'y', 'z', 'd'],
42 | functionKeys = lockedActions.concat(arrowKeys).concat(['c', 'n']),
43 | defaultMarkers = ['m', '2', '3'];
44 |
45 | if (_STORE.locked && lockedActions.includes(key)) return true;
46 |
47 | if (this.shiftSensitiveKeys.includes(keyCode)) key = this.keyCodeMap[keyCode];
48 |
49 | if (!functionKeys.includes(key) && window.getSelection().isCollapsed) return true;
50 |
51 | if (this.isEditable(e.target)) return true;
52 |
53 | _STORE.get('settings').then(settings => {
54 |
55 | if (!settings) return true;
56 |
57 | const origKey = key;
58 | const markers = settings.markers;
59 | const shortcuts = settings.shortcuts;
60 | const isMarkerKey = markers[key];
61 | const isCustomMarkerKey = isMarkerKey && !defaultMarkers.includes(key);
62 | if (isCustomMarkerKey) key = 'cm';
63 | const setting = shortcuts[key];
64 |
65 | if (!setting) return true;
66 |
67 | if (!modKey) {
68 | if (key === 'w' && !setting[0] && setting[1]) {
69 | this.emit('lookup:word', window.getSelection().toString());
70 | }
71 | else if (isMarkerKey && !setting[0] && setting[1]) {
72 | this.emit('pressed:marker-key', e, origKey);
73 | }
74 | } else {
75 | if (!setting[1]) return true;
76 |
77 | const shortcut = setting[0].split('-');
78 | const s1 = shortcut[0];
79 | const s2 = shortcut[1];
80 |
81 | if (!e[s1] || (s2 && !e[s2])) return true;
82 |
83 | if (key === 'w') this.emit('lookup:word', window.getSelection().toString());
84 |
85 | else if (isMarkerKey) {
86 | this.emit('pressed:marker-key', e, origKey);
87 | }
88 | else this.emit('pressed:hotkey', key)
89 | }
90 | });
91 | },
92 | onSelectionChange(e) {
93 | const selectionCollapsed = window.getSelection().isCollapsed;
94 | if (this.selectionCollapsed !== selectionCollapsed) {
95 | this.emit('changed:selection', !selectionCollapsed);
96 | this.selectionCollapsed = selectionCollapsed;
97 | }
98 | }
99 | });
100 |
--------------------------------------------------------------------------------
/src/content/page-injections/port.js:
--------------------------------------------------------------------------------
1 | import { _PORT } from '../_shared/utils'
2 |
3 | export default new _PORT({
4 | name: 'injection',
5 | type: 'content',
6 | events: {
7 | ONEOFF: [
8 | 'copy:marks',
9 | 'save:entry?',
10 | 'update:entry?',
11 | 'lookup:word',
12 | 'error:browser-console',
13 | 'changed:selection',
14 | 'unsaved-changes',
15 | 'clicked:mark',
16 | 'activated:mark',
17 | 'added:bookmark',
18 | 'removed:bookmark',
19 | 'added:note',
20 | 'removed:last-note',
21 | 'page-state',
22 | 'notes-state',
23 | 'visually-ordered:marks',
24 | 'fetch:entries',
25 |
26 | // @RESTORER
27 | 'finished:restoration',
28 | 'failed:restoration',
29 | 'succeeded:restoration',
30 | 'failed:restore-range',
31 | 'canceled:save-after-canceled-restoration'
32 | ]
33 | }
34 | });
35 |
--------------------------------------------------------------------------------
/src/content/page-injections/sass/components/_copyshop.scss:
--------------------------------------------------------------------------------
1 | copyshop {
2 | all: unset !important;
3 | display: block !important;
4 | position: absolute !important;
5 | bottom: -100% !important;
6 | left: -100% !important;
7 | }
8 |
--------------------------------------------------------------------------------
/src/content/page-injections/sass/components/_marker-popup.scss:
--------------------------------------------------------------------------------
1 | tmpopup {
2 | position: absolute;
3 | display: block;
4 | z-index: $layer--max;
5 | background: $color-bg--dark;
6 | border-radius: 3px;
7 | box-sizing: border-box !important;
8 | padding: 2px 4px;
9 | }
10 | tmpopupcolor {
11 | display: inline-block;
12 | width: 16px;
13 | height: 16px;
14 | border-radius: 50%;
15 | margin: 4px 2px 0;
16 | cursor: pointer;
17 |
18 | &:hover {
19 | box-shadow: 0 0 1px $color-bg--light;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/content/page-injections/sass/components/_marks.scss:
--------------------------------------------------------------------------------
1 | tm {display: inline !important;}
2 | span.textmarker-highlight {
3 | all: inherit;
4 | background-image: none !important;
5 | display: inline !important;
6 | position: static !important;
7 | padding: 0 !important;
8 | margin: 0 !important;
9 | border: 0;
10 | outline: 0 !important;
11 | float: none !important;
12 | cursor: pointer;
13 |
14 | &--active {
15 | border-top: 4px groove #2ba5b7 !important;
16 | border-bottom: 4px groove #2ba5b7 !important;
17 | }
18 | }
19 |
20 | #textmarker-bookmark-anchor {
21 | padding-left: 15px !important;
22 | @include icon('bm2', -2px 0, $important: !important);
23 | }
24 |
--------------------------------------------------------------------------------
/src/content/page-injections/sass/components/_notes.scss:
--------------------------------------------------------------------------------
1 | $colors:
2 | (green, #dfd, #efe, #cfc),
3 | (white, #eee, #fff, #eee),
4 | (yellow, #ffd, #ffe, #ffc),
5 | (orange, #fec, #fed, #feb),
6 | (red, #fdd, #fee, #fcc),
7 | (purple, #edf, #eef, #ecf),
8 | (blue, #cef, #def, #bef),
9 | (turquoise, #c1e9f2, #d5f0f7, #b9e4ec);
10 |
11 | @each $name, $val, $val2, $val3 in $colors {
12 | .tmnote--#{$name} {
13 | tmnotepalette,
14 | tmnoteheader {
15 | background: $val3;
16 | }
17 | tmnoteactions {
18 | background: $val2;
19 | &:hover {
20 | background: $val;
21 | }
22 | }
23 | textarea {
24 | background-image: linear-gradient($val2, $val) !important;
25 | }
26 | }
27 | .tmnotecolor--#{$name} {
28 | border-color: $val;
29 | background-image: linear-gradient($val2, $val);
30 | }
31 | }
32 |
33 | tmnote {
34 | display: none;
35 | position: absolute;
36 | font-family: Verdana, sans-serif;
37 | font-size: 12px;
38 | color: $color-grey--dark;
39 | line-height: 1.4;
40 | margin: 0;
41 | z-index: $layer--max;
42 | box-sizing: border-box !important;
43 |
44 | .tmnotes--0_8 & {
45 | opacity: 0.8;
46 | }
47 | &:hover {
48 | opacity: 1 !important;
49 | }
50 | }
51 | tmnoteheader {
52 | height: 22px;
53 | position: relative;
54 | display: block;
55 | z-index: 1;
56 | cursor: move;
57 |
58 | .tmnotes--plain-view & {
59 | display: none;
60 | }
61 | tmnote:hover & {
62 | display: block !important;
63 | }
64 | }
65 | tmnotepalette {
66 | position: absolute;
67 | top: 0;
68 | left: 0;
69 | z-index: 1;
70 | width: 100%;
71 | padding: 4px 0 6px;
72 | text-align: center;
73 | box-sizing: border-box;
74 | border-bottom: 1px solid $color-border--light;
75 | }
76 | tmnotecolor {
77 | width: 22px;
78 | height: 14px;
79 | position: relative;
80 | display: inline-block;
81 | margin: 0 7px;
82 | border-top: 2px solid;
83 | cursor: pointer;
84 | box-shadow: 0 1px 3px $color-shadow--lighter;
85 | &:hover {
86 | top: -1px;
87 | box-shadow: 0 1px 3px $color-shadow--light;
88 | filter: brightness(1.02);
89 | }
90 | }
91 | tmnoteactions {
92 | position: absolute;
93 | display: block;
94 | right: -20px;
95 | background: #ffe;
96 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
97 | border-radius: 0 2px 2px 2px;
98 | box-sizing: border-box !important;
99 |
100 | .tmnotes--plain-view & {
101 | display: none;
102 | }
103 | tmnote:hover & {
104 | display: block !important;
105 | }
106 | }
107 | tmnotedelete,
108 | tmnoteminimize,
109 | tmnotecustomize {
110 | display: block;
111 | font-size: 14px;
112 | border-radius: 2px;
113 | cursor: pointer;
114 | color: $color-text--disabled;
115 | line-height: 1;
116 | height: 20px;
117 | box-sizing: border-box !important;
118 |
119 | &:hover {
120 | color: $color-text--dark;
121 | }
122 | }
123 | tmnotedelete {
124 | top: 40px;
125 | font-size: 10px;
126 | font-weight: bold;
127 | padding: 5px 6px 0 6px;
128 | }
129 | tmnoteminimize {
130 | top: 60px;
131 | font-size: 18px;
132 | padding: 0px 4px 0 5px;
133 | }
134 | tmnotecustomize {
135 | top: 20px;
136 | font-size: 16px;
137 | padding: 1px 3px 2px 5px;
138 | }
139 | body > tmnote {
140 | > textarea[data-tm-note] {
141 | display: block !important;
142 | padding: 10px !important;
143 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5) !important;
144 | position: relative !important;
145 | width: 300px;
146 | min-height: 3em !important;
147 | font-family: Verdana, sans-serif !important;
148 | font-size: 12px !important;
149 | border: 0 !important;
150 | margin: 0 !important;
151 | box-sizing: border-box !important;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/content/page-injections/sass/components/_tmui.scss:
--------------------------------------------------------------------------------
1 | tmui {
2 | display: block;
3 | z-index: $layer--max;
4 | position: fixed;
5 | background: $color-bg--light;
6 | border: 1px solid $color-border--light;
7 | padding-top: 3px;
8 | box-sizing: border-box !important;
9 | }
10 | tmbm, tmnotestoggle {
11 | display: inline-block;
12 | cursor: pointer;
13 | margin-left: 3px;
14 | margin-right: 3px;
15 | vertical-align: top;
16 | width: 12px;
17 | filter: drop-shadow(2px 2px 2px rgba(0, 0, 0, 0.25));
18 | }
19 | tmbm {
20 | @include icon('bm', center);
21 | height: 18px;
22 | }
23 | tmbm:hover {
24 | filter: brightness(1.15);
25 | }
26 | tmnotestoggle {
27 | @include icon('note', center);
28 | height: 16px;
29 | }
30 | tmnotestoggle:hover {
31 | filter: brightness(1.15);
32 | }
33 |
--------------------------------------------------------------------------------
/src/content/page-injections/sass/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../_shared/sass/vars';
2 | @import '../../_shared/sass/icons';
3 |
4 | @import './components/marks';
5 | @import './components/notes';
6 | @import './components/tmui';
7 | @import './components/marker-popup';
8 | @import './components/copyshop';
9 |
--------------------------------------------------------------------------------
/src/content/sidebar/_store.js:
--------------------------------------------------------------------------------
1 | import { _STORE } from './../_shared/utils'
2 |
3 | export default new _STORE({
4 | events: {
5 | ENV: {
6 | 'toggled:sync': 'onToggledSync',
7 | 'saved:entry': 'updateEntry',
8 | 'entry:found': 'updateEntryOnFound',
9 | 'entry:found-for-tab': 'updateEntry',
10 | 'entry:deleted-for-tab': 'resume'
11 | }
12 | },
13 |
14 | env: 'sidebar',
15 | entry: null,
16 | locked: false,
17 |
18 | updateEntry(entry) {
19 | if (entry) {
20 | const isArr = Array.isArray(entry);
21 | const currentEntry = !!this.entry;
22 |
23 | this.locked = this.locked || isArr || entry.locked;
24 |
25 | if (!this.locked || isArr) {
26 | this.entry = entry;
27 | }
28 | else if (this.locked && !isArr) {
29 | if (this.entry && Array.isArray(this.entry)) this.entry.push(entry);
30 | else this.entry = [entry];
31 | }
32 |
33 | if (currentEntry) this.emit('updated:stored-entry', this.entry);
34 | else this.emit('stored:entry', this.entry);
35 | }
36 | },
37 | updateEntryOnFound(entry) {
38 | if (entry) {
39 | this.updateEntry(entry);
40 | if (!Array.isArray(entry)) {
41 | this.emit('initially-stored:entry', entry);
42 | }
43 | }
44 | },
45 |
46 | resume() {
47 | this.entry = null;
48 | this.locked = false;
49 | this.emit('removed:entry');
50 | },
51 |
52 |
53 | _get_mode() {
54 | return browser.storage[this.area_settings].get().then(storage => {
55 | if (!storage || !storage.settings || storage.settings.addon.active) return true;
56 | return false;
57 | });
58 | },
59 | _get_autosave() {
60 | return browser.storage[this.area_settings].get().then(storage => {
61 | if (!storage || !storage.settings) return false;
62 | return storage.settings.history.autosave;
63 | });
64 | },
65 | _get_settings() {
66 | return browser.storage[this.area_settings].get().then(storage => storage.settings);
67 | },
68 | _get_markers() {
69 | return browser.storage[this.area_settings].get().then(storage => storage.settings.markers);
70 | },
71 | _get_pagenotes() {
72 | return browser.storage[this.area_settings].get().then(storage => storage.pagenotes || null);
73 | }
74 | });
75 |
--------------------------------------------------------------------------------
/src/content/sidebar/index.js:
--------------------------------------------------------------------------------
1 | import './sass/index.scss'
2 |
3 | import _ERRORTRACKER from './../_shared/utils'
4 | import { _MODULE } from './../_shared/utils'
5 | import { _L10N } from './../_shared/utils'
6 | import _PORT from './port'
7 | import _STORE from './_store'
8 |
9 | import './modules/themes'
10 | import './modules/tabs'
11 | import './modules/header'
12 | import './modules/meta-infos'
13 | import './modules/tags'
14 | import './modules/page-notes'
15 | import './modules/markers'
16 | import './modules/history-actions'
17 | import './modules/mark-actions'
18 | import './modules/page-actions'
19 | import './modules/marks'
20 | import './modules/links'
21 |
22 | _L10N();
23 |
24 | new _MODULE({
25 | events: {
26 | ENV: {
27 | 'started:app': 'onStart',
28 | 'toggled:addon': 'power',
29 | 'stored:entry': 'toggle',
30 | 'updated:stored-entry': 'toggle',
31 | 'initially-stored:entry': 'toggle'
32 | }
33 | },
34 |
35 | autoinit() {
36 | this.emit('opened:sidebar', { tab: 'active' });
37 | },
38 |
39 | power(on) {
40 | const placeholder = document.getElementById('textmarker-sidebar--paused');
41 | const content = document.getElementById('textmarker-sidebar');
42 |
43 | if (on) {
44 | placeholder.classList.add('u-display--none');
45 | content.classList.remove('u-display--none');
46 | } else {
47 | placeholder.classList.remove('u-display--none');
48 | content.classList.add('u-display--none');
49 | }
50 | },
51 | onStart() {
52 | _STORE.get('mode').then(mode => this.power(mode));
53 | },
54 | toggle(entry) {
55 | const sidebar = document.getElementById('textmarker-sidebar');
56 | if (entry && _STORE.locked) {
57 | sidebar.classList.add('textmarker-sidebar--locked');
58 | } else {
59 | sidebar.classList.remove('textmarker-sidebar--locked');
60 | }
61 | }
62 | })
63 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/header.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('header'),
6 | events: {
7 | ENV: {
8 | 'stored:entry': 'render',
9 | 'updated:stored-entry': 'render'
10 | }
11 | },
12 |
13 | render(entry) {
14 | const header = this.el;
15 |
16 | if (!entry) header.classList.add('u-display--none');
17 | else if (Array.isArray(entry)) return;
18 |
19 | header.classList.remove('u-display--none');
20 | this.updateName(entry.name);
21 | },
22 |
23 | updateName(name) {
24 | const el = this.el.getElementsByClassName('header__name')[0];
25 | el.innerText = name;
26 | el.title = name;
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/history-actions.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('history-actions'),
6 | events: {
7 | ENV: {
8 | 'started:app': 'toggleSave',
9 | 'updated:settings': 'toggleSave',
10 | 'set-areas-after-sync-change': 'toggleSave',
11 | 'updated:entry-on-save': 'deactivateSave',
12 | 'saved:entry': 'deactivateSave',
13 | 'unsaved-changes': 'activateSave',
14 | 'finished:restoration': 'activateRetry',
15 | 'update:entry?': 'deactivateRetry',
16 | 'stored:entry': 'updateImmut',
17 | 'page-state': 'onPageState',
18 | 'initially-stored:entry': 'updateImmut'
19 | },
20 | DOM: {
21 | click: {
22 | '#page-action--retry': 'retryRestoration',
23 | '#page-action--save': 'save',
24 | '#page-action--delete': 'onDeleteRequest',
25 | '.switch': 'toggleImmut'
26 | }
27 | }
28 | },
29 |
30 | retryBtnShown: false,
31 | saveBtn: document.getElementById('page-action--save'),
32 | retryBtn: document.getElementById('page-action--retry'),
33 |
34 | autoinit() {
35 | this.toggleSave();
36 | },
37 |
38 | save() {
39 | this.emit('sidebar:save-changes', { tab: 'active' });
40 | },
41 | retryRestoration() {
42 | this.emit('sidebar:retry-restoration', { tab: 'active' });
43 | this.deactivateRetry();
44 | },
45 | onDeleteRequest() {
46 | const confirmed = window.confirm(browser.i18n.getMessage('sb514'));
47 | if (confirmed) {
48 | if (_STORE.entry) {
49 | this.emit('sidebar:delete-entry', [_STORE.entry.name], { tab: 'active' });
50 | } else {
51 | window.alert(browser.i18n.getMessage('sb515'));
52 | }
53 | }
54 | },
55 | toggleSave() {
56 | _STORE.get('autosave').then(autosave => {
57 | const meth = autosave ? 'add' : 'remove';
58 | this.saveBtn.classList[meth]('u-display--none');
59 | });
60 | },
61 | activateSave(on = true) {
62 | if (on) this.saveBtn.removeAttribute('disabled');
63 | else this.saveBtn.setAttribute('disabled', true);
64 | },
65 | deactivateSave() {
66 | this.activateSave(false);
67 | },
68 | activateRetry() {
69 | if (!this.retryBtnShown) {
70 | this.retryBtn.classList.remove('u-display--none');
71 | this.retryBtnShown = true;
72 | }
73 | },
74 | deactivateRetry() {
75 | if (this.retryBtnShown) {
76 | this.retryBtn.classList.add('u-display--none');
77 | this.retryBtnShown = false;
78 | }
79 | },
80 | toggleImmut(e, el) {
81 | el = el.classList.contains('switch--immut') ? el : el.parentNode;
82 | el.classList.toggle('active');
83 | this.emit('sidebar:immut', el.classList.contains('active'), { tab: 'active' });
84 | },
85 | updateImmut(entry) {
86 | if (entry) {
87 | const meth = entry.immut ? 'add' : 'remove';
88 | document.getElementById('page-action--immut').classList[meth]('active');
89 | document.getElementById('switch-box').classList.remove('u-display--none');
90 | } else {
91 | document.getElementById('switch-box').classList.add('u-display--none');
92 | }
93 | },
94 | onPageState(state) {
95 | if (state.retryActive) this.activateRetry();
96 | }
97 | });
98 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/links.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 |
3 | new _DOMMODULE({
4 | el: document.getElementById('tab--links'),
5 | events: {
6 | DOM: {
7 | click: {
8 | '.link': 'link',
9 | '.link__icon': 'link',
10 | '.link__text': 'link'
11 | }
12 | }
13 | },
14 |
15 | link(e, el) {
16 | el = el.classList.contains('link') ? el : el.parentNode;
17 | this.emit('open:addon-page(sb)', el.getAttribute('data-id'));
18 | }
19 | });
20 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/mark-actions.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('mark-actions'),
6 | events: {
7 | ENV: {
8 | 'clicked:mark': 'activate',
9 | 'activated:mark': 'activate'
10 | },
11 | DOM: {
12 | click: {
13 | '.action-button--mark': 'markAction'
14 | }
15 | }
16 | },
17 | frame: 0,
18 | buttons: [],
19 |
20 | autoinit() {
21 | this.buttons = Array.from(this.el.getElementsByTagName('button'));
22 | },
23 | markAction(e, el) {
24 | if (el.hasAttribute('disabled')) return;
25 | const action = el.getAttribute('data-action');
26 | if (action === 'copy') {
27 | browser.permissions.contains({ permissions: ['clipboardWrite'] }).then(granted => {
28 | this.emit('sidebar:' + el.getAttribute('data-action'), granted, null, { tab: 'active', frameId: this.frame });
29 | });
30 | } else {
31 | this.emit('sidebar:' + el.getAttribute('data-action'), null, null, { tab: 'active', frameId: this.frame });
32 | }
33 | },
34 | activate(markInfos, sender) {
35 | this.frame = sender && sender.frameId ? sender.frameId : 0;
36 |
37 | this.buttons.forEach(btn => {
38 | let type = btn.getAttribute('data-action');
39 | if (
40 | type === 'copy' ||
41 | type === 'delete-highlight' ||
42 | (typeof markInfos[type] === 'boolean' && !markInfos[type]) ||
43 | (type === 'delete-bookmark' && markInfos.bookmark)
44 | ) {
45 | btn.removeAttribute('disabled');
46 | btn.parentNode.classList.remove('disabled');
47 | }
48 | });
49 | },
50 | deactivate() {
51 | this.buttons.forEach(btn => {
52 | btn.setAttribute('disabled', true);
53 | btn.parentNode.classList.add('disabled');
54 | });
55 | }
56 | });
57 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/meta-infos.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('tab--meta'),
6 | events: {
7 | ENV: {
8 | 'stored:entry': 'render',
9 | 'updated:stored-entry': 'render'
10 | }
11 | },
12 |
13 | render(entry) {
14 | if (entry && !Array.isArray(entry)) {
15 | const yes = browser.i18n.getMessage('yes');
16 | const no = browser.i18n.getMessage('no');
17 |
18 | document.getElementById('meta__number-marks').innerText = entry.marks.length;
19 | document.getElementById('meta__created').innerText = this.optimizeDateString(new Date(entry.first).toLocaleString());
20 | document.getElementById('meta__last-modified').innerText = this.optimizeDateString(new Date(entry.last).toLocaleString());
21 |
22 | ['synced', 'immut']
23 | .forEach(field => document.getElementById('meta__' + field).innerText = entry[field] ? yes : no);
24 | }
25 | },
26 |
27 | optimizeDateString(date) {
28 | return (date
29 | .replace(/^(\d{1})(\D{1})/, (m, p, q)=> '0' + p + q)
30 | .replace(/(\D{1})(\d{1}\D{1})/g, (m, p, q) => p + '0' + q));
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/page-actions.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('page-actions'),
6 | events: {
7 | ENV: {
8 | 'added:bookmark': 'activateBookmark',
9 | 'removed:bookmark': 'deactivateBookmark',
10 | 'added:note': 'activateNotes',
11 | 'removed:last-note': 'deactivateNotes',
12 | 'page-state': 'onPageState',
13 | 'notes-state': 'onNotesState'
14 | },
15 | DOM: {
16 | click: {
17 | '.action-button--page': 'pageAction'
18 | }
19 | }
20 | },
21 |
22 | activateBookmark() {
23 | this.activate('scroll', true);
24 | },
25 | deactivateBookmark() {
26 | this.activate('scroll', false);
27 | },
28 | activateNotes() {
29 | this.activate('notes', true);
30 | },
31 | deactivateNotes() {
32 | this.activate('notes', false);
33 | },
34 | activate(type, on) {
35 | const btn = document.getElementById('page-action--' + type);
36 | if (on) {
37 | btn.removeAttribute('disabled');
38 | btn.parentNode.classList.remove('disabled');
39 | }
40 | else {
41 | btn.setAttribute('disabled', true);
42 | btn.parentNode.classList.add('disabled');
43 | }
44 | },
45 | pageAction(e, el) {
46 | this.emit('sidebar:' + el.getAttribute('data-action'), { tab: 'active' });
47 | },
48 | onPageState(state) {
49 | if (state.bookmark) this.activateBookmark();
50 | },
51 | onNotesState(notes) {
52 | if (notes) this.activateNotes();
53 | }
54 | });
55 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/tabs.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('textmarker-sidebar'),
6 | events: {
7 | ENV: {
8 | 'stored:entry': 'showEntrySpecificTabs'
9 | },
10 | DOM: {
11 | click: {
12 | '.tab__title': 'toggle',
13 | '.tab__name': 'toggle',
14 | '.tab__toggle': 'toggle'
15 | }
16 | }
17 | },
18 |
19 | tabs: {},
20 |
21 | autoinit() {
22 | _STORE.get('settings').then(settings => {
23 | if (!settings || !settings.sb) return;
24 | const tabSettings = settings.sb.tabs;
25 | for (let tab in tabSettings) {
26 | this.tabs[tab] = document.getElementById('tab--' + tab);
27 | if (tabSettings[tab].unfolded) this.open(tab);
28 | else this.close(tab);
29 | }
30 | });
31 | },
32 |
33 | open(tab) {
34 | this.tabs[tab].classList.remove('tab--folded');
35 | },
36 | close(tab) {
37 | this.tabs[tab].classList.add('tab--folded');
38 | },
39 | toggle(e, el) {
40 | el = el.nodeName === 'H2' ? el : el.parentNode;
41 | const id = el.getAttribute('data-target');
42 | const tab = id.split('--').pop();
43 | const tabEl = document.getElementById(id);
44 | tabEl.classList.toggle('tab--folded');
45 | this.emit('toggled:sidebar-tab', tab, !tabEl.classList.contains('tab--folded'));
46 | },
47 | showEntrySpecificTabs() {
48 | Array.from(document.getElementsByClassName('tab--entry'))
49 | .forEach(tab => tab.classList.remove('u-display--none'));
50 | }
51 | });
52 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/tags.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('tab--tags'),
6 | events: {
7 | ENV: {
8 | 'stored:entry': 'render'
9 | },
10 | DOM: {
11 | click: {
12 | '.tags__remove': 'removeTag',
13 | '.add-tag': 'addTag'
14 | }
15 | }
16 | },
17 |
18 | render(entry) {
19 | if (entry && !Array.isArray(entry)) {
20 | const tags = entry.tag ? entry.tag.split(' ') : [];
21 | document.getElementById('tags').innerText = '';
22 | tags.forEach(tag => this.renderTag(tag));
23 | }
24 | },
25 | renderTag(tag) {
26 | const container = document.getElementById('tags');
27 | const el = document.createElement('div');
28 | const del = document.createElement('span');
29 | const x = document.createTextNode(String.fromCharCode(10005));
30 | el.className = 'tags__item u-overflow--ellipsis';
31 | del.className = 'tags__remove';
32 | del.setAttribute('data-tag', tag);
33 | el.innerText = tag;
34 | del.appendChild(x);
35 | el.appendChild(del);
36 | container.appendChild(el);
37 | },
38 | addTag() {
39 | const inp = document.getElementById('new-tag');
40 | let tag = inp.value.trim();
41 | if (!tag) return;
42 | this.emit('add:tag', tag, _STORE.entry);
43 | inp.value = '';
44 | tag.split(' ').forEach(tag => this.renderTag(tag));
45 | },
46 | removeTag(e, el) {
47 | this.emit('remove:tag', el.getAttribute('data-tag'), _STORE.entry);
48 | el.parentNode.parentNode.removeChild(el.parentNode);
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/src/content/sidebar/modules/themes.js:
--------------------------------------------------------------------------------
1 | import { _DOMMODULE } from './../../_shared/utils'
2 | import _STORE from './../_store'
3 |
4 | new _DOMMODULE({
5 | el: document.getElementById('tab--themes'),
6 | events: {
7 | DOM: {
8 | change: {
9 | '.theme-opt': 'onChange'
10 | }
11 | }
12 | },
13 |
14 | theme: 'default',
15 |
16 | autoinit() {
17 | _STORE.get('settings').then(settings => {
18 | const theme = settings && settings.sb && settings.sb.theme ? settings.sb.theme : 'default';
19 | document.getElementById(`theme--${theme}`).checked = true;
20 | this.update(theme);
21 | });
22 | },
23 |
24 | onChange(e, el) {
25 | const theme = el.getAttribute('data-value');
26 | this.emit('changed:sidebar-theme', theme);
27 | this.update(theme);
28 | },
29 | update(theme) {
30 | this.theme = theme;
31 | document.getElementById('textmarker-sidebar').className = `textmarker-sidebar--${theme}`;
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/src/content/sidebar/port.js:
--------------------------------------------------------------------------------
1 | import { _PRIVPORT } from './../_shared/utils'
2 |
3 | export default new _PRIVPORT({
4 | name: 'sidebar',
5 | type: 'privileged',
6 | id: Math.random().toString().slice(2, 16),
7 | events: {
8 | CONNECTION: [
9 | 'change:bg-setting',
10 | 'error:browser-console',
11 | 'sidebar:highlight',
12 | 'sidebar:delete-highlight',
13 | 'sidebar:bookmark',
14 | 'sidebar:delete-bookmark',
15 | 'sidebar:note',
16 | 'sidebar:immut',
17 | 'sidebar:save-changes',
18 | 'sidebar:retry-restoration',
19 | 'sidebar:delete-entry',
20 | 'sidebar:undo',
21 | 'sidebar:redo',
22 | 'sidebar:copy',
23 | 'sidebar:scroll-to-bookmark',
24 | 'sidebar:toggle-notes',
25 | 'sidebar:next-mark',
26 | 'remove:tag',
27 | 'add:tag',
28 | 'open:addon-page(sb)',
29 | 'opened:sidebar',
30 | 'updated:page-note',
31 | 'toggled:sidebar-tab',
32 | 'sidebar:selected-marker',
33 | 'changed:sidebar-theme'
34 | ]
35 | }
36 | })
37 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/_buttons.scss:
--------------------------------------------------------------------------------
1 | button {
2 | border: 1px solid $color-border;
3 | border-radius: 50%;
4 | transition: background 0.5s;
5 | float: right;
6 | width: 24px;
7 | height: 24px;
8 | cursor: pointer;
9 | background-color: transparent;
10 | background-repeat: no-repeat;
11 | background-position: center, 0 0;
12 | opacity: 0.75;
13 |
14 | &:hover {
15 | opacity: 1;
16 | }
17 | &:last-child {
18 | margin: 0;
19 | }
20 | &[disabled] {
21 | opacity: 0.2 !important;
22 | cursor: default;
23 | }
24 |
25 | &.action-button--add {
26 | width: 20px;
27 | height: 20px;
28 | font-size: 16px;
29 | padding: 0;
30 | position: relative;
31 | top: 2px;
32 | background: $gradient--button;
33 | color: $color-text--grey;
34 |
35 | &:hover {
36 | background: $gradient--button-hover;
37 | }
38 |
39 | span {
40 | position: absolute;
41 | top: -1px;
42 | left: 3px;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/_form-elements.scss:
--------------------------------------------------------------------------------
1 | ::-moz-placeholder,
2 | :placeholder-shown {
3 | color: $color-text--disabled;
4 | }
5 | select, input {
6 | font-family: inherit !important;
7 | }
8 | select {
9 | -moz-appearance: none;
10 | height: 24px;
11 | color: $color-text--grey;
12 | float: left;
13 | width: auto;
14 | border: 0;
15 | }
16 | input[type="text"] {
17 | border: 0;
18 | box-shadow: 0 0 3px -1px #c8c8c8 inset;
19 | color: inherit;
20 | padding: 0;
21 | height: 24px;
22 | width: calc(100% - 27px);
23 | padding: 0 4px;
24 | }
25 | input[type="radio"] {
26 | display: none;
27 | }
28 | label.fake-rb {
29 | width: auto;
30 | }
31 | input[type="radio"] + label span {
32 | border-radius: 50%;
33 | line-height: 1;
34 | vertical-align: -2px;
35 | }
36 | input[type="radio"]:checked + label span {
37 | text-indent: 0;
38 | }
39 | label span {
40 | display: inline-block;
41 | line-height: 1.2;
42 | height: 20px;
43 | width: 20px;
44 | text-align: center;
45 | color: $color-text--turquoise;
46 | text-indent: -1000px;
47 | overflow: hidden;
48 | border: 1px solid $color-border;
49 | border-radius: 2px;
50 | vertical-align: -5px;
51 | margin-right: 8px;
52 | }
53 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/_general.scss:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: $font-size--base-small;
3 | color: $color-text--grey;
4 | font-family: inherit;
5 | padding: 0;
6 | }
7 | #header {
8 | margin-bottom: 15px;
9 | }
10 | h1 {
11 | width: 100%;
12 | font-size: 100%;
13 | font-weight: normal;
14 | margin: 0 0 10px;
15 | color: $color-text--turquoise;
16 | }
17 | h2 {
18 | font-size: 100%;
19 | margin: 0 0 8px;
20 | font-weight: normal;
21 | position: relative;
22 | }
23 | h3 {
24 | font-size: 80%;
25 | margin: 0 0 8px;
26 | color: $color-grey--medium;
27 | font-weight: normal;
28 | text-align: right;
29 | }
30 | label {
31 | float: left;
32 | padding-top: 2px;
33 | cursor: pointer;
34 | }
35 | svg {
36 | height: 0;
37 | }
38 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/components/_actions.scss:
--------------------------------------------------------------------------------
1 | .action-button {
2 | width: 28px;
3 | height: 28px;
4 | }
5 |
6 | $action-buttons: (
7 | '#mark-action--delete-highlight' : 'bin',
8 | '#mark-action--bookmark' : 'bm3',
9 | '#mark-action--delete-bookmark': 'del-bm',
10 | '#mark-action--add-note': 'note2',
11 | '#mark-action--copy': 'copy',
12 | '#page-action--delete': 'bin',
13 | '#page-action--save': 'save',
14 | '#page-action--retry': 'retry',
15 | '#page-action--undo': 'undo',
16 | '#page-action--redo': 'redo',
17 | '#page-action--scroll': 'to-bm',
18 | '#page-action--notes': 'toggle-notes',
19 | '#page-action--copy-all': 'copy',
20 | '#nav-action--up': 'up',
21 | '#nav-action--down': 'down',
22 |
23 | '.marker__apply': 'highlight'
24 | );
25 |
26 | @each $selector, $icon in $action-buttons {
27 | #{$selector} {
28 | @include button_icon($icon, $gradient: true);
29 | }
30 | }
31 |
32 | #nav-action--up, #nav-action--down {
33 | background-size: 12px, 100%;
34 | }
35 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/components/_links.scss:
--------------------------------------------------------------------------------
1 | .link {
2 | opacity: .5;
3 |
4 | &__icon {
5 | display: inline-block;
6 | width: 12px;
7 | height: 12px;
8 | top: 2px;
9 | margin-right: 8px;
10 | }
11 | &__text {
12 | display: inline-block;
13 | }
14 | &:hover {
15 | opacity: 1;
16 | }
17 | }
18 |
19 | $links: (
20 | 'settings': 'tools',
21 | 'history': 'clock',
22 | 'help': 'book',
23 | 'troubleshooting': 'debug',
24 | 'contact': 'ghost'
25 | );
26 |
27 | @each $link, $icon in $links {
28 | .link--#{$link} .link__icon {
29 | @include icon(#{$icon}, 0 0, 12px);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/components/_marker.scss:
--------------------------------------------------------------------------------
1 | .marker {
2 | margin-bottom: 8px;
3 | width: 78px;
4 |
5 | .auto & {
6 | width: 85px;
7 | }
8 |
9 | &__cb-box {
10 | float: right;
11 | margin: 0 0 0 10px;
12 | display: none;
13 | .auto & {
14 | display: block;
15 | }
16 | }
17 |
18 | &__apply {
19 | margin: 0 0 0 10px;
20 | border-radius: 0;
21 | .auto & {
22 | display: none;
23 | }
24 | }
25 | &__label {
26 | display: inline-block;
27 | }
28 | &__text {
29 | position: relative;
30 | margin-top: -24px;
31 | left: 10px;
32 | color: $color-text--dark;
33 | font-size: 11px !important;
34 | }
35 | &__color {
36 | padding: 0;
37 | width: 44px;
38 | border: 0;
39 | -moz-appearance: none;
40 | cursor: pointer;
41 |
42 | &[disabled] {
43 | opacity: .2;
44 | cursor: default;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/components/_marks.scss:
--------------------------------------------------------------------------------
1 | #nav-action--up,
2 | #nav-action--down {
3 | width: 20px;
4 | height: 20px;
5 | background-size: 14px;
6 | }
7 |
8 | .mark {
9 | position: relative;
10 | width: 100%;
11 | margin-bottom: 5px;
12 |
13 | &__text-container {
14 | position: relative;
15 | z-index: 2;
16 | background: #fff;
17 | border: 1px solid $color-border--light;
18 | border-radius: 2px 0 0 2px;
19 |
20 | .mark--note & {
21 | width: calc(100% - 16px);
22 | float: left;
23 | }
24 |
25 | .mark--active &,
26 | &:hover {
27 | border: 1px solid $color-border;
28 | }
29 | }
30 |
31 | &__text {
32 | border-left: 2px solid;
33 | padding: 2px 8px 2px 5px;
34 |
35 | &.unfolded {
36 | border-left: 0;
37 | border-top: 1px solid;
38 | padding-bottom: 10px;
39 | text-overflow: inherit;
40 | white-space: normal;
41 | }
42 | }
43 |
44 | &__note-btn {
45 | @include icon('note4');
46 | position: relative;
47 | z-index: 2;
48 | float: right;
49 | width: 16px;
50 | height: 16px;
51 |
52 | &--red {
53 | filter: url(#filter--red);
54 | &:hover {
55 | filter: url(#filter--red) brightness(.75);
56 | }
57 | }
58 | &--orange {
59 | filter: url(#filter--orange);
60 | &:hover {
61 | filter: url(#filter--orange) brightness(.75);
62 | }
63 | }
64 | &--yellow {
65 | filter: url(#filter--yellow);
66 | &:hover {
67 | filter: url(#filter--yellow) brightness(.75);
68 | }
69 | }
70 | &--white {
71 | filter: url(#filter--white);
72 | &:hover {
73 | filter: url(#filter--white) brightness(.75);
74 | }
75 | }
76 | &--green {
77 | filter: url(#filter--green);
78 | &:hover {
79 | filter: url(#filter--green) brightness(.75);
80 | }
81 | }
82 | &--blue {
83 | filter: url(#filter--blue);
84 | &:hover {
85 | filter: url(#filter--blue) brightness(.75);
86 | }
87 | }
88 | &--purple {
89 | filter: url(#filter--purple);
90 | &:hover {
91 | filter: url(#filter--purple) brightness(.75);
92 | }
93 | }
94 | &--turquoise {
95 | filter: url(#filter--turquoise);
96 | &:hover {
97 | filter: url(#filter--turquoise) brightness(.75);
98 | }
99 | }
100 | }
101 |
102 | &__note {
103 | display: none;
104 | position: relative;
105 | top: 2px;
106 | padding: 5px;
107 | margin-left: 3px;
108 | border: 1px solid $color-border--light;
109 |
110 | &.unfolded {
111 | display: block;
112 | }
113 |
114 | &--red {
115 | background: #fee;
116 | }
117 | &--orange {
118 | background: #fed;
119 | }
120 | &--yellow {
121 | background: #ffe;
122 | }
123 | &--white {
124 | background: #f9f9f9;
125 | }
126 | &--green {
127 | background: #efe;
128 | }
129 | &--blue {
130 | background: #def;
131 | }
132 | &--purple {
133 | background: #eef;
134 | }
135 | &--turquoise {
136 | background: #d5f0f7;
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/components/_page-notes.scss:
--------------------------------------------------------------------------------
1 | tmnotesave {
2 | display: none;
3 | position: absolute;
4 | background: #fff;
5 | bottom: -1px;
6 | left: 5px;
7 | border: 1px solid $color-border--light;
8 | border-bottom: 0;
9 | border-radius: 3px 3px 0 0;
10 | width: auto;
11 | padding: 0 4px;
12 | color: $color-text--turquoise;
13 |
14 | tmnote & {
15 | display: block;
16 | }
17 | tmnote.tmnote--min & {
18 | display: none;
19 | }
20 | }
21 |
22 | tmnote {
23 | display: block;
24 | position: relative;
25 | width: 100%;
26 | border: 1px solid $color-border--light !important;
27 | margin-bottom: 4px;
28 |
29 | textarea {
30 | @extend .__note_textarea;
31 | padding: 5px 5px 10px;
32 | width: 100%;
33 | max-width: 100%;
34 | margin: 1px 0 0 0;
35 | }
36 | &.tmnote--min {
37 | margin-bottom: 2px;
38 | border: 0 !important;
39 | textarea,
40 | tmnotepalette {
41 | display: none !important;
42 | }
43 | tmnotebuttons {
44 | display: block !important;
45 | }
46 | .tmnote__header {
47 | background: none;
48 | &::placeholder {
49 | color: transparent;
50 | }
51 | }
52 | }
53 | .tmnote__header {
54 | width: calc(100% - 70px);
55 | height:22px !important;
56 | border: 0;
57 | color: $color-text--grey;
58 | }
59 | }
60 | tmnotepalette {
61 | position: absolute;
62 | display: block;
63 | background: none !important;
64 | padding: 7px 3px 0;
65 | width: auto;
66 | top: 22px;
67 | left: 0;
68 | border: 0 !important;
69 | }
70 | tmnotecolor {
71 | float: left;
72 | width: 22px;
73 | height: 14px;
74 | margin: 0 2px;
75 | }
76 | tmnotebuttons {
77 | display: block;
78 | }
79 | tmnotedelete {
80 | padding-top: 4px;
81 | }
82 | tmnoteminimize {
83 | font-size: 20px;
84 | }
85 | tmnotecustomize,
86 | tmnoteminimize,
87 | tmnotedelete {
88 | float: right;
89 | position: relative;
90 | height: 18px;
91 | width: 18px;
92 | margin: 2px 2px 0 0;
93 | text-align: center;
94 | }
95 | tmnoteminimize {
96 | &:before {
97 | content: '\2012';
98 | position: absolute;
99 | top: -1px;
100 | left: 3px;
101 |
102 | .tmnote--min & {
103 | content: '\25A1';
104 | top: -4px;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/components/_tabs.scss:
--------------------------------------------------------------------------------
1 | .tab {
2 | &__header {
3 | position: relative;
4 | color: $color-grey--medium-light;
5 |
6 | &:hover {
7 | color: $color-black;
8 | }
9 | }
10 |
11 | &__title {
12 | border-bottom: 1px solid $color-border--light;
13 |
14 | .tab--folded & {
15 | border-top: 1px solid $color-border--light;
16 | border-bottom: 0;
17 | }
18 | }
19 |
20 | &__toggle {
21 | border: 1px solid $color-border--light;
22 | line-height: 1;
23 | position: absolute;
24 | bottom: -1px;
25 | right: 0;
26 | text-align: center;
27 | width:15px;
28 |
29 | &:before {
30 | content: '\2212';
31 | display: inline-block;
32 | position: relative;
33 | top: -1px;
34 |
35 | .tab--folded & {
36 | content: '\002B';
37 | }
38 | }
39 |
40 | .tab--folded & {
41 | top: -1px;
42 | bottom: auto;
43 | }
44 | }
45 |
46 | &__content {
47 | padding: 10px 0 25px;
48 |
49 | .tab--folded & {
50 | display: none;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/components/_tags.scss:
--------------------------------------------------------------------------------
1 | .tags {
2 |
3 | &__item {
4 | position: relative;
5 | margin: 0 5px 5px 0;
6 | display: inline-block;
7 | border-radius: 2px;
8 | padding: 0 20px 2px 4px;
9 | background: $color-bg--light;
10 | border: 1px solid transparentize($color-bg--turquoise, .75);
11 | max-width: 100%;
12 | }
13 |
14 | &__remove {
15 | position: absolute;
16 | right: 4px;
17 | top: 3px;
18 | font-size: 10px;
19 | margin-left: 10px;
20 | cursor: pointer;
21 |
22 | &:hover {
23 | color: $color-text--red;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../_shared/sass/vars';
2 | @import '../../_shared/sass/general';
3 | @import '../../_shared/sass/links';
4 | @import '../../_shared/sass/helpers';
5 | @import '../../_shared/sass/icons';
6 | @import '../../_shared/sass/shadows';
7 | @import '../../_shared/sass/switches';
8 | @import '../../_shared/sass/notes';
9 |
10 | @import './general';
11 | @import './buttons';
12 | @import './form-elements';
13 |
14 | @import './components/tabs';
15 | @import './components/tags';
16 | @import './components/marks';
17 | @import './components/marker';
18 | @import './components/actions';
19 | @import './components/page-notes';
20 | @import './components/links';
21 |
22 | @import './themes/dark';
23 |
24 | @include links(inherit);
25 |
26 | #textmarker-sidebar {
27 | min-height: 100vh;
28 | padding: 10px 15px;
29 | }
30 | #textmarker-sidebar--paused {
31 | padding: 10px 20px;
32 | color: $color-text--red;
33 | }
34 | .textmarker-sidebar--locked {
35 | #header,
36 | #tab--meta,
37 | #tab--tags,
38 | #switch-box,
39 | #mark-action--delete-highlight,
40 | #mark-action--bookmark,
41 | #page-action--scroll,
42 | #page-action--undo,
43 | #page-action--redo {
44 | display: none !important;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/content/sidebar/sass/themes/_dark.scss:
--------------------------------------------------------------------------------
1 | .textmarker-sidebar {
2 |
3 | &--dark, &--tm {
4 | background: $color-bg--dark-ff;
5 | color: $color-text--light;
6 |
7 | button {
8 | border-color: transparent;
9 | color: $color-bg--dark-ff;
10 | background-color: $color-grey--medium-light !important;
11 |
12 | &:hover {
13 | background-color: $color-grey--light !important;
14 | }
15 | }
16 |
17 | $action-buttons: (
18 | '#mark-action--delete-highlight' : 'bin',
19 | '#mark-action--bookmark' : 'bm3',
20 | '#mark-action--delete-bookmark': 'del-bm',
21 | '#mark-action--add-note': 'note2',
22 | '#page-action--save': 'save',
23 | '#page-action--retry': 'retry',
24 | '#page-action--undo': 'undo',
25 | '#page-action--redo': 'redo',
26 | '#page-action--scroll': 'to-bm',
27 | '#page-action--notes': 'toggle-notes',
28 | '#nav-action--up': 'up',
29 | '#nav-action--down': 'down',
30 |
31 | '.marker__apply': 'highlight'
32 | );
33 |
34 | @each $selector, $icon in $action-buttons {
35 | #{$selector} {
36 | @include button_icon($icon, $gradient: false);
37 | }
38 | }
39 |
40 | input[type="text"] {
41 | background-color: rgba(0, 0, 0, .1);
42 | }
43 |
44 | select {
45 | background-color: transparent;
46 | color: $color-grey--medium;
47 | }
48 |
49 | label span {
50 | background-color: rgba(0, 0, 0, .1);
51 | }
52 |
53 | .switch__indicator {
54 | background-color: rgba(255, 255, 255, .1);
55 | }
56 | .switch--immut {
57 | background-color: rgba(0, 0, 0, .1);
58 | }
59 |
60 | .tab {
61 | &__header {
62 | position: relative;
63 | color: $color-grey--medium-light;
64 |
65 | &:hover {
66 | color: $color-white;
67 | }
68 | }
69 | &__title {
70 | border-color: $color-grey--medium;
71 |
72 | .tab--folded & {
73 | border-color: $color-grey--medium;
74 | }
75 | }
76 |
77 | &__toggle {
78 | border-color: $color-grey--medium;
79 | }
80 | }
81 |
82 | .tags {
83 | &__item {
84 | background: $color-bg--dark;
85 | }
86 | &__remove {
87 | &:hover {
88 | color: lighten($color-text--red, 20);
89 | }
90 | }
91 | }
92 |
93 | .mark {
94 | &__text-container {
95 | border-color: $color-border--dark !important;
96 | background-color: rgba(0, 0, 0, .1);
97 | color: $color-text--light-grey;
98 | }
99 | &__text {
100 | &.unfolded {
101 | background-color: $color-bg--dark-ff;
102 | }
103 | }
104 | &__note {
105 | color: $color-bg--dark-ff;
106 | }
107 | }
108 |
109 | .open-note {
110 | .mark__text {
111 | background-color: $color-bg--dark-ff;
112 | }
113 | }
114 |
115 | .link {
116 | opacity: 1;
117 | color: #fff;
118 |
119 | &:hover {
120 | color: $color-text--turquoise;
121 | }
122 | }
123 | }
124 |
125 | &--tm {
126 | background: $color-grey--darker;
127 |
128 | button {
129 | color: $color-bg--dark;
130 | }
131 |
132 | .mark {
133 | &__text {
134 | &.unfolded {
135 | background-color: $color-bg--dark;
136 | }
137 | }
138 |
139 | &__note {
140 | color: $color-bg--dark;
141 | }
142 | }
143 |
144 | .open-note {
145 | .mark__text {
146 | background-color: $color-bg--dark;
147 | }
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/content/tbb-menu/index.js:
--------------------------------------------------------------------------------
1 | import './sass/index.scss'
2 |
3 | import _ERRORTRACKER from './../_shared/utils'
4 | import { _DOMMODULE } from './../_shared/utils'
5 | import { _L10N } from './../_shared/utils'
6 | import _PORT from './port'
7 | _L10N();
8 |
9 | new _DOMMODULE({
10 | el: document.getElementById('menu'),
11 | events: {
12 | DOM: {
13 | click: {
14 | '#activate': 'toggle',
15 | '.send': 'open',
16 | 'span': 'open'
17 | }
18 | }
19 | },
20 | port: null,
21 | disabled: false,
22 |
23 | autoinit() {
24 | browser.storage.local.get().then(storage => {
25 | return storage && storage.settings ? storage.settings.addon.active : true;
26 | })
27 | .then(active => this.setActivateText(active));
28 | },
29 | toggle() {
30 | this.emit('toggle:addon');
31 | window.close();
32 | },
33 | open(e, el) {
34 | const page = el.nodeName === 'SPAN' ? el.parentNode.id : el.id;
35 | this.emit('open:addon-page(tbb)', page);
36 | window.close();
37 | },
38 | setActivateText(active) {
39 | const btn = document.getElementById('activate');
40 | btn.title = browser.i18n.getMessage('tbb_mitem_1_' + active);
41 | btn.textContent = browser.i18n.getMessage('tbb_pause_' + active);
42 | if (active) {
43 | btn.classList.remove('menu__icon--start');
44 | btn.classList.add('menu__icon--pause');
45 | } else {
46 | btn.classList.add('menu__icon--start');
47 | btn.classList.remove('menu__icon--pause');
48 | }
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/src/content/tbb-menu/port.js:
--------------------------------------------------------------------------------
1 | import { _PRIVPORT } from './../_shared/utils'
2 |
3 | export default new _PRIVPORT({
4 | name: 'tbbmenu',
5 | type: 'privileged',
6 | id: Math.random().toString().slice(2, 16),
7 | events: {
8 | CONNECTION: [
9 | 'toggle:addon',
10 | 'open:addon-page(tbb)',
11 | 'error:browser-console'
12 | ]
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/src/content/tbb-menu/sass/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../_shared/sass/vars';
2 | @import '../../_shared/sass/reset';
3 | @import '../../_shared/sass/icons';
4 |
5 | body {
6 | width: 200px;
7 | font-family: system, 'Segoe UI', Tahoma;
8 | }
9 | a {
10 | text-decoration: none;
11 | }
12 | ul {
13 | list-style-type: none;
14 | font-size: 12px;
15 | padding: 5px 0 8px;
16 | }
17 | li {
18 | padding: 5px 10px;
19 |
20 | &:hover {
21 | background: transparentize($color-bg--turquoise, .9);
22 |
23 | .menu__icon {
24 | opacity: 1;
25 | }
26 | }
27 | }
28 | .menu__icon {
29 | position: relative;
30 | display: inline-block;
31 | width: 16px;
32 | height: 16px;
33 | top: 3px;
34 | margin-right: 8px;
35 | opacity: .5;
36 | }
37 | .partingline {
38 | height: 1px;
39 | padding: 0;
40 | background: $color-border--light;
41 | margin: 5px 0;
42 | }
43 | .partingline:hover {
44 | background: $color-border--light;
45 | }
46 |
47 | $links: (
48 | 'settings': 'tools',
49 | 'history': 'clock',
50 | 'help': 'book',
51 | 'contact': 'ghost',
52 | 'news': 'forklift',
53 | 'sync': 'sync2',
54 | 'export': 'export',
55 | 'troubleshooting': 'debug',
56 | 'logs': 'dna',
57 | 'donate': 'donate',
58 | 'github': 'github'
59 | );
60 |
61 | @each $link, $icon in $links {
62 | ##{$link} .menu__icon {
63 | @include icon(#{$icon});
64 | }
65 | }
66 | #activate {
67 | padding-left: 22px;
68 | color: #fff;
69 |
70 | &.menu__icon--start {
71 | @include icon('start');
72 | }
73 |
74 | &.menu__icon--pause {
75 | @include icon('pause');
76 | }
77 | }
78 |
79 | #donate, #github {
80 | float: right;
81 | }
82 |
83 | footer {
84 | background: $color-bg--turquoise;
85 | padding: 7px 6px 8px 12px;
86 |
87 | .menu__icon {
88 | opacity: .85;
89 |
90 | &:hover {
91 | opacity: 1;
92 | }
93 | }
94 | }
95 |
96 | #depricated {
97 | background: #3aad74;
98 | color: #fbfbfb;
99 | padding: 5px 8px 8px;
100 | }
101 |
--------------------------------------------------------------------------------
/src/data/default-storage.js:
--------------------------------------------------------------------------------
1 | export default {
2 | version: browser.runtime.getManifest().version,
3 | settings:{
4 | shortcuts: {
5 | z: ['ctrlKey-altKey', true],
6 | y: ['ctrlKey-altKey', true],
7 | s: ['ctrlKey-altKey', true],
8 | c: ['ctrlKey-altKey', false, true],
9 | b: ['ctrlKey-altKey', true, true],
10 | d: ['shiftKey', false, true],
11 | '-b': ['', '', true],
12 | m: ['', true, true],
13 | w: ['', true, true],
14 | n: ['shiftKey', false, true],
15 | '2': ['', true],
16 | '3': ['', true],
17 | arrowup: ['altKey', false, false],
18 | arrowdown: ['altKey', false, false],
19 | sb: ['', '', true],
20 | cm: ['', true]
21 | },
22 | markers: {
23 | '1': { style: 'background-color:#dd99ff;' },
24 | '2': { style: 'background-color:#66bbff;' },
25 | '3': { style: 'background-color:#55ff55;' },
26 | '4': { style: 'background-color:#ff6666;color:#ffffff;' },
27 | '5': { style: 'background-color:#ffcc00;' },
28 | m: { style: 'background-color:#ffee00;' }
29 | },
30 | history: {
31 | autosave: true,
32 | saveInPriv: false,
33 | dropLosses: true,
34 | immut: false,
35 | naming: 'title',
36 | download: 'json',
37 | copy: 'text',
38 | saveNote: true,
39 | sorted: 'date-last',
40 | view: 'list',
41 | pp: 10,
42 | ignoreHash: true,
43 | autoRetry: true
44 | },
45 | addon: {
46 | active: true,
47 | autocs: true,
48 | iframes: true
49 | },
50 | misc: {
51 | bmicon: true,
52 | noteicon: true,
53 | noteonclick: true,
54 | notetransp: false,
55 | noteplainview: false,
56 | notefontsize: 12,
57 | overwrite: false,
58 | failureNote: true,
59 | successNote: true,
60 | pbmNote: true,
61 | changedNote: false,
62 | errorNote: true,
63 | vipNote: true,
64 | loadNote: false,
65 | customSearch: false,
66 | tmuipos: 'top-right',
67 | markmethod: 'popup',
68 | progressbar: true,
69 | tbbpower: false
70 | },
71 | sb: {
72 | tabs: {
73 | meta: { unfolded: false },
74 | tags: { unfolded: false },
75 | notes: { unfolded: false },
76 | markers: { unfolded: true },
77 | actions: { unfolded: true },
78 | marks: { unfolded: false },
79 | links: { unfolded: false },
80 | themes: { unfolded: false }
81 | },
82 | theme: 'tm'
83 | }
84 | },
85 | history: {
86 | entries: {}
87 | },
88 | pagenotes: {},
89 | sync: {
90 | settings: false,
91 | history: false,
92 | pagenotes: false
93 | }
94 | };
95 |
--------------------------------------------------------------------------------
/src/data/global-settings.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | MAX_ENTRY_NAME_CHARS: 70,
4 |
5 | MAX_LOG_ENTRIES: 20,
6 |
7 | NOTE_COLORS: {
8 | TURQUOISE: '#b9e4ec',
9 | GREEN: '#ccffcc',
10 | YELLOW: '#ffffcc',
11 | ORANGE: '#ffeebb',
12 | RED: '#ffcccc',
13 | PURPLE: '#eeccff',
14 | BLUE: '#bbeeff',
15 | WHITE: '#eeeeee'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/data/log-keys.js:
--------------------------------------------------------------------------------
1 | export default {
2 | note_pbm: 1,
3 | note_restoration_failure: 2,
4 | note_url: 3,
5 | error_save_style: 4,
6 | error_save__toggle_sc: 5,
7 | error_save_change_sc: 6,
8 | error_save_ctm: 7,
9 | error_save_autosave: 8,
10 | error_save_naming: 9,
11 | error_save_notify: 10,
12 | error_save_download: 11,
13 | error_save_bmicon: 12,
14 | error_clean_history: 13,
15 | error_add_marker: 14,
16 | error_remove_marker: 15,
17 | error_save_entry: 16,
18 | error_update_entry: 17,
19 | error_del_entry: 18,
20 | error_empty_synced_storage_onstart: 19,
21 | error_empty_synced_storage_onupdate: 20,
22 | error_empty_local_storage_onstart: 21,
23 | error_import_empty: 22,
24 | error_import_history: 23,
25 | error_import_settings: 24,
26 | error_import_outdated: 25,
27 | error_import_history_not_found: 26,
28 | error_import_settings_not_found: 27,
29 | error_naming: 28,
30 | error_storage_migration: 29,
31 | error_empty_local_storage_onupdate: 30,
32 | error_toggle_sync: 31,
33 | error_save_priv: 32,
34 | note_restoration_warning_1: 33,
35 | note_restoration_warning_2: 34,
36 | error_save_change_autonote: 35,
37 | error_save_mark_method: 36,
38 | js_injection_failure: 37,
39 | css_injection_failure: 38,
40 | missing_permission_wn: 39,
41 |
42 | getKeyByValue(val) {
43 | for (let key in this) {
44 | if (this[key] == val) {
45 | return key;
46 | break;
47 | }
48 | }
49 | return '';
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/icons/arrow-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/arrow-down.png
--------------------------------------------------------------------------------
/src/icons/bin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/bin.png
--------------------------------------------------------------------------------
/src/icons/bm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/bm.png
--------------------------------------------------------------------------------
/src/icons/bm2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/bm2.png
--------------------------------------------------------------------------------
/src/icons/bm3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/bm3.png
--------------------------------------------------------------------------------
/src/icons/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/book.png
--------------------------------------------------------------------------------
/src/icons/clipboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/clipboard.png
--------------------------------------------------------------------------------
/src/icons/clock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/clock.png
--------------------------------------------------------------------------------
/src/icons/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/copy.png
--------------------------------------------------------------------------------
/src/icons/debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/debug.png
--------------------------------------------------------------------------------
/src/icons/del-bm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/del-bm.png
--------------------------------------------------------------------------------
/src/icons/dna.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/dna.png
--------------------------------------------------------------------------------
/src/icons/donate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/donate.png
--------------------------------------------------------------------------------
/src/icons/double-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/double-arrow.png
--------------------------------------------------------------------------------
/src/icons/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/down.png
--------------------------------------------------------------------------------
/src/icons/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/download.png
--------------------------------------------------------------------------------
/src/icons/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/edit.png
--------------------------------------------------------------------------------
/src/icons/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/email.png
--------------------------------------------------------------------------------
/src/icons/export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/export.png
--------------------------------------------------------------------------------
/src/icons/forklift.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/forklift.png
--------------------------------------------------------------------------------
/src/icons/ghost.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/ghost.png
--------------------------------------------------------------------------------
/src/icons/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/github.png
--------------------------------------------------------------------------------
/src/icons/highlight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/highlight.png
--------------------------------------------------------------------------------
/src/icons/m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/m.png
--------------------------------------------------------------------------------
/src/icons/menu-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/menu-blue.png
--------------------------------------------------------------------------------
/src/icons/note.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/note.png
--------------------------------------------------------------------------------
/src/icons/note2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/note2.png
--------------------------------------------------------------------------------
/src/icons/note4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/note4.png
--------------------------------------------------------------------------------
/src/icons/off16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/off16.png
--------------------------------------------------------------------------------
/src/icons/off18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/off18.png
--------------------------------------------------------------------------------
/src/icons/on16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/on16.png
--------------------------------------------------------------------------------
/src/icons/on18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/on18.png
--------------------------------------------------------------------------------
/src/icons/on32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/on32.png
--------------------------------------------------------------------------------
/src/icons/on36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/on36.png
--------------------------------------------------------------------------------
/src/icons/on64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/on64.png
--------------------------------------------------------------------------------
/src/icons/pageaction16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/pageaction16.png
--------------------------------------------------------------------------------
/src/icons/pageaction32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/pageaction32.png
--------------------------------------------------------------------------------
/src/icons/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/pause.png
--------------------------------------------------------------------------------
/src/icons/redo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/redo.png
--------------------------------------------------------------------------------
/src/icons/retry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/retry.png
--------------------------------------------------------------------------------
/src/icons/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/save.png
--------------------------------------------------------------------------------
/src/icons/save2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/save2.png
--------------------------------------------------------------------------------
/src/icons/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/search.png
--------------------------------------------------------------------------------
/src/icons/sel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/sel.jpg
--------------------------------------------------------------------------------
/src/icons/start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/start.png
--------------------------------------------------------------------------------
/src/icons/submit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/submit.png
--------------------------------------------------------------------------------
/src/icons/sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/sync.png
--------------------------------------------------------------------------------
/src/icons/sync2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/sync2.png
--------------------------------------------------------------------------------
/src/icons/tm48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/tm48.png
--------------------------------------------------------------------------------
/src/icons/tm64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/tm64.png
--------------------------------------------------------------------------------
/src/icons/to-bm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/to-bm.png
--------------------------------------------------------------------------------
/src/icons/toggle-notes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/toggle-notes.png
--------------------------------------------------------------------------------
/src/icons/tools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/tools.png
--------------------------------------------------------------------------------
/src/icons/undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/undo.png
--------------------------------------------------------------------------------
/src/icons/up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/up.png
--------------------------------------------------------------------------------
/src/icons/view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufb/Textmarker/4d22cac2036ed6db8f7fbc32ad6ff854a1b3dac6/src/icons/view.png
--------------------------------------------------------------------------------
/src/utils/copy.js:
--------------------------------------------------------------------------------
1 | const _COPY = function(src) {
2 | const target = Array.isArray(src) ? [] : {};
3 | let val;
4 | for (let prop in src) {
5 | if (src.hasOwnProperty(prop)) {
6 | val = src[prop];
7 | if (val !== null && typeof val === 'object') {
8 | target[prop] = _COPY(val);
9 | } else
10 | target[prop] = val;
11 | }
12 | }
13 | return target;
14 | }
15 |
16 | export { _COPY }
17 |
--------------------------------------------------------------------------------
/src/utils/dommodule.js:
--------------------------------------------------------------------------------
1 | import { _MODULE } from './module'
2 |
3 | export class _DOMMODULE extends _MODULE {
4 |
5 | constructor(obj) {
6 | obj._bound = {};
7 | obj._extraBound = [];
8 |
9 | super(obj)
10 |
11 | this.addListenersManually || this.addListeners();
12 | !this.autoPause || this.setAutoPause();
13 | }
14 | generalHandler(subMap, type, e) {
15 | let el = e.target,
16 | selector, _sel, meth, f, isId, isClass, isDoc;
17 |
18 | for (selector in subMap) {
19 | f = selector[0];
20 | _sel = selector;
21 | isId = f === '#';
22 | isClass = f === '.';
23 | isDoc = f === '*';
24 |
25 | if (isId || isClass) selector = selector.substr(1);
26 |
27 | if (isDoc ||
28 | isClass && el.classList.contains(selector) ||
29 | isId && el.id === selector ||
30 | el.nodeName.toLowerCase() === selector
31 | ) {
32 |
33 | meth = subMap[_sel];
34 |
35 | if (typeof meth === 'function') meth(e, el);
36 | else if (this[meth]) this[meth](e, el);
37 |
38 | break;
39 | }
40 | }
41 | }
42 | setAutoPause() {
43 | this.on('toggled:addon', on => {
44 | if (on) this.addListeners();
45 | else this.removeListeners();
46 | });
47 | }
48 | addListener(type, meth, el) {
49 | el = el || this.el;
50 | const handler = typeof meth === 'function' ? meth : this[meth];
51 |
52 | el.addEventListener(type, handler, false);
53 |
54 | if (el === this.el) {
55 | if (!this._bound[type]) this._bound[type] = [];
56 | this._bound[type].push(handler);
57 | } else {
58 | this._extraBound.push([el, type, handler]);
59 | }
60 | }
61 | addListeners() {
62 | let events = this.events,
63 | domEvents, el, subMap, type, handler;
64 |
65 | if (!events || !(domEvents = events.DOM) || !(el = this.el)) return false;
66 |
67 | for (type in domEvents) {
68 | subMap = domEvents[type];
69 | handler = this.proxy(this, this.generalHandler, subMap, type);
70 | el.addEventListener(type, handler, false);
71 | if (!this._bound[type]) this._bound[type] = [];
72 | this._bound[type].push(handler);
73 | }
74 | }
75 | removeListeners() {
76 | let type, _bound, l;
77 |
78 | for (type in this._bound) {
79 | _bound = this._bound[type];
80 | l = _bound.length;
81 | while (l--) {
82 | this.el.removeEventListener(type, _bound[l], false);
83 | _bound.splice(l, 1);
84 | }
85 | }
86 |
87 | let _extra = this._extraBound, i = 0, set;
88 | l = _extra.length;
89 |
90 | for (; i < l; i++) {
91 | set = _extra[i];
92 | set[0].removeEventListener(set[1], set[2], false);
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/utils/error-tracker.js:
--------------------------------------------------------------------------------
1 | import { _MODULE } from './module'
2 |
3 | const _ERRORTRACKER = new _MODULE({
4 | autoinit() {
5 | window.addEventListener('error', error => {
6 | const file = error.filename.split('/').pop();
7 | if (['sidebar.wp.js', 'tbb-menu.wp.js', 'options.wp.js', 'addon-page.wp.js'].includes(file)) {
8 | this.emit('error:browser-console', {
9 | message: error.message,
10 | location: error.filename.split('/').pop().split('.').shift() + ':' + error.lineno + ':' + error.colno,
11 | time: (new Date()).getTime()
12 | });
13 | }
14 | }, false);
15 | }
16 | });
17 |
18 | export default _ERRORTRACKER;
19 |
--------------------------------------------------------------------------------
/src/utils/extend.js:
--------------------------------------------------------------------------------
1 | export default function(obj1, obj2) {
2 | for (var i in obj2)
3 | if (!obj1[i]) obj1[i] = obj2[i];
4 |
5 | return obj1;
6 | }
7 |
--------------------------------------------------------------------------------
/src/utils/getActiveTab.js:
--------------------------------------------------------------------------------
1 | const _GET_ACTIVE_TAB = function() {
2 |
3 | return browser.tabs.query({ currentWindow: true, active: true }).then(tabs => tabs[0]);
4 | }
5 |
6 | export { _GET_ACTIVE_TAB }
7 |
--------------------------------------------------------------------------------
/src/utils/hashless.js:
--------------------------------------------------------------------------------
1 | const _HASHLESS = function(url) {
2 | if (!url) return '';
3 | const h = url.lastIndexOf('#');
4 | if (h === -1) return url;
5 | else return url.substr(0, h);
6 | }
7 |
8 | export { _HASHLESS }
9 |
--------------------------------------------------------------------------------
/src/utils/l10n.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Translates a HTMl page in the web l10n style from the Add-on SDK with WebExtensions strings.
3 | * Modified by underflyingbirches
4 | *
5 | * @author Martin Giger
6 | * @see {@link https://gist.github.com/freaktechnik/4a72bc0711d9bc82cf3b075bcc292953}
7 | * @license MPL-2.0
8 | */
9 | function translateDocument() {
10 | let el, data, dataset;
11 | // Set the language attribute of the document.
12 | document.documentElement.setAttribute('lang', browser.i18n.getUILanguage().replace('_', '-'));
13 | // Get all elements that are marked as being translateable.
14 | const textElements = document.querySelectorAll('*[data-l10n-id]');
15 | const attrElements = document.querySelectorAll('*[data-l10n-attr]');
16 |
17 | for(el of textElements) {
18 | dataset = el.dataset;
19 | const l10nId = dataset.l10nId;
20 |
21 | if (l10nId) {
22 | data = browser.i18n.getMessage(l10nId, '');
23 |
24 | if(data && data != '??') {
25 | el.textContent = data;
26 | }
27 | }
28 | }
29 | for(el of attrElements) {
30 | dataset = el.dataset;
31 |
32 | ['Title', 'Placeholder', 'Href'].forEach(attr => {
33 | const l10nAttr = dataset['l10n' + attr];
34 |
35 | if (l10nAttr) {
36 | data = browser.i18n.getMessage(l10nAttr);
37 |
38 | if (data && data != '??') {
39 | el.setAttribute(attr.toLowerCase(), data);
40 | }
41 | }
42 | });
43 | }
44 | }
45 |
46 | export default function() {
47 | document.addEventListener('DOMContentLoaded', () => translateDocument(), {
48 | capture: false,
49 | passive: true,
50 | once: true
51 | });
52 | }
53 |
--------------------------------------------------------------------------------
/src/utils/mediator.js:
--------------------------------------------------------------------------------
1 | let topics = {};
2 |
3 | export default class {
4 |
5 | on(event, handler) {
6 | if (!topics[event]) topics[event] = [];
7 |
8 | topics[event].push(handler);
9 | }
10 | emit(events, ...args) {
11 | events = events.split(' ');
12 |
13 | let i = 0, l = events.length, topic;
14 |
15 | for (; i < l; i++) {
16 | topic = topics[events[i]];
17 |
18 | if (topic)
19 | topic.forEach(handler => handler.apply(this, args))
20 | }
21 | }
22 | request(event, ...args) {
23 | if (this.type === 'background') {
24 | return browser.tabs.sendMessage(args[0].tabId, { ev: event, wait: true }, { frameId: args[0].frameId });
25 | } else {
26 | return browser.runtime.sendMessage({ ev: event, args: args, wait: true }).catch(() => {});
27 | }
28 | }
29 | proxy(context, func, ...args1) {
30 | return function(...args2) {
31 | return func.apply(context, args1.concat(args2));
32 | };
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/utils/module.js:
--------------------------------------------------------------------------------
1 | import _MEDIATOR from './mediator'
2 |
3 | export class _MODULE extends _MEDIATOR {
4 |
5 | constructor(obj) {
6 | super()
7 |
8 | for (o in obj) this[o] = obj[o];
9 |
10 | let events = this.events,
11 | envEvents, o, e, handler;
12 |
13 | if (events && (envEvents = events.ENV)) {
14 | for (e in envEvents) {
15 | handler = envEvents[e];
16 | if (this[handler])
17 | this.on(e, this.proxy(this, this[handler]));
18 | }
19 | }
20 | !this.autoinit || this.autoinit();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/utils/store.js:
--------------------------------------------------------------------------------
1 | import { _MODULE } from './module'
2 | import _DEFAULT_STORAGE from '../data/default-storage'
3 |
4 | export class _STORE extends _MODULE {
5 |
6 | constructor(obj) {
7 | super(obj)
8 |
9 | this.initialized = false;
10 | this.initializing = false;
11 |
12 | this.area_settings = _DEFAULT_STORAGE.sync.settings ? 'sync' : 'local';
13 | this.area_history = _DEFAULT_STORAGE.sync.history ? 'sync' : 'local';
14 | this.area_pagenotes = _DEFAULT_STORAGE.sync.pagenotes ? 'sync' : 'local';
15 | }
16 |
17 | init() {
18 | return browser.storage.local.get().then(storage => {
19 | if (storage && storage.sync) {
20 | this.setAreas(storage.sync);
21 | }
22 | });
23 | }
24 |
25 | setAreas(sync) {
26 | for (let area in sync) {
27 | this['area_' + area] = sync[area] ? 'sync' : 'local';
28 | }
29 | }
30 |
31 | onToggledSync() {
32 | this.init().then(() => this.emit('set-areas-after-sync-change'));
33 | }
34 |
35 | get(field = 'storage') {
36 | if (this.initializing) {
37 | return (new Promise(r => window.setTimeout(() => r(this.get(field)), 10)));
38 | }
39 | const meth = this['_get_' + field];
40 | if (!meth) throw('field ' + field + ' doesn\'t exist');
41 |
42 | if (!this.initialized) {
43 | this.initializing = true;
44 | this.initialized = true;
45 |
46 | return this.init().then(() => {
47 | this.initializing = false;
48 | return this['_get_' + field]();
49 | });
50 | }
51 | return this['_get_' + field]();
52 | }
53 |
54 | _get_storage() {
55 | return browser.storage.local.get().then(localStorage => {
56 | return browser.storage.sync.get().then(syncedStorage => {
57 | ['version', 'logs'].forEach(field => {
58 | localStorage[field] = localStorage[field] || syncedStorage[field];
59 | });
60 | if (this.area_settings === 'sync') localStorage.settings = syncedStorage.settings;
61 | return this._get_history().then(history => {
62 | localStorage.history = history;
63 | return localStorage;
64 | });
65 | });
66 | });
67 | }
68 | _get_local_storage() {
69 | return browser.storage.local.get();
70 | }
71 | _get_synced_storage() {
72 | return browser.storage.sync.get();
73 | }
74 | _get_history() {
75 | return browser.storage.sync.get().then(syncedStorage => {
76 | const syncedHistory = syncedStorage.history;
77 |
78 | return browser.storage.local.get().then(localStorage => {
79 | const localHistory = localStorage.history;
80 | if (!syncedHistory) return localHistory;
81 | if (!localHistory) return syncedHistory;
82 |
83 | //syncedHistory.order = syncedHistory.order.concat(localHistory.order);
84 | for (let e in localHistory.entries) syncedHistory.entries[e] = localHistory.entries[e];
85 |
86 | return syncedHistory;
87 | });
88 | });
89 | }
90 | _get_settings() {
91 | return browser.storage[this.area_settings].get().then(storage => storage.settings || _DEFAULT_STORAGE.settings);
92 | }
93 | _get_logs() {
94 | return browser.storage.local.get().then(localStorage => {
95 | if (!localStorage || !localStorage.logs) return [];
96 | return localStorage.logs;
97 | });
98 | }
99 | _get_version() {
100 | return browser.storage.local.get().then(localStorage => {
101 | if (!localStorage || !localStorage.version) {
102 | return browser.storage.sync.get().then(syncedStorage => syncedStorage.version || '');
103 | }
104 | return localStorage.version;
105 | });
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const process = require('process');
3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin");
4 |
5 | const PROD = (process.env.NODE_ENV === 'PROD');
6 |
7 | module.exports = {
8 | context: path.resolve(__dirname, 'src'),
9 | entry: {
10 | 'background/background': './background/index.js',
11 | 'content/tbb-menu/tbb-menu': './content/tbb-menu/index.js',
12 | 'content/addon-page/addon-page': './content/addon-page/index.js',
13 | 'content/page-injections/injection': './content/page-injections/index.js',
14 | 'content/sidebar/sidebar': './content/sidebar/index.js',
15 | 'content/options-ui/options': './content/options-ui/index.js',
16 | 'content/detail-view/detail-view': './content/detail-view/index.js'
17 | },
18 | output: {
19 | path: path.resolve(__dirname, 'extension'),
20 | filename: '[name].wp.js'
21 | },
22 | // avoid UglifyJS (-> https://developer.mozilla.org/en-US/Add-ons/Source_Code_Submission#Requirements_for_the_source_package)
23 | mode: 'development',//PROD ? 'production' : 'development',
24 | devtool: PROD ? false : 'npm inline-source-map',
25 | module: {
26 | rules: [{
27 | test: /\.js$/,
28 | include: [path.resolve(__dirname, 'src')],
29 | exclude: /node_modules/,
30 | use: {
31 | loader: 'babel-loader',
32 | options: {
33 | presets: ['@babel/preset-env']
34 | }
35 | }
36 | }, {
37 | test: /\.png$/,
38 | exclude: /node_modules/,
39 | loader: 'file-loader',
40 | options: {
41 | name: '[name].[ext]',
42 | outputPath: 'icons/',
43 | publicPath: '../../icons/'
44 | }
45 | }, {
46 | test: /\.scss$/,
47 | exclude: /node_modules/,
48 | use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
49 | }]
50 | },
51 | plugins: [
52 | new MiniCssExtractPlugin({ filename: '[name].css' })
53 | ]
54 | }
55 |
--------------------------------------------------------------------------------