/** * I LOVE :echo * I LOVE :js * * 説明 * :echo コマンドが大好きな人用 * とにかく、ワンラインでタブ補完しつつコードを実行するためのネタ的プラグイン * * 完成度は低い * * 設計思想 * * 最初に与えた引数を変換に変換を重ねてコネくり回すことが主眼 * * 各メソッドやgetterは常にvalue(またはxhr)メンバを持つObject * * メソッドやgetterはvalue値に相応しいメソッドまたはgetterである * * * Example: * 1 2 3 4 5 6 7 8 * :echo $x("http://d.hatena.ne.jp/teramako/rss").open().send().xml.toObject.get("item").map(function(item) item.title).value * ブログのRSSをゲットしてオブジェクト化して、各item要素内のtitle文字列を格納した配列をechoする * * 1. $xにURLを渡し * 2. XMLHttpRequest.open()し * 3. リクエストを送信し * 4. レスポンスをDOMオブジェクトを得て(XMLHttpRequest.responseXML) * 5. さらにObject化し(toObjectはXMLのタグ名をキーにツリー構造を模したObject) * 6. オブジェクト内の"item"メンバ(配列)を得て * 7. map関数で各item内のtitleを得て * 8. その値 :echo に渡す(RSSのタイトル一覧の出力となる) * * :js $_.url.MD5Hash.copy() * 現在開いているURLのMD5ハッシュ値をクリップボードにコピー * * :js $x("http://example.com").open().send().xml.stack() * とりあえず、http://exapmle.comのDOMドキュメントをstack * :echo $_.cache.last.inspect() * 最後にstackしたものを取り出し、DOM Inspectorが入っている場合はDOM Inspectorに出力 * * :echo $('//a').evaluate().grep(/^http/).join("\n").copy() * リンクを抽出してクリップボードにコピー * * :echo $('//a').evaluate().map(function(v)v.href.replace(/.*\//,'')).copy() * :echo $('//a').evaluate().map($f.href().replace(/.*\//,'')).copy() * リンクのファイル名部分のみをクリップボードにコピー * */ (function(){ var cache = []; var cacheXHR = null; function $(arg){ //{{{ if (!arg) return new $c(); if (typeof arg == "string"){ let s = new $s(arg); if (/^https?:\/\/./.test(arg)){ s.open = function(){ var x = liberator.modules.$x(arg); return x.open(); }; } return s; } else if (typeof arg == "xml"){ return new $e4x(arg); } else if (arg instanceof Array){ return new $a(arg); } else if (arg instanceof Element || arg instanceof Document){ return new $xml(arg); } else if (typeof arg == "object"){ return new $o(arg); } } //}}} liberator.modules.$f = (function () { //{{{ const pests = [ '__defineGetter__', '__defineSetter__', 'hasOwnProperty', 'isPrototypeOf', '__lookupGetter__', '__lookupSetter__', '__parent__', 'propertyIsEnumerable', '__proto__', 'toSource', 'toString', 'toLocaleString', 'unwatch', 'valueOf', 'watch' ]; function id(value) value; function memfn(parent) function (name,args) FFF(function(self) let (s = parent(self)) let (f = s[name]) (f instanceof Function ? s[name].apply(s, args) : f)); function mem(parent) function (name) FFF(function(self) parent(self)[name]); function FFF(parent) { parent.__noSuchMethod__ = memfn(parent) parent.m = {__noSuchMethod__: mem(parent)}; pests.forEach(function(it) (parent[it] = function() parent.__noSuchMethod__(it, arguments))); return parent; }; return FFF(id); })(); //}}} liberator.modules.$ = $; liberator.modules.$x = function $x(url, method, user, password){ //{{{ if (!cacheXHR){ cacheXHR = new $xhr(url, method, user, password); } else if (cacheXHR.success && cacheXHR.url == url){ return cacheXHR; } else { cacheXHR = new $xhr(url || cacheXHR.url, method || cacheXHR.method, user || cacheXHR.user, password || cacheXHR.password); } return cacheXHR; }; //}}} liberator.modules.$_ = { //{{{ cache: { get: function(num) $(cache[num]), get length() cache.length, get first() this.length > 0 ? this.get(0) : null, get last() this.length > 0 ? this.get(this.length-1) : null, get all() $(cache), shift: function() $(cache.shift()), pop: function() $(cache.pop()), get xhr() cacheXHR, clear: function(){ cache = []; cacheXHR = null; } }, env: { maxCacheLength: 20, autoCache: false, xhr: { user: null, password: null } }, get clipboard() $(util.readFromClipboard()), get url() $(buffer.URL), get sel() $(content.window.getSelection().toString()), get lastInputValue(){ if (buffer.lastInputField){ return $(buffer.lastInputField.value); } return null; } }; // }}} const DOMINSPECTOR = Application.extensions.has("inspector@mozilla.org") && Application.extensions.get("inspector@mozilla.org").enabled; // ----------------------------------------------------------------------------- // Core // --------------------------------------------------------------------------{{{ function $c(arg){ this.value = arg || null; } $c.prototype = { echo: function(flag){ liberator.echo(this.value, flag); return this; }, log: function(level){ liberator.log(this.value, level || 0); return this; }, copy: function(){ util.copyToClipboard(this.value); return this; }, stack: function(){ cache.push(this.value); return this; }, toString: function(){ return this.value.toString(); }, __noSuchMethod__: function(name, args){ return $(this.value[name].apply(this.value, args)); } }; // }}} // ----------------------------------------------------------------------------- // String // --------------------------------------------------------------------------{{{ function $s(arg){ this.value = arg || null; } $s.prototype = new $c(); createPrototype($s, { get htmlEscape() $(this.value.replace("&","&","g").replace("<","<","g").replace(">",">","g")), get utf16() $(["\\u"+("0000"+this.value.charCodeAt(i).toString(16).toUpperCase()).slice(-4) for (i in this.value)].join("")), get numCharRef() $(["&#" + this.value.charCodeAt(i) + ";" for (i in this.value)].join("")), get base64() $(window.btoa(this.value)), get encodeURICompoenent() $(encodeURIComponent(this.value)), get MD5Hash(){ var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter); converter.charset = "UTF-8"; var result = {}; var data = converter.convertToByteArray(this.value, result); var ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); ch.init(ch.MD5); ch.update(data, data.length); var hash = ch.finish(false); function toHexString(charCode){ return ("0" + charCode.toString(16)).slice(-2); } var s = [i < hash.length ? toHexString(hash.charCodeAt(i)) : "" for (i in hash)].join(""); return $(s); }, s: function(from, to) $(this.value.replace(from,to)), split: function(reg) $(this.value.split(reg)), get toJSON(){ var json; try { json = Cc["@mozilla.org/dom/json;1"].getService(Ci.nsIJSON); return $(json.decode(this.value)); } catch (e){ return null; } }, evaluate: function(doc,context){ if (!doc) doc = content.document; if (!context) context = doc; var result = []; var nodes = doc.evaluate(this.value, context, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); var node; while (node = nodes.iterateNext()) { result.push(node); } return $(result); } }); // }}} // ----------------------------------------------------------------------------- // Array // --------------------------------------------------------------------------{{{ function $a(arg){ this.value = arg; } $a.prototype = new $c(); createPrototype($a, { get length() this.value.length, item: function(index) 0 <= index && index < this.length ? $(this.value[index]) : null, join: function(str) $(this.value.join(str)), grep: function(reg) $(this.value.filter(function(v) reg.test(v.toString()))), forEach: function(func, thisp) $(this.value.forEach(func, thisp)), map: function(func, thisp) $(this.value.map(func, thisp)), some: function(func, thisp) $(this.value.some(func, thisp)), filter: function(func, thisp) $(this.value.filter(func, thisp)), push: function(arg){ this.value.push(arg); return this; }, unshift: function(arg){ this.value.unshift(arg); return this; }, get first() $(this.value[0]), get last() $(this.value[this.length - 1]), }); // }}} // ----------------------------------------------------------------------------- // Object // --------------------------------------------------------------------------{{{ function $o(arg){ this.value = arg; } $o.prototype = new $c(); createPrototype($o, { get toArrayName() $([i for (i in this.value)]), get toArrayValue() $([this.value[i] for (i in this.value)]), get: function(prop){ if (prop in this.value) return $(this.value[prop]); }, getItemsByKeyName: function(itemName){ var a = []; function walk(obj){ for (let item in obj){ if (itemName == item) a.push(obj[itemName]); if (typeof obj[item] == "object") walk(obj[item]);; } } walk(this.value); return $(a); }, map: function(func, thisp){ if (typeof func != "function") throw new TypeError(); var obj = {}; var thisp = arguments[1]; for (let i in this.value){ obj[i] = func.call(thisp, this.value[i], i, this); } return $(obj); }, forEach: function(func, thisp){ if (typeof func != "function") throw new TypeError(); var thisp = arguments[1]; for (let i in this.value){ func.call(thisp, this.value[i], i, this); } return this; }, filter: function(func, thisp){ if (typeof func != "function") throw new TypeError(); var obj = {}; var thisp = arguments[1]; for (let i in this.value){ if (func.call(thisp, this.value[i], i, this)){ obj[i] = this.value[i]; } } return $(obj); }, get toJSON(){ var json = Cc["@mozilla.org/dom/json;1"].getService(Ci.nsIJSON); return $(json.encode(this.value)); } }); if (DOMINSPECTOR){ createPrototype($o,{ inspect: function(){ inspectObject(this.value); return ""; } }); } // }}} // ----------------------------------------------------------------------------- // XML // --------------------------------------------------------------------------{{{ function $xml(arg){ this.value = arg; } $xml.prototype = new $c(); createPrototype($xml, { get toObject(){ // {{{2 function parseElement(node){ //{{{3 var res = {}; var isTextOnly = true; if (node.attributes && node.attributes.length > 0){ isTextOnly = false; let attrs = nod
var PLUGIN_INFO =
<VimperatorPlugin>
<name>{NAME}</name>
<description>clear privacy data</description>
<minVersion>2.0pre</minVersion>
<maxVersion>2.0pre</maxVersion>
<author mail="teramako@gmail.com" homepage="http://vimperator.g.hatena.ne.jp/teramako/">teramako</author>
<license>MPL 1.1/GPL 2.0/LGPL 2.1</license>
<version>0.1</version>
<updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/clear_privacy_data.js</updateURL>
<detail lang="ja"><![CDATA[
== 注意 ==
このプラグインはFirefox 3.1用です

== Command ==
:clearp[rivacy]:
    既定の設定でプライバシーデータを削除します

:clearp[rivacy] -l[ist] {itemName}:
    {itemName}のデータを削除します
    省略すると既定の値が用いられます
    既定の値はプライバシー情報の消去設定で行えます

    {itemName}:
        cache: Webキャッシュ
        cookies: Cookie
        offlineApps: Webサイトのオフライン作業用データ
        history: 表示したページの履歴
        formdata: フォームと検索エントリーの履歴
        sessions: 現在のログイン情報

:clearp[rivacy] -t[ime] {timeSpan}:
    現在から{timeSpan}分の期間のデータを削除します
    省略すると既定の値が用いられます
    既定の値はabout:config にある privacy.sanitize.timeSpan になり
    0:全て,1:1時間以内,2:2時間以内,3:4時間以内,4:今日 となっています

    期間内指定で有効なのは
        + cookies
        + history
    のみでそれ以外は指定にかかわらず全て削除されます

    {timeSpan} format:
        数値m数値d数値h
            - m は30日
            - d は日数
            - h は時間
         1m2d3h  32日と3時間 という意味になり現在から32日と3時間前までのデータを削除します

]]></detail>
</VimperatorPlugin>;
liberator.plugins.privacySanitizer = (function(){

var isFx31 = (Application.version.substring(0, 3) == "3.1") 
var prefPrefix = isFx31 ? "privacy.cpd." : "privacy.item.";

var privacyManager = { // {{{
    cache: {
        clear: function(){
            var cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService);
            try {
                getCacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE);
            } catch (er){}
        },
        getPref: function() options.getPref(prefPrefix + "cache")
    },
    cookies: {
        clear: function(range){
            var cookieMgr = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
            if (range){
                let cookiesEnum = cookieMgr.enumrator;
                while (cookiesEnum.hasMoreElements()){
                    let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
                    if (cookie.creationTime > this.range[0])
                        cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
                }
            } else {
                cookieMgr.removeAll();
            }
        },
        getPref: function() options.getPref(prefPrefix + "cookies")
    },
    offlineApps: {
        clear: function(){
            var cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService);
            try {
                cacheService.evictEntries(Ci.nsICache.STORE_OFFLINE);
            } catch(er){}

            var storageManagerService = Cc["@mozilla.org/dom/storagemanager;1"].getService(Ci.nsIDOMStorageManager);
            storageManagerService.clearOfflineApps();
        },
        getPref: function() options.getPref(prefPrefix + "offlineApps")
    },
    history: {
        clear: function(range){
            var globalHistory = Cc["@mozilla.org/browser/global-history;2"].getService(Ci.nsIBrowserHistory);
            if (range)
                globalHistory.removePageByTimeframe(range[0], range[1]);
            else
                globalHistory.removeAllPages();

            try {
                let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
                os.notifyObservers(null, "browser:purge-session-history", "");
            } catch(e){}
            var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch2);
            try {
                prefs.clearUserPref("general.open_location.last_url");
            } catch(er){}
        },
        getPref: function() options.getPref(prefPrefix + "history")
    },
    formdata: {
        clear: function(range){
            var windowManager = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
            var windows = windowManager.getEnumerator("navigator:browser");
            while (windows.hasMoreElements()){
                let searchBar = windows.getNext().document.getElementById("searchbar");
                if (searchBar){
                    searchBar.value = "";
                    searchBar.textbox.editor.transactionManager.clear();
                }
            }
            var formHistory = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
            if (range)
                formHistory.removeEntriesByTimeframe(range[0], range[1]);
            else
                formHistory.removeAllEntries();
        },
        getPref: function() options.getPref(prefPrefix + "formdata")
    },
    downloads: {
        clear: function(range){
            var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
            let dlIDsRemove = [];
            if (range){
                dlMgr.removeDownloadsByTimeframe(range[0], range[1]);
                let dlsEnum = dlMgr.activeDownloads;
                while (dlsEnum.hasMoreElements()){
                    let dl = dlsEnum.next();
                    if (dl.startTime >= range[0])
                        dlIDsRemove.push(dl.id);
                }
            } else {
                dlMgr.cleanUp();
                let dlsEnum = dlMgr.activeDownloads;
                while (dlsEnum.hasMoreElements()){
                    dlIDsRemove.push(dlsEnum.next().id)
                }
            }
            dlIDsRemove.forEach(function(id) {
                dlMgr.removeDownload(id);
            });
        },
        getPref: function() options.getPref(prefPrefix + "downloads")
    },
    sessions: {
        clear: function(){
            var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
            sdr.logoutAndTeardown();
            var authMgr = Cc["@mozilla.org/network/http-auth-manager;1"].getService(Ci.nsIHttpAuthManager);
            authMgr.clearAll();
        },
        getPref: function() options.getPref(prefPrefix + "sessions")
    }
}; // }}}

function getDefaultClearList(){
    var list = [];
    for (let name in privacyManager){
        if (privacyManager[name].getPref())
            list.push(name);
    }
    return list;
}
function getTimeRange(ts, isPref){
    var endDate = Date.now() * 1000;
    var startDate;
    if (isPref){
        if (ts == 0) return null;
        switch (ts){
            case 1:
            case 2:
                startDate = endDate - (ts*60*60*1000000);
                break;
            case 3:
                startDate = endDate - (4*60*60*1000000);
                break;
            case 4:
                let d = new Date();
                d.setHours(0);
                d.setMinutes(0);
                d.setSeconds(0);
                startDate = d.valueof() * 1000;
                break;
            default:
                throw "Invalid time span for clear private data: " + ts;
        }
    } else {
        startDate = endDate - parseTime(ts);
    }
    return [startDate, endDate];

}
// TODO: かなり適当なので要修正
function parseTime(ts){
    var int = parseInt(ts, 10);
    if (isNaN(int)){
        let matches = ts.match(/^(?:(\d+)m)?(?:(\d+)d)?(?:(\d+)h)?$/);
        let [, month, day, hour] = matches;
        let time = (month ? month * 30 * 24 * 60 * 60 * 1000000 : 0) +
                   (day   ? day   *      24 * 60 * 60 * 1000000 : 0) +
                   (hour  ? hour            * 60 * 60 * 1000000 : 0);
        return time;
    }
    return int * 60 * 60 * 1000000;
}
var ops = [
    [["-list", "-l"], commands.OPTION_LIST, null, [[name, "-"] for (name in privacyManager)]],
];
if (isFx31) ops.push([["-time", "-t"], commands.OPTION_STRING]);

// --------------------------
// Command
// --------------------------
commands.addUserCommand(["clearp[rivacy]"], "Clear Privacy data",
    function(args){
        var clearList = args["-data"] || getDefaultClearList();
        var range = null;
        if (isFx31){
            range = args["-time"] ?
                    getTimeRange(args["-time"], false) :
                    getTimeRange(options.getPref("privacy.sanitize.timeSpan"), true);
        }
        clearList.forEach(function(name) this[name].clear(range), plugins.privacySanitizer);
    }, {
        options: ops,
    },
    true);

return privacyManager;

})();

// vim:sw=4 ts=4 et fdm=marker: