I[★]
@@ -3,7 +3,6 @@
* @description greedy quoted Hatena Star
* @include http://*
* @include https://*
- * @contributor Hatena https://s.hatena.ne.jp/images/add.gif
* @contributor a-kuma3 http://let.hatelabo.jp/a-kuma3/let/hJmc_YyG8sE-
* @nitpicker noromanba
* @license MIT License https://opensource.org/licenses/MIT
@@ -15,89 +14,71 @@
(() => {
'use strict';
- function change_button_color(img) {
- // change color with canvas
- // https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Canvas_tutorial/Pixel_manipulation_with_canvas
- let canvas;
- document.body.querySelectorAll([
- 'img.hatena-star-add-button[src*="s.hatena.ne.jp"]',
- ]).forEach(button => {
- if (!canvas) {
- canvas = Object.assign(document.createElement('canvas'), {
- width: img.width,
- height: img.height,
- });
- const ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0);
- const imageData = ctx.getImageData(0, 0, img.width, img.height);
- let data = imageData.data;
- for (let i = 0 ; i < data.length ; i += 4) {
- if (data[i] < 200) {
- // gold : rgb(255,215,0)
- data[i] = 255; // red
- data[i + 1] = 215; // green
- data[i + 2] = 0; // blue
-// data[i + 3] = 255; // opacity
- }
- }
- ctx.putImageData(imageData, 0, 0);
- }
- button.src = canvas.toDataURL();
- button.title = button.title + ' (force quote)';
- });
- }
-
- // IDK necessary needs star icon at bottom is?
- //document.body.appendChild(Object.assign(document.createElement('img'), {
- Object.assign(document.createElement('img'), {
- // TODO over CORS, canvas too
- //src: 'https://s.hatena.com/images/add.gif',
- //src: 'https://s.hatena.ne.jp/images/add.gif',
- // XXX check term of use
- src: 'data:image/gif;base64,R0lGODlhEAAQAIABALrJ9f///yH5BAEAAAEALAAAAAAQABAAAAIojI+pm+APYQCIMlfZtLOvSEkexhmchXkjaHYlGpLpqrZNqbngnvd+AQA7',
- // https://stackoverflow.com/questions/17035106/context-getimagedata-operation-is-insecure
- crossOrigin: 'anonymous',
- onload() {
- change_button_color(this);
- },
- //}));
+ document.body.querySelectorAll([
+ 'img.hatena-star-add-button[src*="s.hatena.ne.jp"]',
+ ]).forEach(button => {
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/filter
+ // NOTE order sensitive
+ button.style.filter = [
+ // reset to black
+ 'grayscale(1) brightness(0)',
+ // w/ black to hex CSS filter generator by Barrett Sonntag c.f.
+ // https://codepen.io/sosuke/pen/Pjoqqp
+ // https://codepen.io/sosuke/pen/Pjoqqp/license
+ // via
+ // https://gist.github.com/barretts/e90d7e5251f36b183c67e02ba54c9ae1
+ //
+ // gold | rgb(255,215,0) | hex #ffd700
+ 'invert(74%)',
+ 'sepia(56%)',
+ 'saturate(811%)',
+ 'hue-rotate(0deg)',
+ 'brightness(106%)',
+ 'contrast(103%)',
+ ].join(' ');
+
+ button.title += ' (force quote)';
});
// http://q.hatena.ne.jp/1487227736#a1262105
- function convert_numeric_reference_quote(url_string) {
- // https://developer.mozilla.org/en-US/docs/Web/API/URL
- // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
+ const to_numeric_reference = (str) => {
+ let letter = '', i = 0;
+ while (i < str.length) {
+ const code = str.codePointAt(i);
+ letter += code < 128 ? String.fromCharCode(code) : `&#${code};`;
+ i += 1;
+ // for surrogate pair
+ if (code > 0xffff) {
+ i += 1;
+ }
+ }
+ return letter;
+ };
+
+ const enforce_quote = (url_string) => {
const url = new URL(url_string);
- let quote = url.searchParams.get('quote');
- if (quote) {
- // to numeric reference
- quote = (s => {
- let o = '', i = 0;
- while (i < s.length) {
- const code = s.codePointAt(i);
- o += code < 128 ? String.fromCharCode(code) : `&#${code};`;
- i += 1;
- if (code > 0xffff) { // for surrogate pair
- i += 1;
- }
- }
- return o;
- })(quote);
- url.searchParams.set('quote', quote);
- url_string = url.href;
+ // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
+ const quote = url.searchParams.get('quote');
+ if (!quote) {
+ return url_string;
}
- return url_string;
- }
+
+ const safed = to_numeric_reference(quote);
+
+ url.searchParams.set('quote', safed);
+
+ return url.href;
+ };
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
// https://s.hatena.ne.jp/js/HatenaStar.js:111
- // Ten.JSONP(URL, CALLBACK, METHOD)
+ // Ten.JSONP(<URL_STRING>, <CALLBACK>, <HTTP_METHOD>)
window.Ten.JSONP = new Proxy(window.Ten.JSONP, {
construct: (target, args) => {
const url_string = args[0];
if (url_string.includes('//s.hatena.ne.jp/star.add.json')) {
- args[0] = convert_numeric_reference_quote(url_string);
+ args[0] = enforce_quote(url_string);
}
return Reflect.construct(target, args);
/*
* @title I[★]
* @description greedy quoted Hatena Star
* @include http://*
* @include https://*
* @contributor a-kuma3 http://let.hatelabo.jp/a-kuma3/let/hJmc_YyG8sE-
* @nitpicker noromanba
* @license MIT License https://opensource.org/licenses/MIT
* @javascript_url
*/
// TBD .com handling
// TBC spec and behavior
(() => {
'use strict';
document.body.querySelectorAll([
'img.hatena-star-add-button[src*="s.hatena.ne.jp"]',
]).forEach(button => {
// https://developer.mozilla.org/en-US/docs/Web/CSS/filter
// NOTE order sensitive
button.style.filter = [
// reset to black
'grayscale(1) brightness(0)',
// w/ black to hex CSS filter generator by Barrett Sonntag c.f.
// https://codepen.io/sosuke/pen/Pjoqqp
// https://codepen.io/sosuke/pen/Pjoqqp/license
// via
// https://gist.github.com/barretts/e90d7e5251f36b183c67e02ba54c9ae1
//
// gold | rgb(255,215,0) | hex #ffd700
'invert(74%)',
'sepia(56%)',
'saturate(811%)',
'hue-rotate(0deg)',
'brightness(106%)',
'contrast(103%)',
].join(' ');
button.title += ' (force quote)';
});
// http://q.hatena.ne.jp/1487227736#a1262105
const to_numeric_reference = (str) => {
let letter = '', i = 0;
while (i < str.length) {
const code = str.codePointAt(i);
letter += code < 128 ? String.fromCharCode(code) : `&#${code};`;
i += 1;
// for surrogate pair
if (code > 0xffff) {
i += 1;
}
}
return letter;
};
const enforce_quote = (url_string) => {
const url = new URL(url_string);
// https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
const quote = url.searchParams.get('quote');
if (!quote) {
return url_string;
}
const safed = to_numeric_reference(quote);
url.searchParams.set('quote', safed);
return url.href;
};
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
// https://s.hatena.ne.jp/js/HatenaStar.js:111
// Ten.JSONP(<URL_STRING>, <CALLBACK>, <HTTP_METHOD>)
window.Ten.JSONP = new Proxy(window.Ten.JSONP, {
construct: (target, args) => {
const url_string = args[0];
if (url_string.includes('//s.hatena.ne.jp/star.add.json')) {
args[0] = enforce_quote(url_string);
}
return Reflect.construct(target, args);
},
});
})();
- Permalink
- このページへの個別リンクです。
- RAW
- 書かれたコードへの直接のリンクです。
- Packed
- 文字列が圧縮された書かれたコードへのリンクです。
- Userscript
- Greasemonkey 等で利用する場合の .user.js へのリンクです。
- Loader
- @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
- Metadata
- コード中にコメントで @xxx と書かれたメタデータの JSON です。