copy_tuner_client 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.eslintrc.js +12 -0
- data/.gitattributes +2 -0
- data/.github/workflows/rspec.yml +4 -8
- data/.ruby-version +1 -1
- data/.vscode/settings.json +7 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +117 -116
- data/README.md +5 -10
- data/app/assets/javascripts/main.js +388 -0
- data/app/assets/stylesheets/style.css +1 -0
- data/copy_tuner_client.gemspec +1 -1
- data/gemfiles/{5.2.gemfile → 7.0.gemfile} +1 -1
- data/index.html +34 -0
- data/lib/copy_tuner_client/copyray_middleware.rb +15 -29
- data/lib/copy_tuner_client/engine.rb +2 -2
- data/lib/copy_tuner_client/version.rb +1 -1
- data/package.json +14 -15
- data/spec/copy_tuner_client/i18n_backend_spec.rb +2 -0
- data/{app/assets/stylesheets → src}/copyray.css +0 -0
- data/src/copyray.ts +130 -0
- data/src/copytuner_bar.ts +115 -0
- data/src/main.ts +58 -0
- data/src/specimen.ts +84 -0
- data/src/util.ts +38 -0
- data/src/vite-env.d.ts +1 -0
- data/tsconfig.json +26 -0
- data/vite.config.ts +18 -0
- data/yarn.lock +2251 -0
- metadata +25 -22
- data/.eslintrc +0 -4
- data/app/assets/javascripts/copyray.js +0 -1069
- data/app/views/_copy_tuner_bar.html.erb +0 -6
- data/gemfiles/6.0.gemfile +0 -5
- data/package-lock.json +0 -4341
- data/rollup.config.js +0 -16
- data/src/copyray.js +0 -112
- data/src/copytuner_bar.js +0 -96
- data/src/main.js +0 -42
- data/src/specimen.js +0 -63
- data/src/util.js +0 -32
- data/test.sh +0 -6
data/rollup.config.js
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
import babel from 'rollup-plugin-babel';
|
2
|
-
import resolve from 'rollup-plugin-node-resolve';
|
3
|
-
import commonjs from 'rollup-plugin-commonjs';
|
4
|
-
|
5
|
-
export default {
|
6
|
-
input: 'src/main.js',
|
7
|
-
output: {
|
8
|
-
file: 'app/assets/javascripts/copyray.js',
|
9
|
-
format: 'iife',
|
10
|
-
},
|
11
|
-
plugins: [
|
12
|
-
resolve({ jsnext: true, main: true }),
|
13
|
-
commonjs(),
|
14
|
-
babel({ exclude: 'node_modules/**' }),
|
15
|
-
],
|
16
|
-
};
|
data/src/copyray.js
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
import Specimen from './specimen';
|
2
|
-
import CopyTunerBar from './copytuner_bar';
|
3
|
-
|
4
|
-
const findBlurbs = () => {
|
5
|
-
const filterNone = () => NodeFilter.FILTER_ACCEPT;
|
6
|
-
|
7
|
-
const iterator = document.createNodeIterator(
|
8
|
-
document.body,
|
9
|
-
NodeFilter.SHOW_COMMENT,
|
10
|
-
filterNone,
|
11
|
-
false,
|
12
|
-
);
|
13
|
-
|
14
|
-
const comments = [];
|
15
|
-
let curNode;
|
16
|
-
// eslint-disable-next-line no-cond-assign
|
17
|
-
while ((curNode = iterator.nextNode())) {
|
18
|
-
comments.push(curNode);
|
19
|
-
}
|
20
|
-
|
21
|
-
return comments.filter(comment => comment.nodeValue.startsWith('COPYRAY')).map((comment) => {
|
22
|
-
const [, key] = comment.nodeValue.match(/^COPYRAY (\S*)$/);
|
23
|
-
const element = comment.parentNode;
|
24
|
-
return { key, element };
|
25
|
-
});
|
26
|
-
};
|
27
|
-
|
28
|
-
export default class Copyray {
|
29
|
-
constructor(baseUrl, data) {
|
30
|
-
this.baseUrl = baseUrl;
|
31
|
-
this.data = data;
|
32
|
-
this.isShowing = false;
|
33
|
-
this.specimens = [];
|
34
|
-
this.overlay = this.makeOverlay();
|
35
|
-
this.toggleButton = this.makeToggleButton();
|
36
|
-
this.boundOpen = this.open.bind(this);
|
37
|
-
|
38
|
-
this.copyTunerBar = new CopyTunerBar(
|
39
|
-
document.getElementById('copy-tuner-bar'),
|
40
|
-
this.data,
|
41
|
-
this.boundOpen,
|
42
|
-
);
|
43
|
-
}
|
44
|
-
|
45
|
-
show() {
|
46
|
-
this.reset();
|
47
|
-
|
48
|
-
document.body.appendChild(this.overlay);
|
49
|
-
this.makeSpecimens();
|
50
|
-
|
51
|
-
this.specimens.forEach((specimen) => {
|
52
|
-
specimen.show();
|
53
|
-
});
|
54
|
-
|
55
|
-
this.copyTunerBar.show();
|
56
|
-
this.isShowing = true;
|
57
|
-
}
|
58
|
-
|
59
|
-
hide() {
|
60
|
-
this.overlay.remove();
|
61
|
-
this.reset();
|
62
|
-
this.copyTunerBar.hide();
|
63
|
-
this.isShowing = false;
|
64
|
-
}
|
65
|
-
|
66
|
-
toggle() {
|
67
|
-
if (this.isShowing) {
|
68
|
-
this.hide();
|
69
|
-
} else {
|
70
|
-
this.show();
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
open(key) {
|
75
|
-
const url = `${this.baseUrl}/blurbs/${key}/edit`;
|
76
|
-
window.open(url, null, 'width=700, height=600');
|
77
|
-
}
|
78
|
-
|
79
|
-
makeSpecimens() {
|
80
|
-
findBlurbs().forEach(({ element, key }) => {
|
81
|
-
this.specimens.push(new Specimen(element, key, this.boundOpen));
|
82
|
-
});
|
83
|
-
}
|
84
|
-
|
85
|
-
makeToggleButton() {
|
86
|
-
const element = document.createElement('a');
|
87
|
-
|
88
|
-
element.addEventListener('click', () => {
|
89
|
-
this.show();
|
90
|
-
});
|
91
|
-
|
92
|
-
element.classList.add('copyray-toggle-button');
|
93
|
-
element.classList.add('hidden-on-mobile');
|
94
|
-
element.textContent = 'Open CopyTuner';
|
95
|
-
document.body.appendChild(element);
|
96
|
-
|
97
|
-
return element;
|
98
|
-
}
|
99
|
-
|
100
|
-
makeOverlay() {
|
101
|
-
const div = document.createElement('div');
|
102
|
-
div.setAttribute('id', 'copyray-overlay');
|
103
|
-
div.addEventListener('click', () => this.hide());
|
104
|
-
return div;
|
105
|
-
}
|
106
|
-
|
107
|
-
reset() {
|
108
|
-
this.specimens.forEach((specimen) => {
|
109
|
-
specimen.remove();
|
110
|
-
});
|
111
|
-
}
|
112
|
-
}
|
data/src/copytuner_bar.js
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
import debounce from 'lodash.debounce';
|
2
|
-
|
3
|
-
const HIDDEN_CLASS = 'copy-tuner-hidden';
|
4
|
-
|
5
|
-
export default class CopytunerBar {
|
6
|
-
constructor(element, data, callback) {
|
7
|
-
this.element = element;
|
8
|
-
this.data = data;
|
9
|
-
this.callback = callback;
|
10
|
-
this.searchBoxElement = element.querySelector('.js-copy-tuner-bar-search');
|
11
|
-
this.logMenuElement = this.makeLogMenu();
|
12
|
-
this.element.appendChild(this.logMenuElement);
|
13
|
-
|
14
|
-
this.addHandler();
|
15
|
-
}
|
16
|
-
|
17
|
-
addHandler() {
|
18
|
-
const openLogButton = this.element.querySelector('.js-copy-tuner-bar-open-log');
|
19
|
-
openLogButton.addEventListener('click', (event) => {
|
20
|
-
event.preventDefault();
|
21
|
-
this.toggleLogMenu();
|
22
|
-
});
|
23
|
-
|
24
|
-
this.searchBoxElement.addEventListener('input', debounce(this.onKeyup.bind(this), 250));
|
25
|
-
}
|
26
|
-
|
27
|
-
show() {
|
28
|
-
this.element.classList.remove(HIDDEN_CLASS);
|
29
|
-
this.searchBoxElement.focus();
|
30
|
-
}
|
31
|
-
|
32
|
-
hide() {
|
33
|
-
this.element.classList.add(HIDDEN_CLASS);
|
34
|
-
}
|
35
|
-
|
36
|
-
showLogMenu() {
|
37
|
-
this.logMenuElement.classList.remove(HIDDEN_CLASS);
|
38
|
-
}
|
39
|
-
|
40
|
-
toggleLogMenu() {
|
41
|
-
this.logMenuElement.classList.toggle(HIDDEN_CLASS);
|
42
|
-
}
|
43
|
-
|
44
|
-
makeLogMenu() {
|
45
|
-
const div = document.createElement('div');
|
46
|
-
div.setAttribute('id', 'copy-tuner-bar-log-menu');
|
47
|
-
div.classList.add(HIDDEN_CLASS);
|
48
|
-
|
49
|
-
const table = document.createElement('table');
|
50
|
-
const tbody = document.createElement('tbody');
|
51
|
-
tbody.classList.remove('is-not-initialized');
|
52
|
-
|
53
|
-
Object.keys(this.data).sort().forEach((key) => {
|
54
|
-
const value = this.data[key];
|
55
|
-
|
56
|
-
if (value === '') {
|
57
|
-
return;
|
58
|
-
}
|
59
|
-
|
60
|
-
const td1 = document.createElement('td');
|
61
|
-
td1.textContent = key;
|
62
|
-
const td2 = document.createElement('td');
|
63
|
-
td2.textContent = value;
|
64
|
-
const tr = document.createElement('tr');
|
65
|
-
tr.classList.add('copy-tuner-bar-log-menu__row');
|
66
|
-
tr.dataset.key = key;
|
67
|
-
|
68
|
-
tr.addEventListener('click', ({ currentTarget }) => {
|
69
|
-
this.callback(currentTarget.dataset.key);
|
70
|
-
});
|
71
|
-
|
72
|
-
tr.appendChild(td1);
|
73
|
-
tr.appendChild(td2);
|
74
|
-
tbody.appendChild(tr);
|
75
|
-
});
|
76
|
-
|
77
|
-
table.appendChild(tbody);
|
78
|
-
div.appendChild(table);
|
79
|
-
|
80
|
-
return div;
|
81
|
-
}
|
82
|
-
|
83
|
-
onKeyup({ target }) {
|
84
|
-
const keyword = target.value.trim();
|
85
|
-
this.showLogMenu();
|
86
|
-
|
87
|
-
const rows = Array.from(this.logMenuElement.getElementsByTagName('tr'));
|
88
|
-
|
89
|
-
rows.forEach((row) => {
|
90
|
-
const isShow =
|
91
|
-
keyword === '' ||
|
92
|
-
Array.from(row.getElementsByTagName('td')).some(td => td.textContent.includes(keyword));
|
93
|
-
row.classList.toggle(HIDDEN_CLASS, !isShow);
|
94
|
-
});
|
95
|
-
}
|
96
|
-
}
|
data/src/main.js
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
import KeyCode from 'keycode-js';
|
2
|
-
import Copyray from './copyray';
|
3
|
-
import { isMac } from './util';
|
4
|
-
|
5
|
-
const start = () => {
|
6
|
-
const dataElement = document.getElementById('copy-tuner-data');
|
7
|
-
const copyTunerUrl = dataElement.dataset.copyTunerUrl;
|
8
|
-
const data = JSON.parse(
|
9
|
-
document.getElementById('copy-tuner-data').dataset.copyTunerTranslationLog,
|
10
|
-
);
|
11
|
-
const copyray = new Copyray(copyTunerUrl, data);
|
12
|
-
|
13
|
-
document.addEventListener('keydown', (event) => {
|
14
|
-
if (copyray.isShowing && event.keyCode === KeyCode.KEY_ESCAPE) {
|
15
|
-
copyray.hide();
|
16
|
-
return;
|
17
|
-
}
|
18
|
-
|
19
|
-
if (
|
20
|
-
((isMac && event.metaKey) || (!isMac && event.ctrlKey)) &&
|
21
|
-
event.shiftKey &&
|
22
|
-
event.keyCode === KeyCode.KEY_K
|
23
|
-
) {
|
24
|
-
copyray.toggle();
|
25
|
-
}
|
26
|
-
});
|
27
|
-
|
28
|
-
if (console) {
|
29
|
-
// eslint-disable-next-line no-console
|
30
|
-
console.log(
|
31
|
-
`Ready to Copyray. Press ${isMac ? 'cmd+shift+k' : 'ctrl+shift+k'} to scan your UI.`,
|
32
|
-
);
|
33
|
-
}
|
34
|
-
|
35
|
-
window.copyray = copyray;
|
36
|
-
};
|
37
|
-
|
38
|
-
if (document.readyState === 'complete' || document.readyState !== 'loading') {
|
39
|
-
start();
|
40
|
-
} else {
|
41
|
-
document.addEventListener('DOMContentLoaded', start);
|
42
|
-
}
|
data/src/specimen.js
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
import { computeBoundingBox } from './util';
|
2
|
-
|
3
|
-
const ZINDEX = 2000000000;
|
4
|
-
|
5
|
-
export default class Specimen {
|
6
|
-
constructor(element, key, callback) {
|
7
|
-
this.element = element;
|
8
|
-
this.key = key;
|
9
|
-
this.callback = callback;
|
10
|
-
}
|
11
|
-
|
12
|
-
show() {
|
13
|
-
this.box = this.makeBox();
|
14
|
-
if (this.box === null) return;
|
15
|
-
|
16
|
-
this.box.addEventListener('click', () => {
|
17
|
-
this.callback(this.key);
|
18
|
-
});
|
19
|
-
|
20
|
-
document.body.appendChild(this.box);
|
21
|
-
}
|
22
|
-
|
23
|
-
remove() {
|
24
|
-
if (!this.box) {
|
25
|
-
return;
|
26
|
-
}
|
27
|
-
this.box.remove();
|
28
|
-
this.box = null;
|
29
|
-
}
|
30
|
-
|
31
|
-
makeBox() {
|
32
|
-
const box = document.createElement('div');
|
33
|
-
box.classList.add('copyray-specimen');
|
34
|
-
box.classList.add('Specimen');
|
35
|
-
|
36
|
-
const bounds = computeBoundingBox(this.element);
|
37
|
-
if (bounds === null) return null;
|
38
|
-
|
39
|
-
Object.keys(bounds).forEach((key) => {
|
40
|
-
const value = bounds[key];
|
41
|
-
box.style[key] = `${value}px`;
|
42
|
-
});
|
43
|
-
box.style.zIndex = ZINDEX;
|
44
|
-
|
45
|
-
const { position, top, left } = getComputedStyle(this.element);
|
46
|
-
if (position === 'fixed') {
|
47
|
-
this.box.style.position = 'fixed';
|
48
|
-
this.box.style.top = `${top}px`;
|
49
|
-
this.box.style.left = `${left}px`;
|
50
|
-
}
|
51
|
-
|
52
|
-
box.appendChild(this.makeLabel());
|
53
|
-
return box;
|
54
|
-
}
|
55
|
-
|
56
|
-
makeLabel() {
|
57
|
-
const div = document.createElement('div');
|
58
|
-
div.classList.add('copyray-specimen-handle');
|
59
|
-
div.classList.add('Specimen');
|
60
|
-
div.textContent = this.key;
|
61
|
-
return div;
|
62
|
-
}
|
63
|
-
}
|
data/src/util.js
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
const isMac = navigator.platform.toUpperCase().indexOf('MAC') !== -1;
|
2
|
-
|
3
|
-
const isVisible = element =>
|
4
|
-
!!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
5
|
-
|
6
|
-
const getOffset = (elment) => {
|
7
|
-
const box = elment.getBoundingClientRect();
|
8
|
-
|
9
|
-
return {
|
10
|
-
top: box.top + (window.pageYOffset - document.documentElement.clientTop),
|
11
|
-
left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
|
12
|
-
};
|
13
|
-
};
|
14
|
-
|
15
|
-
const computeBoundingBox = (element) => {
|
16
|
-
if (!isVisible(element)) {
|
17
|
-
return null;
|
18
|
-
}
|
19
|
-
|
20
|
-
const boxFrame = getOffset(element);
|
21
|
-
boxFrame.right = boxFrame.left + element.offsetWidth;
|
22
|
-
boxFrame.bottom = boxFrame.top + element.offsetHeight;
|
23
|
-
|
24
|
-
return {
|
25
|
-
left: boxFrame.left,
|
26
|
-
top: boxFrame.top,
|
27
|
-
width: boxFrame.right - boxFrame.left,
|
28
|
-
height: boxFrame.bottom - boxFrame.top,
|
29
|
-
};
|
30
|
-
};
|
31
|
-
|
32
|
-
export { isMac, isVisible, getOffset, computeBoundingBox };
|
data/test.sh
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
#!/bin/sh
|
2
|
-
|
3
|
-
BUNDLE_GEMFILE=gemfiles/5.2.gemfile bundle install && BUNDLE_GEMFILE=gemfiles/5.2.gemfile bundle exec rspec spec
|
4
|
-
BUNDLE_GEMFILE=gemfiles/6.0.gemfile bundle install && BUNDLE_GEMFILE=gemfiles/6.0.gemfile bundle exec rspec spec
|
5
|
-
BUNDLE_GEMFILE=gemfiles/6.1.gemfile bundle install && BUNDLE_GEMFILE=gemfiles/6.1.gemfile bundle exec rspec spec
|
6
|
-
BUNDLE_GEMFILE=gemfiles/main.gemfile bundle install && BUNDLE_GEMFILE=gemfiles/main.gemfile bundle exec rspec spec
|