「JavaScript」タグアーカイブ

Googleの検索結果にサムネイルを追加していくユーザースクリプト Part2

google_thumbnail_2

open.thumbshots.org のサムネイルを追加するやつ が使えなくなったりしたので修正したものです。
色々調べてたら似たようなAdd-onsあります。むしろそっちをおすすめします。

動作確認
スクリプトの適当な説明
  • 検索結果の左側に112×82ぐらいの画像を表示
  • AutoPagerizeの2ページ目以降もどんどんサムネ追加
  • Amazonの場合はサムネじゃなくって商品画像を読み込む(一部画像でない)
  • Youtubeの場合は動画サムネを読み込む(一部画像でないかも)
  • ニコニコ動画の場合も動画サムネを読み込む(一部でない)
  • Chromeでも一応動く(要Tampermonkey)
  • Safariでも一応動くかもしれない(要NinjaKit)
  • もしかしたらBing検索でも表示される

ダウンロード: google_search_thumbnail.user.js
GitHub: google_search_thumbnail.user.js
NinjaKitで管理する場合は Scripts→Add new script→スクリプト貼り付け→Saveで保存 (間違ってるかもしれません)

最近の修正内容とか

Firefoxの「すべての表示したページの履歴」を1発で消す.uc.xul

ucjs_clear_history

動作確認

キャッシュを消すやつ作ってたらこれもあったらいいんじゃないかってことです。
「表示したページの履歴」としてますが「ダウンロード履歴」も消えます。わりと適当です。

あと履歴が多いと消すのに時間が掛かってFirefoxが固まります。

ダウンロード: clear_all_page_history.uc.js for Firefox 27

ダウンロード: clear-all-history-fx50.uc.xul for Firefox 50

  • 2017.01.05 Firefox 50でも動くように書き直し
<?xml version="1.0" encoding="UTF-8"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript"><![CDATA[
var ucjsHistoryClear = function() {
    try {
        PlacesUtils.history.clear();
    } catch (e) {
    }
}
]]></script>
<menupopup id="menu_ToolsPopup">
    <menuitem id="menu_ucjsHistoryClear"
              label="表示したページの履歴を消去"
              oncommand="ucjsHistoryClear()"/>
</menupopup>
</overlay>

右クリック禁止(oncontextmenu)や貼り付け無効(onpaste)を無効にする

油断してるとたまに出てくる「右クリック禁止です」みたいな頭の悪いサイトで禁止された動作を使えるようにします。
oncontextmenu=”return false;” や onpaste=”return false;” のアレです。

以下、貼り付け禁止を無効にするやつです。

なんかいろいろ禁止されてるクソサイトの場合は paste, contextmenu, drop あたり書いとけばいいと思います。

document.addEventListener('paste', event => event.stopImmediatePropagation(), true);
document.addEventListener('contextmenu', event => event.stopImmediatePropagation(), true);
document.addEventListener('drop', event => event.stopImmediatePropagation(), true);

指定したサイトでだけこのスクリプトを有効にしないと右クリックで独自のメニューを出すサイトでは、その右クリックが出なくなるので注意が必要です。

