copy_tuner_client 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.babelrc +18 -0
- data/.eslintrc +4 -0
- data/CHANGELOG.md +6 -2
- data/Gemfile.lock +1 -1
- data/README.md +4 -4
- data/app/assets/javascripts/copyray.js +937 -409
- data/app/assets/stylesheets/copyray.css +14 -16
- data/app/views/_copy_tuner_bar.html.erb +0 -6
- data/lib/copy_tuner_client/copyray.rb +3 -20
- data/lib/copy_tuner_client/version.rb +1 -1
- data/package-lock.json +2578 -4
- data/package.json +16 -3
- data/rollup.config.js +14 -0
- data/src/copyray.js +111 -0
- data/src/copytuner_bar.js +96 -0
- data/src/main.js +42 -0
- data/src/specimen.js +63 -0
- data/src/util.js +32 -0
- metadata +10 -4
- data/coffeelint.json +0 -135
- data/src/copyray.coffee +0 -354
data/package.json
CHANGED
@@ -5,10 +5,23 @@
|
|
5
5
|
"author": "SonicGarden",
|
6
6
|
"license": "MIT",
|
7
7
|
"scripts": {
|
8
|
-
"build": "
|
9
|
-
"watch": "
|
8
|
+
"build": "rollup -c",
|
9
|
+
"watch": "rollup -c -w"
|
10
|
+
},
|
11
|
+
"dependencies": {
|
12
|
+
"keycode-js": "0.0.4",
|
13
|
+
"lodash.debounce": "^4.0.8"
|
10
14
|
},
|
11
15
|
"devDependencies": {
|
12
|
-
"
|
16
|
+
"babel-plugin-external-helpers": "^6.22.0",
|
17
|
+
"babel-preset-env": "^1.6.0",
|
18
|
+
"eslint": "^3.19.0",
|
19
|
+
"eslint-config-airbnb-base": "^11.2.0",
|
20
|
+
"eslint-plugin-import": "^2.7.0",
|
21
|
+
"rollup": "^0.45.2",
|
22
|
+
"rollup-plugin-babel": "^2.7.1",
|
23
|
+
"rollup-plugin-commonjs": "^8.0.2",
|
24
|
+
"rollup-plugin-node-resolve": "^3.0.0",
|
25
|
+
"rollup-watch": "^4.3.1"
|
13
26
|
}
|
14
27
|
}
|
data/rollup.config.js
ADDED
@@ -0,0 +1,14 @@
|
|
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
|
+
entry: 'src/main.js',
|
7
|
+
dest: 'app/assets/javascripts/copyray.js',
|
8
|
+
format: 'iife',
|
9
|
+
plugins: [
|
10
|
+
resolve({ jsnext: true, main: true }),
|
11
|
+
commonjs(),
|
12
|
+
babel({ exclude: 'node_modules/**' }),
|
13
|
+
],
|
14
|
+
};
|
data/src/copyray.js
ADDED
@@ -0,0 +1,111 @@
|
|
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.textContent = 'Open CopyTuner';
|
94
|
+
document.body.appendChild(element);
|
95
|
+
|
96
|
+
return element;
|
97
|
+
}
|
98
|
+
|
99
|
+
makeOverlay() {
|
100
|
+
const div = document.createElement('div');
|
101
|
+
div.setAttribute('id', 'copyray-overlay');
|
102
|
+
div.addEventListener('click', () => this.hide());
|
103
|
+
return div;
|
104
|
+
}
|
105
|
+
|
106
|
+
reset() {
|
107
|
+
this.specimens.forEach((specimen) => {
|
108
|
+
specimen.remove();
|
109
|
+
});
|
110
|
+
}
|
111
|
+
}
|
@@ -0,0 +1,96 @@
|
|
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
ADDED
@@ -0,0 +1,42 @@
|
|
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
ADDED
@@ -0,0 +1,63 @@
|
|
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
ADDED
@@ -0,0 +1,32 @@
|
|
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 };
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: copy_tuner_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SonicGarden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: i18n
|
@@ -226,6 +226,8 @@ executables: []
|
|
226
226
|
extensions: []
|
227
227
|
extra_rdoc_files: []
|
228
228
|
files:
|
229
|
+
- ".babelrc"
|
230
|
+
- ".eslintrc"
|
229
231
|
- ".gitignore"
|
230
232
|
- ".rspec"
|
231
233
|
- ".ruby-version"
|
@@ -239,7 +241,6 @@ files:
|
|
239
241
|
- app/assets/javascripts/copyray.js
|
240
242
|
- app/assets/stylesheets/copyray.css
|
241
243
|
- app/views/_copy_tuner_bar.html.erb
|
242
|
-
- coffeelint.json
|
243
244
|
- copy_tuner_client.gemspec
|
244
245
|
- features/rails.feature
|
245
246
|
- features/step_definitions/copycopter_server_steps.rb
|
@@ -270,6 +271,7 @@ files:
|
|
270
271
|
- lib/tasks/copy_tuner_client_tasks.rake
|
271
272
|
- package-lock.json
|
272
273
|
- package.json
|
274
|
+
- rollup.config.js
|
273
275
|
- spec/copy_tuner_client/cache_spec.rb
|
274
276
|
- spec/copy_tuner_client/client_spec.rb
|
275
277
|
- spec/copy_tuner_client/configuration_spec.rb
|
@@ -291,7 +293,11 @@ files:
|
|
291
293
|
- spec/support/fake_unicorn.rb
|
292
294
|
- spec/support/middleware_stack.rb
|
293
295
|
- spec/support/writing_cache.rb
|
294
|
-
- src/copyray.
|
296
|
+
- src/copyray.js
|
297
|
+
- src/copytuner_bar.js
|
298
|
+
- src/main.js
|
299
|
+
- src/specimen.js
|
300
|
+
- src/util.js
|
295
301
|
- ui/views/copytuner/index.html.erb
|
296
302
|
- ui/views/layouts/copytuner_default.html.erb
|
297
303
|
homepage: https://github.com/SonicGarden/copy-tuner-ruby-client
|