@@ -96,7 +96,7 @@
construct: (target, args) => {
const url_string = args[0];
- if (url_string.include('//s.hatena.ne.jp/star.add.json')) {
+ if (url_string.includes('//s.hatena.ne.jp/star.add.json')) {
args[0] = convert_numeric_reference_quote(url_string);
}
/*
* @title I[★]
* @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
* @javascript_url
*/
// TBD .com handling
// TBC spec and behavior
(() => {
'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);
},
//}));
});
// 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 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;
}
return url_string;
}
// 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)
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);
}
return Reflect.construct(target, args);
},
});
})();