ldr で購読
:ldr [url]
で購読。url 省略時は現在のタブ。
(function () { liberator.commands.addUserCommand(['ldr'], 'Subscribe URL with livedoor Reader', function(arg, special) { var url = (arg) ? arg : window._content.top.location; window.loadURI('http://reader.livedoor.com/subscribe/' + url); }, {} ); })();
あ、見ればわかるとおり、バックグラウンドで動いたりせず、遷移します。
Adium の負荷状況を見て再起動する Bash Script
Adium で Twitter などのサービスを IM で受け続けていると、やがて CPU 食いまくって kill しない限り固まる現象が頻発する。
これがウザいなあとおもったので簡単な Shell Script 書いてみた。
なんちゃって daemon がこんな感じ。ファイル名は supervisediumd にした。
1秒間隔で監視して CPU 使用率 75% 超えた状態が、10回続けば再起動する。
#!/bin/sh # settings COUNT_LIMIT=10 CPU_LIMIT=750 APP="/Applications/Adium.app" PIDFILE=$1 if [ -f $PIDFILE ] ; then echo "supervisediumd is already running" exit 0 fi trap "rm -rf $PIDFILE; echo 'stopped supervisediumd'" EXIT echo $$ > $PIDFILE echo "start supervising adium" COUNT=0 while [ 1 ] ; do PSINFO=`ps ux | grep $APP | grep -v grep` if [ -z "$PSINFO" ] ; then open -a $APP fi PID=`echo $PSINFO | awk '{print $2}'` CPU=`echo $PSINFO | awk '{print $3 * 10}'` if [ $CPU -gt $CPU_LIMIT ] ; then COUNT=`expr $COUNT + 1` if [ $COUNT -gt $COUNT_LIMIT ] ; then kill $PID open -a $APP COUNT=0 fi else COUNT=0 fi sleep 1 done
操作用のコマンドはこんな感じ。ファイル名は supervisedium にしてる。
#!/bin/sh PIDFILE="/tmp/supervisediumd.`whoami`" COMMAND="supervisediumd" case $1 in "clean" ) echo "cleaning pidfile: $PIDFILE" rm -rf $PIDFILE ;; "start" | "" ) if [ -f $PIDFILE ] ; then echo "supervisediumd is already running" exit 0 else $COMMAND $PIDFILE & echo "starting supervisediumd $!" fi ;; "stop" | "shutdown" ) echo "stopping supervisediumd..." if [ -f $PIDFILE ] ; then kill `cat $PIDFILE` fi ;; "restart" ) echo "restarting supervisediumd..." if [ -f $PIDFILE ] ; then kill `cat $PIDFILE` fi while [ -f $PIDFILE ] ; do sleep 1 done $COMMAND $PIDFILE & ;; * ) cat <<_EOT_ usage: $0 [start|stop|shutdown|clean|restart] start : start supervisediumd stop, shutdown : shutdown supervisediumd clean : cleaning pidfile restart : restart supervisediumd _EOT_ ;; esac
で、両方 PATH 通ってるところに置いて、 supervisedium start すれば OK 。
引数無しの場合は start するようにしているので、ログイン項目に登録しておいたら、起動時に勝手に監視はじめてくれるので便利。
閾値の類は、適当に弄って好みにあわせればいいとおもいます。
いまどきのイベントハンドリングは遅いのかどうか問題
id:HolyGrail 周りで盛り上がってたので、ちょっと調べてみた。
DOMContentLoaded イベント使ったら、計測どころを何処にしていいかわからないので、それ以外の部分で。
昔ながらのやりかた。
<html lang="ja"> <head> <title>Test</title> </head> <body> <script> var start = new Date(); </script> <div> <ol> <li><a class="events" onclick="javascript:(function(){alert(1)})()">click!</a></li> </ol> </div> <script> var end = new Date(); alert(end.getTime() - start.getTime()); </script> </body> </html>
いまどきのやりかた。
<html lang="ja"> <head> <title>Test</title> <script> var $C = (function() { if (document.getElementsByClassName) return function(classname,tag,node) { if (node == null) node = document; if(tag == null) tag = '*'; return document.getElementsByClassName(classname,tag,node); } else return function(classname,tag,node) { if (node == null) node = document; if(tag == null) tag = '*'; var elems = node.getElementsByTagName(tag); var pattern = new RegExp('(?:^|\\s)' + name + '(?:\\s|$)'); var found = []; for(var i = 0, length = elems.length; i < length; i++) if(pattern.test(elems[i].className)) found.push(elems[i]); return found; } })(); var addEvent = (function() { if(document.addEventListener) return function(elem,type,fn,useCapture) { elem.addEventListener(type,fn,useCapture); } else if (document.attachEvent) return function(elem,type,fn,useCapture) { elem.attachEvent('on' + type); } else return function(elem,type,fn,useCapture) { elem['on' + type] = fn; } })(); </script> </head> <body> <script> var start = new Date(); </script> <div> <ol> <li><a class="events" >click!</a></li> </ol> </div> <script> var elems = $C('events'); for (var i = 0,length = elems.length; i < length; i++) addEvent(elems[i],'click',function(){alert(1)},false); var end = new Date(); alert(end.getTime() - start.getTime()); </script> </body> </html>
結果
li 要素を 400 にして試してみた。環境は、MacBook (CPU:Core Duo 2GHz, MEM: 2GB)
ブラウザ | 昔ながらのやりかた | いまどきのやりかた |
Firefox3B5 | 10ms | 32ms |
Safari3.1.1 | 9ms | 8ms |
Opera9.27 | 20-50ms | 40-80ms |
結論
体感では違いがわからない程度。Safari はむしろいまどきのほうが早い傾向がある。Safari すげー。
まあ、特殊な事情が無い限り、いまどきのやりかたのほうがいろいろと便利なのでいいんじゃないすかね。
jQuery とか YUI なんか使うと、イベントバブル辺りもよしなにやってくれるし、便利便利。逆に初心者は積極的にライブラリ使った方がいいと思う。
補足
10000 でも試してみた
ブラウザ | 昔ながらのやりかた | いまどきのやりかた |
Firefox3B5 | 2000-3500ms | 1900-2400ms |
Safari3.1.1 | 180-200ms | 200-210ms |
Opera9.27 | 650-710ms | 1300-1500ms |
だいぶ結果が変わった。
当り前だけど、完全に実装によるよね。
一概に速いとか遅いとか言えないので、その辺、特定の環境を根拠にどうこう言うのはおかしいよな。
はっきり言えるのは、 Safari3 素晴らしいと言うことぐらいですね。
LDR Full Feed が 時々動かなくなる件
w.Keybind オブジェクトができる前に走るから。
ちょっと、自分が適当に混ぜ込んだ Site Info とかも混じってるけど以下、 patch 。Keybind オブジェクトができるまで、 setTimeout まわるようにした。
--- ldrfullfeed.user.js.200802119 2008-02-19 23:48:09.000000000 +0900 +++ ldrfullfeed.user.js 2008-02-19 23:44:26.000000000 +0900 @@ -30,6 +30,16 @@ var SITE_INFO = [ { + url: 'http://today-yuuri.cocolog-nifty.com/yuuri/', + xpath: '//div[@class="entry-body-text"]', + enc: 'UTF-8', + }, + { + url: 'http://cross-breed.com/ura/', + xpath: '//div[@class="contentarea"]', + enc: 'UTF-8', + }, + { url: 'http://(rssblog.ameba.jp|ameblo.jp)', xpath: '//div[@class="subContents"]', base: 'http://ameblo.jp', @@ -146,6 +156,11 @@ xpath: '//div[(@class="main") or (@class="mainmore") or (@id="comment")]', enc: 'EUC-JP', }, + { + url: 'http://blog.goo.ne.jp', + xpath: '//div[(@class="entry-body-text")]', + enc: 'EUC-JP', + }, ]; // == [Application] ================================================= @@ -313,10 +328,17 @@ if(LOADING_MOTION){ addStyle(CSS, 'gm_fulfeed'); } -w.Keybind.add(KEY, function(){ - launchFullFeed(SITE_INFO); -}); +var timer = setTimeout(function() { + if(timer) clearTimeout(timer); + if (typeof w.Keybind != 'undefined' ) { + w.Keybind.add(KEY, function(){ + launchFullFeed(SITE_INFO); + }); + } else { + timer = setTimeout(arguments.callee,100); + } +}); // == [Utility] ===================================================== function relativeToAbsolutePath (text, link){
DOMNodeInsertedIntoDocument イベントのクロスブラウザ対応試作
Firefox や、 Opera で DOMNodeInsertedIntoDocument イベントがあまりイケてなかったので、比較的マシな DOMNodeInserted イベント使って、似たような動きになるようなモノを作ってみた。
Firefox 2.0.0.12 と Opera 9.25 と Safari 3.0.4 で動作確認してます。
IE は、今、手元に環境が無いから試してないけど多分動くんじゃないかな。動けばいいな。
var InsertedIntoDocument = function(){}; (function(){ /*************************/ /** * Original Source : * jQuery (jquery.com) */ var ua = navigator.userAgent.toLowerCase(); var browser = { version: (ua.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], safari: /webkit/.test(ua), opera: /opera/.test(ua), msie: /msie/.test(ua) && !/opera/.test(ua), mozilla: /mozilla/.test(ua) && !/(compatible|webkit)/.test(ua) }; /*************************/ this.uniqid = function(prefix, table) { var id = prefix + ((new Date()).getTime() + Math.random()); return (typeof table[id] != 'undefined') ? arguments.callee(prefix, table) : id; } if (!browser.msie && !browser.safari) { this.handlers = {}; document.addEventListener('DOMNodeInserted',function(evt) { var handler = InsertedIntoDocument.handlers[evt.target.__handler_id__]; if (typeof handler != 'object') return; /* target 周りが上手く行かないからとりあえず DOMNodeInserted を渡す。 var e = document.createEvent("MutationEvents"); e.initMutationEvent( "DOMNodeInsertedIntoDocument", evt.cancelBubble, evt.cancelable, undefined, evt.prevValue, evt.newValue, evt.attrName, evt.attrChange ); */ for (var i in handler) handler[i].call(evt.target,evt); },true); } this.add = (function() { // for Internet Explorer /* @cc_on return function(element, fn, useCapture) { target.attachEvent('onreadystatechange',fn); } @*/ if(browser.safari) { return function(element, fn, useCapture) { var type = (browser.version < 3) ? 'readystatechange' : 'DOMNodeInsertedIntoDocument'; element.addEventListener(type, fn, useCapture); } } else if(!browser.msie) { return function(element, fn, useCapture) { if (typeof element.__handler_id__ == 'undefined') element.__handler_id__ = this.uniqid('handler_', this.handlers); var id = element.__handler_id__; if (typeof this.handlers[id] != 'object') this.handlers[id] = {}; var listener_id = this.uniqid('listener_', this.handlers[id]) fn.listener_id = listener_id; this.handlers[id][listener_id] = fn; } } })(); this.remove = (function() { // for Internet Explorer /* @cc_on return function(element, fn, useCapture) { target.detachEvent('onreadystatechange',fn); } @*/ if(browser.safari) { return function(element, fn, useCapture) { var type = (browser.version < 3) ? 'readystatechange' : 'DOMNodeInsertedIntoDocument'; element.removeEventListener(type, fn, useCapture); } } else if(!browser.msie) { return function(element, fn, useCapture) { delete this.handlers[element.__handler_id__][fn.listener_id]; if (this.countProperty(this.handlers[element.__handler_id__]) == 0) delete this.handlers[element.__handler_id__]; delete fn.listener_id; delete element.__handler_id__; } } })(); this.countProperty = function(obj) { var count = 0; for(var i in obj) count++; return count; } return this; }).call(InsertedIntoDocument);
使い方はこんな感じ。
var e1 = document.createElement('div'); e1.id = "e1"; InsertedIntoDocument.add(e1,function(evt){ console.log("DOMNodeInsertedIntoDocument !"); console.dir(evt); },true); setTimeout(function() { document.body.appendChild(e1); console.log('added'); },1000);
イベントハンドラやリスナの管理とかするために、DOM オブジェクトや Function オブジェクトにそれぞれの ID 振ったりしてるのがあまり綺麗じゃないので、いい方法思いついたらなんとかしたい。
あと、ちゃんと DOMInsertedIntoDocument イベントをリスナに渡せるようにしたい。
せつなくてまぶしくて
UTF-8 で。
// ==UserScript== // @name Twitter Append Setunakutemabushikute // @namespace http://d.hatena.ne/jp/send/ // @include http://twitter.com/home // ==/UserScript== /** * Original Source: * http://userscripts.org/scripts/show/9086 */ (function() { var onclick_orig; var w = (typeof unsafeWindow == 'undefined') ? window : unsafeWindow; var submit = w.document.getElementsByClassName('update-button')[0]; var onclick = function() { document.getElementById('status').value += 'せつなくてまぶしくて'; submit.onclick = onclick_orig; submit.click(); submit.onclick = onclick; return false; }; onclick_orig = submit.onclick; submit.onclick = onclick; })();
AutoPagerize 対応の filter 管理GM
いちいち個別の filter スクリプトに AutoPagerize 対応入れるのもなんかなと思ったから、ちょっと書いてみた。
こういうの userscripts.org に上げた方がいいのかな?
// ==UserScript== // @name Filterize // @namespace http://d.hatena.ne.jp/send/ // @description apply filter on any site. // @include * // ==/UserScript== (function () { var FilterContainer = function() { this.__container = {}; this.addFilter = function(name,func) { this.__container[name] = func; return this; } this.batch = function(nodes) { for each (var node in nodes) for each (var filter in this.__container) filter(node); } }; if(window.Filterize == undefined) window.Filterize = { onAutoPagerized : new FilterContainer(), onLoaded : new FilterContainer(), }; // for AutoPagerize if(window.AutoPagerize != undefined) window.AutoPagerize.addFilter(function (context){ return Filterize.onAutoPagerized.batch.call(Filterize.onAutoPagerized, context); }); // for loaded // XXX: 調査してないけど、0 sec でも setTimeoutしたらうまく行った window.Filterize.timer = setTimeout(function() { for each(var filter in window.Filterize) if(filter.batch) filter.batch([document]); clearTimeout(window.Filterize.timer); },0); })();
AutoPagarize 対応が要らない場合のスクリプト例
twitter の status 入力エリアを大きくする。
// ==UserScript== // @name AdjustStatusForTwitter // @namespace http://d.hatena.ne.jp/send/ // @description adjust status text area for Filterize // @include http://twitter.com/home // @include http://twitter.com/replies // @include http://twitter.com/account/archive // ==/UserScript== if(window.Filterize != undefined) Filterize.onLoaded.addFilter('adjustTextArea', function (context) { var textarea = context.getElementById('status'); if (textarea) textarea.style['height'] = '3.5em'; });
AutoPagerize 対応がいる場合のスクリプト例
twitter が 半角文字とかで、デザイン崩れるのを矯正する。
// ==UserScript== // @name StatusBreakerForTwitter // @namespace http://d.hatena.ne.jp/send/ // @description apply status break on twitter // @include http://twitter.com/* // ==/UserScript== /** * Original Script: * url breaker+ * http://piro.sakura.ne.jp/latest/2005/06/url_breaker_plus.user.js * XXX: とりあえずUTF-8 */ if(window.Filterize != undefined) Filterize.onAutoPagerized.addFilter('applyWordBreak', function (context){ var path = '//td[@class="content"]/span[@class="entry-title entry-content"]//child::text()'; var elems = document.evaluate(path, context, null,7 ,null ); var regex = /([@!-%'-/:=\\?@\\[-`\\{-~\w]|&[\w]{1,6};)/; var range = document.createRange(); var wbr = document.createElement('wbr'); var last; var elem; var started; for(var e = 0 ; e < elems.snapshotLength; e++) { elem = elems.snapshotItem(e); range.selectNode(elem); while(elem && (last = range.toString().search(regex)) !== -1) { range.setStart(elem, last + RegExp.$1.length); range.insertNode(wbr.cloneNode(true)); elem = elem.nextSibling.nextSibling; range.selectNode(elem); } } range.detach(); });