タブの一覧表示メニュー(#alltabs-popup)のミドルクリックでタブを閉じる.uc.js

動作確認

たまにタブを開きすぎてどうにもならないことがあるので一覧表示メニューから閉じられたら便利そうだと思いまして。
#alltabs-popup > menuitem には menuitem.tab というのがあってそれでタブ操作がしやすくなってます。

ダウンロード: middle-click-close-tab-in-alltabs-popup.uc.js

セキュアなサイトでURLバーの背景色を変えるやつ.uc.js

動作確認

緑色の鍵マーク表示するだけじゃ分かりづらい!
と感じてuserChrome.cssである程度指定できていたのがFirefox 38あたりから使えなくなったので、鍵アイコン(identity-box)と同じような属性持ってきてCSSで背景色を変更してます。
XPCOMなのでそのうち使えなくなります。

HTTPS: 青っぽくする
url_ssl

HTTPS (EV SSL): 緑っぽくする
url_evssl

混在コンテンツ: 黄色っぽくする
url_mixed

ダウンロード: secure-site-urlbar-color.uc.js

// テーマによっては変になるかもしれないのでこの部分の色を変更するといいかもしれない
var colors = {'insecure': '#fff', 'secure': '#def0f7', 'ev': '#def2d7', 'broken': '#ffffdd'},

検索バーのマウスホイールスクロールで検索エンジンを切り替えるやつ.uc.js

wheel_search

動作確認

検索バー上のホイールスクロールで検索エンジン切り替えるだけです。
Firefox 43以降では検索エンジンのアイコンも変えるようにしてます。

ダウンロード: wheelscroll-to-change-search-engine.uc.js
GitHub: wheelscroll-to-change-search-engine.uc.js

JavaScriptのtypeofって大雑把すぎんじゃね?というやつ

JavaScriptでオブジェクトの型を判別するのにtypeof演算子使うとツラいよね – Qiita
で is(‘型’, 値) という関数が便利なのだけど typeof と比較して10倍ぐらい遅い。
なので、それなりに速度を上げてarrayやnull(とついでにNaN)も判定する関数を考えた。

typeofでなくてtypeOf()です。

ES6が使えない場合は Number.isNaN がダメなので typeof val === ‘number’ && isNaN(val) とする必要があります。

node-mysqlのコールバック地獄をES6 GeneratorとPromiseでなんとかする

node-mysqlのコールバック地獄をthunkifyでなんとかする でやったthunkify使わずにPromiseでやるやつです。

thunkifyより柔軟な処理ができるかもしれません。
db.query(‘SELECT … ‘, function(err, rows) {}) の代わりにPromiseな db.exec() も同じように用意してます。

'use strict';
// pool connection + Promise
var mysql = require('mysql2'),
    pool;

function getPoolConnection() {
    if (!pool) pool = mysql.createPool({host: '***', user: '***', password: '***'});
     return new Promise(function(resolve, reject) {
         pool.getConnection(function(err, connection) {
             if (err) {
                 reject(err);
             } else {
                 // conn.exec = <Promise>
                 connection.exec = function(query) {
                     return new Promise(function(res, rej) {
                         connection.query(query, function(err, rows) {
                             err ? rej(err) : res(rows);
                         });
                     });
                 }
                 resolve(connection);
             }
         });
     });
}
// then, catchを使う…… と、あまり変わらないけれど
function example1() {
    getPoolConnection()
        .then(function(db) {
            db.exec('SELECT * FROM ... ')
              .then(function(rows) {
                  console.log(rows);
                  db.release();
              })
        })

}

// generator functionを使う
function* example2() {
    var db = yield getPoolConnection();
    var res = yield db.exec('SELECT * FROM ....');
    console.log(res);
    db.release();
}

// coでもいい
co(function* () {
    var db = yield getPoolConnection();
    var res = yield db.exec('SELECT * FROM ....');
    console.log(res);
    db.release();
})

UserScriptLoader.uc.jsの@requireが動かない

@requireでjQueryやunderscore.jsを読み込んでるユーザースクリプトを使うとき、requireディレクトリにキャッシュがない場合、saveURI()で失敗するようになってます。
調べるとFirefox 36以降でパラメータが増えてるのが原因っぽいです。

void saveURI(
  in nsIURI aURI,
  in nsISupports aCacheKey,
  in nsIURI aReferrer,
  in long aReferrerPolicy,
  in nsIInputStream aPostData,
  in string aExtraHeaders,
  in nsISupports aFile,
  in nsILoadContext aPrivacyContext
);

ということで wbp.saveURI(uri, null, null, null, null, aFile, null); となってる箇所の4番目(in long aReferrerPolicy)に null を追加すると保存されます。

	wbp.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE;
	wbp.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
	wbp.saveURI(uri, null, null, null, null, null, aFile, null);
	USL.debug("getContents: " + aURL);

同じようにメニューのSave Scriptをクリックしたときに保存できないのもsaveURIのパラメータが足りないからです。
こんな感じで null 追加。

			var loadContext = win.QueryInterface(Ci.nsIInterfaceRequestor)
				.getInterface(Ci.nsIWebNavigation)
				.QueryInterface(Ci.nsILoadContext);
			wbp.saveURI(uri, null, uri, null, null, null, fp.file, loadContext);

XPCOM廃止するとか言ってんのに今さら仕様変更すんなよ……。

わりと高速なJavaScriptテンプレートエンジン、doT.js を使おう

Webアプリ作っているとPHPやRubyでHTMLを返すよりNode.js (io.js)でJSONを返してそれをクライアント側で処理してHTMLにするってのがやってみるとめっちゃ楽だったので。

で、まずどれを使おうって調べてると結構でてくるんです。

jquery-tmpl
元jQuery公式のテンプレートプラグイン。超遅い。死ぬ。
JsRender
jquery-tmplの後継。JsViewsと合わせるといいらしい。
Hogan.js
Twitter製。わりと使ってるのを見かけるし、そこそこ速い。
Handlebars.js
テンプレートがHogan.jsと似てるがやや遅い。
micro-template.js
スクリプトが軽量だけど for, if などが面倒。Handlebars.jsよりやや遅い。
doT.js
Node.jsでも使える。速度重視という謳い文句。

テンプレートの相互性を考えたらよくある形式のHogan.jsでいいのだけど速度重視ということで doT.js というのを使うことにしました。

jsPerf で速度比較したやつ
適当に用意したテストデータだと doT.js が一番速そうです。
(Windows 7のChrome, Firefox 31, Firefox 36で10回計測、OtherはIE11。)
doT.js vs Hogan.js

<!-- doT.js 用のテンプレートを用意する -->
<script type="text/template" id="tmpl">
<h1>{{!it.title}}</h1>
<p>{{!it.description}}</p>
<ul>
{{~it.entries :item:i}}
<li><a href="{{=item.url}}">{{!item.name}}</a><span>{{=item.tag}}</span></li>
{{~}}
</ul>
</script>
/* 置換するやつデータ */
var data = {
    title: 'Hogan.js vs doT.js vs Handlebars.js',
    description: '',
    entries: []
}
var entry = {
    url: 'http://example.com/',
    name: 'name',
    tag: '<b>tag</b>'
};
for (var i = 0; i < 10; i++) {
    data['entries'].push({url: entry['url']+i, name: entry['name']+i, tag: entry['tag']+i});
}

/* doT.js でテンプレートをコンパイルして #content に書き出す */
var tmplText = $('#tmpl').text(),
    tmpl = doT.template(tmplText);

$('#content').html(tmpl(data));

{{!it.name}}, {{=it.url}}や{{~it.entries}}…{{~}}など少し変わってますがJavaScriptそれなりに書ける人ならむしろこっちのが分かりやすいかと思います。

{{!it.hoge}} hogeをhtmlencodeしたやつに置換
{{=it.hoge}} HTMLタグを含めてhogeをそのまま置換する
{{? it.hoge }} … {{?}} if (hoge) { }
hogeが空じゃなければ{{? it.hoge }}~{{?}}内を置換する
{{~ it.entries :item:index }} … {{~}} for文
{{ for (var key in it.data) { }}
<li> {{!it.data[key]}} </li>
{{ } }}
for in なんかも書ける
{{= encodeURIComponent(it.hoge) }} JavaScriptの関数をそのまま使ったりもできる

この辺を覚えとけばなんとかいけます。

というわけで、doT.jsより速いのあったらそっちを使おう。