沒穿方服

索引

顯示╱隱藏內文
顯示具有 bookmarklet 標籤的文章。 顯示所有文章
顯示具有 bookmarklet 標籤的文章。 顯示所有文章

104 會員中心的「工作」頁面(職缺搜尋),例如 pda.104.com.tw/work/mate/list,一堆公司不寫清楚在做什麼,看介紹才發現又是博奕,直接進黑名單。

但是內建的排除功能不太方便,所以寫了一支簡易自救;
將 bookmarklet 加進書籤 → 104 職缺 忽略特定公司

原始碼放在 bootleq/bookmarklets/blob/master/tw-104-mate-list.js
單純是用 MutationObserver,沒有很特別的地方。


其實 104 有提供排除功能(自訂配對條件才有),可以排除 20 個公司。 104 內建的排除功能

2012-09-22: 現在插入的 HTML 不一定有 <div class="separator"> 部分,script 也改為沒有這個 div 也能取代。

Blogger 文章編輯器「上傳圖片」自動插入的 HTML 為:

做了一支 bookmarklet 把它改成以下格式:

將 bookmarklet 加進書籤 → 清理 blogger 插入的圖片標籤
在文章編輯器的 HTML 模式執行就立刻取代,不保證原文的安全,請自己保重。


原始碼

var
  $textarea = document.getElementById('postingHtmlBox'),
  pattern = [
    '(<div class="separator" style="clear: both; text-align: center;">\n)?',
    '<a href="(.+)"',
    ' imageanchor="1" style=".*"><img border="0" height=".+" width=".+" ',
    'src="(.+)" /></a>(</div>)?'
  ].join(''),
  re = new RegExp(pattern, 'g');

if ($textarea) {
  $textarea.value = $textarea.value.replace(re, function (str, p1, p2, p3, p4, p5) {
    return [
      '<a href="', p2, '">',
      '<img src="', p3, '" alt="" /></a>'
    ].join('');
  });
} else {
  alert('錯誤,找不到 textarea#postingHtmlBox。');
}

以前都靠 PSPad 用來製作 bookmarklet 的簡易 script,現在改讓 Vim 處理。

:Bookmarklet 沒錯誤的話即出現此視窗

這個 funcion 需要上一篇的 Vim funcion:使用 closure compiler 壓縮 js 文件,還有 Vim 的 ruby 支援(其實只是用來 encodeURI ……)

原始碼展開如下,但以後更新會放在 gist: 546828 - [.vimrc] make bookmarklet

command! -nargs=* Bookmarklet call Bookmarklet(<f-args>)
fun! Bookmarklet(...)
  let result = JsCompress(0, 0, '--compilation_level=WHITESPACE_ONLY')
  if len(getqflist()) == 0 && strlen(result) > 0
    let reuse_win = 0
    for winnr in tabpagebuflist(tabpagenr())
      if bufname(winnr) == '[Bookmarklet]'
        exec winnr . 'wincmd w'
        let reuse_win = winnr
      endif
    endfor
    if ! reuse_win
      exec 'belowright 5new [Bookmarklet]'
      setlocal noswapfile bufhidden=wipe winfixheight filetype=javascript
    endif
    let result = substitute(result, '[\n]', '', '')
    setlocal buftype=
    call setline(1, 'javascript:(function(){' . result . '})();')
    if has('ruby')
      ruby require("uri"); VIM::Buffer.current.line = URI.escape(VIM::Buffer.current.line);
    else
      echohl WarningMsg | echomsg "No ruby support. Can't do URI encoding." | echohl None
    endif
    setlocal buftype=nofile
  endif
endf
也不必再打主控密碼了(輸入過的話)

這是 Firefox chrome url 用的 bookmarklet。

我註冊都沒在記密碼的。自動登入失靈的時候,只好打開 Firefox 的「已存密碼」然後找個地方(例如搜尋列)把密碼抄一遍,再複製、貼上表單登入。

要是哪天我被盜帳號,原因就是螢幕擷圖給別人,恰好密碼就在搜尋列吧。為甚麼這麼麻煩,都是「已存密碼」不能右鍵複製的緣故啊。

  1. 於是,把這支 bookmarklet 加入書籤 → 複製已存密碼(右鍵選單)
  2. 打開「已存密碼」(chrome://passwordmgr/content/passwordManager.xul)
  3. 點剛剛加入的書籤。
  4. 就可以開右鍵選單,複製或顯示密碼了。多選也有效。

原始碼拆開如下:

// 應於 chrome://passwordmgr/content/passwordManager.xul 執行
if( ! netscape.security.PrivilegeManager.isPrivilegeEnabled('UniversalXPConnect') || ! document.getElementById('signonsTree') ) {
 alert('操作失敗:\n\n請於下述 URL 執行這支 bookmarklet:\nchrome://passwordmgr/content/passwordManager.xul\n\n');
 return;
}

window.leqPasswordPicker = {

 clipboardHelper: Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper),

 init: function(){

   if( ! document.getElementById("leqPasswordPicker_menupopup") ) {

     var popupset = document.createElement("popupset");

       var menupopup = document.createElement("menupopup");
       menupopup.setAttribute('id', 'leqPasswordPicker_menupopup');

         var menuitem = document.createElement("menuitem");
         menuitem.setAttribute('oncommand', 'leqPasswordPicker.show();');
         menuitem.setAttribute('label', '顯示密碼');

         var menuitem2 = document.createElement("menuitem");
         menuitem2.setAttribute('oncommand', 'leqPasswordPicker.copy();');
         menuitem2.setAttribute('label', '複製密碼');

     menupopup.appendChild(menuitem);
     menupopup.appendChild(menuitem2);
     popupset.appendChild(menupopup);

     document.getElementById('signonsTree').getElementsByTagName('treechildren')[0].setAttribute('context', 'leqPasswordPicker_menupopup');
     document.getElementById('SignonViewerDialog').appendChild(popupset);
   }

 },

 show: function(){
   var selections = GetTreeSelections(signonsTree);
   if (selections.length > 0) {
     var txt = [];
     for (var s = 0 ; s < selections.length ; s++) {
       var i = selections[s];
       txt.push(
           '網 站:' + signonsTreeView.getCellText(i,signonsTree.columns[0])
         + '\n使用者:' + signonsTreeView.getCellText(i,signonsTree.columns[1])
         + '\n密 碼:\n\t' + signonsTreeView.getCellText(i,signonsTree.columns[2]) + '\n'
       );
     }
     alert( txt.join('\n') );
   }
 },

 copy: function(){
   var selections = GetTreeSelections(signonsTree);

   if (selections.length > 0) {
     var txt = [];
     for (var s = 0 ; s < selections.length ; s++) {
       var i = selections[s];
       txt.push( signonsTreeView.getCellText(i,signonsTree.columns[2]) );
     }
     this.clipboardHelper.copyString( txt.join('\n') );

     // 送一個 copy 事件,這段可以不用
     var evt = document.createEvent("HTMLEvents");
     evt.initEvent('copy', true, true);
     document.getElementById('signonsTree').getElementsByTagName('treechildren')[0].dispatchEvent(evt);
   }
 }

};

leqPasswordPicker.init();

原版如前文,縮小的感覺真好,如今再改得稍微親切一點: 1)不在 chrome url 的話會提示, 2)顯示 sqlite 檔案前後大小。

若不在 chrome url 內,會提示你問題。 指定次數。 現在會算出檔案大小了。 動手前,還是要問一下。 縮減比率,看了開心。

將 bookmarklet 加入書籤 → Places Cleaner


原始碼拆開如下:

// 原創想法&程式碼來自 Irvin Chen 的 PlacesCleaner 收藏庫清潔工
// https://addons.mozilla.org/firefox/addon/13860/  (以 MPL 1.1 釋出)
// http://irvin.sto.tw/2009/08/firefox-3.html
// 請於 chrome url (例: chrome://browser/content/places/places.xul ) 中執行
var PlacesCleaner = {

clean: function(){

  // 測試是否有 XPConnect 權限
  if( ! netscape.security.PrivilegeManager.isPrivilegeEnabled('UniversalXPConnect') ) {
    alert('操作失敗:\n請於 chrome url 中執行這支 bookmarklet。\n例: chrome://browser/content/places/places.xul');
    return;
  }

  var PIPlacesDatabase = Components.classes["@mozilla.org/browser/nav-history-service;1"].getService(Components.interfaces.nsPIPlacesDatabase).DBConnection;
  var ProfD = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile);
  var sqliteFile = ProfD.clone();
      sqliteFile.append("places.sqlite");
  var originalSize = sqliteFile.fileSize;

  var intViewTime = parseInt(prompt('清除瀏覽次數不超過「幾次」的歷史紀錄?'),10);
  if(intViewTime+''=='NaN') { alert('輸入值非數字,已停止操作。'); return; }

  if(confirm('目前 places.sqlite 檔案大小為 ' + Math.round(originalSize/10.24)/100 + ' KB。\n若要備份,請選擇儲存檔案的位置:')) {
    var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(Components.interfaces.nsIFilePicker);
    fp.init(window, null, fp.modeSave);
    fp.defaultString='places';
    fp.defaultExtension='sqlite';
    fp.appendFilter(".sqlite","*.sqlite");
    if (fp.show() != fp.returnCancel) {
      if (fp.file.exists()) fp.file.remove(true);

      try {
        sqliteFile.copyTo( fp.file.parent, fp.file.leafName );
      }
      catch(e) {
        alert('操作失敗:無法儲存備份檔案。');
        return;
      }
    }
  }

  if(!confirm('確定要開始清除了嗎?\n也許會花上一點時間……')) return;
  try {
    // 取自 https://addons.mozilla.org/firefox/addon/13860/  原始碼授權條款: MPL 1.1
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_historyvisits WHERE place_id IN (SELECT id FROM moz_places WHERE visit_count <=" + intViewTime + ");");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_places WHERE (visit_count <=" + intViewTime + " AND hidden <> 1 AND id NOT IN (SELECT place_id FROM moz_annos UNION SELECT fk FROM moz_bookmarks));");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_inputhistory WHERE place_id NOT IN (SELECT id FROM moz_places);");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_favicons WHERE id NOT IN (SELECT favicon_id FROM moz_places);");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_annos WHERE anno_attribute_id IN (SELECT id FROM moz_anno_attributes WHERE name = 'google-toolbar/thumbnail-score' OR name = 'google-toolbar/thumbnail');");
    PIPlacesDatabase.executeSimpleSQL("VACUUM");
  }
  catch(e) {
    alert('操作失敗:清除中出了狀況……');
    alert('Places Cleaner bookmarklet 錯誤訊息:\n '+e);
    return;
  }

  // 重新取得 fileSize
  sqliteFile = ProfD.clone();
  sqliteFile.append("places.sqlite");

  var ratio = Math.round( (originalSize-sqliteFile.fileSize)*10000/originalSize )/100;
  alert('清除完畢。\n\n檔案大小:' + Math.round(originalSize/10.24)/100 + ' KB → ' + Math.round(sqliteFile.fileSize/10.24)/100 + ' KB(已縮減 ' + ratio  + ' %)');

}

};

PlacesCleaner.clean();

2009-10-14: 稍作修改,新版比較親切好用,請參考 PlacesCleaner bookmarklet (更新)

原創想法&程式碼取自 Irvin Chen 的貢獻,詳見:

這支 bookmarklet 會跑原文所述的幾行 SQL,可指定刪除瀏覽次數(不超過)多少的紀錄,且刪除前可備份 places.sqlite。
※請於 Chrome URL 中執行(例: chrome://browser/content/places/places.xul )否則沒效。

將 bookmarklet 加入書籤 → Places Cleaner


原始碼拆開如下:

// 原創想法&程式碼來自 Irvin Chen 的 PlacesCleaner 收藏庫清潔工
// https://addons.mozilla.org/firefox/addon/13860/  (以 MPL 1.1 釋出)
// http://irvin.sto.tw/2009/08/firefox-3.html
// 請於 chrome url (例: chrome://browser/content/places/places.xul ) 中執行
var PlacesCleaner = {

clean: function(){

  var PIPlacesDatabase = Components.classes["@mozilla.org/browser/nav-history-service;1"].getService(Components.interfaces.nsPIPlacesDatabase).DBConnection;
  var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(Components.interfaces.nsIFilePicker);
  
  var intViewTime = parseInt(prompt('清除瀏覽次數不超過「幾次」的歷史紀錄?'),10);
  if(intViewTime+''=='NaN') { alert('輸入值非數字,已停止操作。'); return; }

  if(confirm('若要備份 places.sqlite,請選擇儲存檔案的位置:')) {
    fp.init(window, null, fp.modeSave);
    fp.defaultString='places';
    fp.defaultExtension='sqlite';
    fp.appendFilter(".sqlite","*.sqlite");
    if (fp.show() != fp.returnCancel) {
      if (fp.file.exists()) fp.file.remove(true);
      var oldFile = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile);
      oldFile = oldFile.clone();
      oldFile.append("places.sqlite");
      try {
        oldFile.copyTo( fp.file.parent, fp.file.leafName );
      }
      catch(e) {
        alert('操作失敗:無法儲存備份檔案。');
        return;
      }
    }
  }
  
  if(!confirm('確定要開始清除了嗎?\n也許會花上一點時間……')) return;
  try {
    // 取自 https://addons.mozilla.org/firefox/addon/13860/  原始碼授權條款: MPL 1.1
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_historyvisits WHERE place_id IN (SELECT id FROM moz_places WHERE visit_count <=" + intViewTime + ");");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_places WHERE (visit_count <=" + intViewTime + " AND hidden <> 1 AND id NOT IN (SELECT place_id FROM moz_annos UNION SELECT fk FROM moz_bookmarks));");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_inputhistory WHERE place_id NOT IN (SELECT id FROM moz_places);");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_favicons WHERE id NOT IN (SELECT favicon_id FROM moz_places);");
    PIPlacesDatabase.executeSimpleSQL("DELETE FROM moz_annos WHERE anno_attribute_id IN (SELECT id FROM moz_anno_attributes WHERE name = 'google-toolbar/thumbnail-score' OR name = 'google-toolbar/thumbnail');");
    PIPlacesDatabase.executeSimpleSQL("VACUUM");
  }
  catch(e) {
    alert('操作失敗:清除中出了狀況……');
    alert('Places Cleaner bookmarklet 錯誤訊息:\n '+e);
    return;
  }
  alert('清除完畢。');

}

};

PlacesCleaner.clean();

可以插好多的表單

最近有些 blogger 管理者把留言表單拿掉了,換成彈出視窗,或整頁模式。

這支 bookmarklet 會試著把表單塞回網頁:
自助嵌入 blogger 留言表單


用了沒反應,蛤~怎會這樣?

1)僅在「單篇文章」頁面有用。 2)因各人模板不同,也可能沒效。 3)只支援 Firefox、Google Chrome、Opera 和 Safari 等。

原始碼拆開如下:

var cmt_div = document.getElementById("comments");
var url, anchor, a = cmt_div.getElementsByTagName("a");
for( i = 0 ; i < a.length ; i++ ) {
  if( a[i].hasAttribute("href") && a[i].getAttribute("href").match(/g\?blogID=([0-9]+)\&postID=([0-9]+)/) ) {
    url = 'http://www.blogger.com/comment-iframe.g?blogID=' + RegExp.$1 + '&postID=' + RegExp.$2;
    anchor = a[i];
  }
}
cmt_iframe = document.createElement("object");
cmt_iframe.setAttribute('data', url);
cmt_iframe.setAttribute('style', 'display:block; width:100%; height:250px; color:#000; background-color:#f4f1f4; opacity:0.95; padding:0 4px; margin:2px 0; border:2px solid #fff;');
anchor.parentNode.insertBefore( cmt_iframe, anchor.nextSibling );
window.scrollTo( cmt_div.offsetLeft, cmt_div.offsetTop );

巴哈哈啦區有個白痴功能,就是點連結會跳出對話框詢問是否確定,是的話便在新視窗開啟——相當多此一舉而且(亂開窗)很落伍;然後為了做這效果,還把網址改成 javascript:confirmLink('http://……') 造成按中鍵也不能在新分頁開鏈結(會開分頁,但內容錯誤)……我點過好幾次,點一次怒一次。

其實在我怒滿之前,已經有人提供解法了:願意裝 Greasemonkey 的話請參考 Goston’s Blog » [FF] 續 - 讓巴哈姆特哈拉區的連結不再以新視窗開啟

不過我沒裝 Greasemonkey 所以繼續集氣…… 結果、最後就寫了 bookmarklet 來做一樣的事:

將 bookmarklet 加入書籤 → 治療巴哈白痴連結(confirmLink)

又發現巴哈插入影片(YouTube 等)的鏈結網址是 http://www.youtube.com/v/……,連過去是全畫面的 .swf 我用不到。所以順便加上把網址變為 http://www.youtube.com/watch?v=…… 的功能,連到影片在 YouTube 的原始網址:

將 bookmarklet 加入書籤 → 治療巴哈白痴連結(兩種功能)

※第二支有副作用,會讓顯示影片的功能爆掉,因為巴哈會用 JavaScript 取鏈結網址來插入 flash。不過反正我的 Stop Autoplay 把全站 flash 都擋掉了所以我也懶得理哩。


原始碼拆開如下:

var links = document.links;
for(var i = 0; i < links.length; i++) {

  var url = links[i].getAttribute('href');

  if (/^javascript:confirmLink/.test(url)) {
    var r = url.replace( /^javascript:confirmLink\(\'(.+)\'\)/i, "$1");
    r = decodeURIComponent(r);
    links[i].setAttribute('href', r);
  }
  else if (/www.youtube.com/.test(url)) {
    var r = url.replace('http://www.youtube.com/v/', 'http://www.youtube.com/watch?v=');
    links[i].setAttribute('href', r);
  }

}

再看一個原設計的荒謬,就是它會提示「您需要先登入才能觀看喔」可是網址明明就顯示在狀態列,為甚麼不能看呢?為甚麼要掩耳盜鈴呢?

圖片的話,點中鍵就可以看了

功能類似 Extension List DumperListZilla 兩個套件,把附加元件清單匯出成檔案。
輸出格式可用樣板(是我參考 Ext JS 寫的陽春版)方式調整,但要改原始碼、比較麻煩就是了。

Bookmarklet 在這裡 → 匯出附加元件列表(僅支援 Firefox 3 系列)


使用說明:

  1. 先將 Bookmarklet 加入書籤。
  2. 在 Firefox 網址列輸入 chrome://mozapps/content/extensions/extensions.xul 並開啟。
    (可見動作是在 Chrome URL 執行的,小提醒:別讓壞人的 bookmarklet 這麼做)
  3. 要匯出擴充套件的話,就先點選「擴充套件」分頁;
    同理,佈景主題和外掛程式也是可以匯出的。
  4. 執行 bookmarklet,順利的話會跑出如下訊息:

    按「確定」繼續

    接下來照著訊息指示進行,便能完成匯出了。

改造說明:(有點囉唆,看不懂原始碼再參考吧)

為了調整輸出格式,需要修改原始碼;可自製幾支不同的樣板,存成不同的 bookmarklet 備用(不會壓製 bookmarklet 的話可參考前文)。原始碼附在文末,重要部分如下:

  • 樣板部分(template-prefix 等)

    樣板由 template_prefixtemplate_itemtemplate_suffix 組成,輸出時會把頭(_prefix)接上一連串的項目(_item)再接上尾(_suffix)。

    樣板字串中 ${foo} 型式的元素,會被代換成實際值,例如 ${name} 會變成套件名稱。
    ※要注意的是 _prefix、_suffix 和 _item 能用的 ${foo} 不同。預設 _prefix、_suffix 專用的是 ${date}${total},其他則是 _item 專用。

  • 過濾不要的資料(data = data.filter)

    清單資料會儲存在 data 陣列裡面,要過濾的話得在
    data = data.filter(function(item, idx, arr){
    內處理。例如 return item['hidden']!='true'; 會把隱藏的項目拿掉。

  • helpers(var helpers = )

    樣板字串可以用 ${foo:helper} 這樣的元素來修改資料,例如 ${isDisabled:isTrue( 「已停用」,)} 會在套件停用時顯示「已停用」。

    相應原始碼在 var helpers = { 裡面有幾個函數,傳回值就是要輸出的字串。
    template_item 可用的函數,基本型為:
    函數名稱 : function( item, value[, 參數1, 參數2, …] ){}

    • item 為目前的 template_item,就是 data 陣列中的一項。
    • value${foo:helper} 之中 foo 的實際值,例如 ${name:helper} 傳回的是套件名稱。
    • 其餘參數,在樣板中是以 ${foo:helper(參數1,參數2)} 型式傳進來的。

原始碼:

var leqExtDumper = {

dump: function(){
  if(typeof(gExtensionsView)=='undefined') return(alert('出了點狀況,無法繼續;\n\n請在 Firefox 3 的\nchrome://mozapps/content/extensions/extensions.xul\n執行這支 bookmarklet.'));
  
  var txt = '';
  var data = [];
  
  // 樣板
  var template_prefix = '附加元件列表 (${total}) - ${date}\n<ul>';
  var template_item   = '<li>\n'
                      + '${homepageURL:addLink} - ${version} - <small>${optionsURL}</small>'
                      + '${isDisabled:isTrue( (已停用),)} ${compatible:isFalse( (與應用程式不相容),)}'
                      + '<br />\n'
                      + '適用於: Firefox ${min} - ${max}\n'
                      + '<p>${description}</p>\n'
                      + '</li>\n';
  var template_suffix = '</ul>';
  
  var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(Components.interfaces.nsIFilePicker);
  var charset = "UTF-8";
  var os = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
  var fos = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);

  fp.init(window, null, fp.modeSave);
  fp.defaultExtension='txt';
  fp.defaultString='附加元件列表';
  fp.appendFilters(fp.filterText | fp.filterHTML);
  
  gExtensionsView.children.forEach(
    function(i){
      var item = gExtensionManager.getItemForID(getIDFromResourceURI(i.getAttribute('id')));
      data.push({
        name        : i.getAttribute('name'),         // 套件名稱
        id          : i.getAttribute('id'),           // 套件 id
        min         : item ? item.minAppVersion : '', // 支援的最低 Firefox 版本
        max         : item ? item.maxAppVersion : '', // 支援的最高 Firefox 版本
        iconURL     : i.getAttribute('iconURL'),      // 套件的圖示位址
        version     : i.getAttribute('version'),      // 套件版本
        description : i.getAttribute('description'),  // 套件的敘述
        homepageURL : i.getAttribute('homepageURL'),  // 套件首頁
        optionsURL  : i.getAttribute('optionsURL'),   // 套件的選項位址
        hidden      : i.getAttribute('hidden'),       // 是否為隱藏套件
        isDisabled  : i.getAttribute('isDisabled'),   // 是否已停用
        compatible  : i.getAttribute('compatible')    // 是否與目前 Firefox 版本相容
      });
    }, this
  );
  
  // 過濾不要列出的項目 (return false 的資料會被排除)
  data = data.filter(function(item, idx, arr){
    return item['hidden']!='true';
  });

  var helpers = {
    isTrue  : function(item, value, strTrue, strFalse){
      return value=='true' ? strTrue : strFalse;
    },
    isFalse : function(item, value, strTrue, strFalse){
      return value=='false' ? strTrue : strFalse;
    },
    addLink : function(item, value){
      return value=='' ? item['name'] : '<a href="' + value + '">' + item['name'] + '</a>';
    },
    // 以下僅適用於 template_prefix 與 template_suffix
    date    : function(){
      return (new Date()).toLocaleString();
    },
    total   : function(){
      return data.length;
    }
  };
  
  // template_prefix
  // 取代 ${key} 形式的字串
  txt = template_prefix.replace( /\$\{([\w-]+)\}/g, function(m, key){ return helpers[key].apply(this); } );

  data.forEach(
    function(i){
      // template_item
      // 取代 ${key:format(args)} 形式的字串 - 參考自 Ext.Template library - http://www.extjs.com
      txt += template_item.replace( /\$\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g, function(m, key, format, args){
        if(format){
          args = [i, i[key]].concat(args.split(','));
          return helpers[format].apply(this, args);
        }
        else {
          return i[key] !== undefined ? i[key] : "";
        }
      });
    }, this
  );
  // template_suffix
  txt += template_suffix.replace( /\$\{([\w-]+)\}/g, function(m, key){ return helpers[key].apply(this); } );

  
  if(!confirm('請選擇匯出檔案的位置:')) return;
  
  if (fp.show() != fp.returnCancel) {
    if (fp.file.exists()) fp.file.remove(true);
    fos.init(fp.file, 0x02 | 0x08 | 0x20, 0666, 0);
    os.init(fos, charset, 0, 0x0000);
    os.writeString(txt);
    os.close();
    fos.close();
    alert('資料已匯出至 ' + fp.file.path);
  }

}

};

leqExtDumper.dump();

製作 bookmarklet 有個問題是,怎麼把寫好的 JavaScript 縮成一行?這方面網路上有個 Bookmarklet Builder 看來不錯,像是將「%20」轉成空白這樣的功能也有準備。

而我目前的作法,是拷貝 PSPad 的擴充 script PSPadJSMin 並稍作修改(原本是用來壓縮 JavaScript 的),以後在 PSPad 下就可以「寫 JavaScript → 按熱鍵壓成 bookmarklet → 複製、貼上網址列測試」——也還算方便。

因為只有小改,故不好意思提供下載,僅列改法:

  1. 先去官網PSPadJSMin.zip ……解開放進 PSPad 的 Script/JScript 資料夾。
  2. 直接複製 PSPadJSMin.js 並改名為 PSPadJSMin_bookmarklet.js
  3. 編輯 PSPadJSMin_bookmarklet.js 內容:
    • function JSMin() 改名為 function JSMinBookmarklet()
      其實愛改什麼名字都行,只要前後對應別弄錯即可。
    • function jsmin(input, level) 底部的:
      return ret;
      改為
      return 'javascript:(function(){' + ret.replace(/\n/g,'').replace(/([\s\"])/g, function(s,p){return encodeURI(p);}) + '})();';
      這樣會把 JSMin 壓的東西縮成一行,對空白、引號做個 encodeURI,最後包成 javascript:(function(){高濃縮原始碼})(); 的樣子。
    • function Init() 內部的:
      addMenuItem("JSMin", "", "JSMin", "Ctrl+Shift+J");
      改為
      addMenuItem("bookmarklet", "", "JSMinBookmarklet", "Ctrl+Alt+B");
      這樣會在 PSPad 工具列的 Scripts 選單加上 bookmarklet 項目,並綁上熱鍵。
  4. 執行 PSPad 工具列的 Script → 重新編譯 Scripts
    這邊我是有先重開 PSPad,否則似乎不會成功。

以下是 blogger 文章編輯器「上傳一張圖片」自動產生的原始碼:

夾雜許多用途不明的東西,而且 bp.blogspot.com 這樣的鏈結會讓某些 lightbox 特效掛掉。 (2009-05-19: 後來發現 lightbox 掛掉其實是鏈結中「-h」禁止外連造成的。
所以我做了一支 bookmarklet 把它清理成下面的格式:

將 bookmarklet 加進書籤 → 清理 blogger 插入的圖片標籤
在文章編輯器的「修改 Html」模式點了就立刻執行,不會多問,所以請自己保重(文章弄壞、Ctrl + Z 又沒用的話,我也沒辦法負責啊。)

原始碼拆解如下:

javascript:
document.getElementById(&apos;textarea&apos;).value = document.getElementById(&apos;textarea&apos;).value.replace(
  /<a onblur=\".+\" href=\"(.+)\"><img style=\".+\" src=\"http:\/\/(\d)+\.bp\.blogspot\.com\/(.+)" border=\"0\" alt=\"\"id=\".+\" \/><\/a>/g,
  function(str, p1, p2, p3) {
    return &apos;<a href="http://lh&apos; + (parseInt(p2)+2) + &apos;.ggpht.com\/&apos; + p3.replace(/s(\d)+(-h)?\//,&apos;&apos;) + &apos;"><img src="http://lh&apos; + (parseInt(p2)+2) + &apos;.ggpht.com\/&apos; + p3 + &apos;" alt="" /></a>\n&apos;;
  }
);void 1

這支 bookmarklet 只是寫好玩的,在 Firefox 的 Chrome URL 執行,就可以在電腦裡存一個文字檔,真是方便(所以最好別讓來路不明的 bookmarklet 做這種事)。

使用步驟:

  1. 將 bookmarklet 加進書籤 → 匯出 FireGestures 手勢對應
  2. 在網址列輸入 chrome://firegestures/content/prefs.xul 進入 FireGestures 選項。
  3. 點選剛剛加入的書籤,就會出現對話框詢問選項:

    設定輸出選項

  4. 選擇要把文字檔存到哪裡。

    另存新檔(畫面剪裁過)

  5. 完成後檔案就存好了。

    完成訊息

  6. 把文字檔拖曳進 Firefox,看看結果。

    完成畫面(其實是文字檔)

疑難排解:

只有在 FireGestures 1.1.5 測試,日後作者隨便改點東西我可能就爆了,所以有疑難也不會排解啦。


原始碼拆解:

var FGTreeDumper = {

dump: function(){
  
  var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
  var charset = "UTF-8";
  var os = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
  var fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);

  fp.init(window, null, fp.modeSave);
  fp.defaultExtension='txt';
  fp.defaultString='FireGesturesTreeDump';
  fp.appendFilters(fp.filterText);
  
  /*
   *  1: 忽略未使用項目
   *  2: 忽略 type 欄位
   *  4: 忽略 flag 欄位
   *  8: 忽略「命令」欄位
  */
  var option = prompt('- 忽略未使用的項目,請選 1\n- 忽略 type 資料,請選 2\n- 忽略 flag 資料(例:firefox3)請選 4\n- 忽略「命令」資料,請選 8\n- 綜合選項,請將各選項數字相加,例如 15\n\n請選擇,或不輸入以使用預設選項:');

  if (fp.show() != fp.returnCancel) {
    if (fp.file.exists()) fp.file.remove(true);
    fos.init(fp.file, 0x02 | 0x08 | 0x20, 0666, 0);
    var data = '';
    var mappings = gMappingsArray;
    mappings.forEach(function(item){
      if( ! ((option==1||option==3||option==5||option==7||option==9||option==11||option==15) && item[kDirectionCol]=='') ) {
        var cell = [];
        item.forEach(function(col,idx){
          if(   ! ((option==2||option==3||option==6||option==7||option==10||option==11||option==14||option==15) && idx==kTypeCol)
             && ! ((option==4||option==5||option==6||option==7||option==12||option==13||option==14||option==15) && idx==kFlagsCol)
             && ! ((option==8||option==9||option==10||option==11||option==12||option==13||option==14||option==15) && idx==kCommandCol)
            ) {
            cell.push(col);
          }
        });
        data += cell.join('\t') + '\n';
      }
    });
    os.init(fos, charset, 0, 0x0000);
    os.writeString(data);
    os.close();
    fos.close();
    alert('已完成。');
  }
}

};

FGTreeDumper.dump();

除了 Mozilla Taiwan 討論區: 如何刪除在 about:config 所建立的資料 提到的方法外,以 bookmarklet 協助刪除也是個選擇。

將 bookmarklet 加進書籤 → about:config 刪除偏好項目

使用步驟:

  1. 備份 pref.js(保險起見。刪除跟重設不同,砍掉就真的沒救了!)
  2. 由網址列進入 about:config(在一般網頁不可執行)
  3. 執行 bookmarklet,會詢問要刪除的分支(分支指的是 nsIPrefBranch),可以輸入:

    輸入要刪除的分支

    • 整個分支

      例如輸入 browser.startup 會刪除 browser.startup.homepage 等項目。可以將點(.)也輸入而變成 browser.startup.,效果一樣。
      註:與 about:config 的篩選條件不同,輸入 browse 或 browser.startup.h 皆無法找到前述項目。

    • 單一項目

      例如輸入 browser.startup.homepage 就會刪掉 browser.startup.homepage 這個項目。

  4. 出現確認畫面。若項目很多(超過 50 個)會再確認一遍。

    確認要刪除的項目

  5. 出現「已執行」訊息。不過我沒有真的檢查是否已刪除,需要重新載入才知道成功與否。

    已執行


原始碼拆解:

javascript:
var v = "", b = null;
try {
  b = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
}
catch(e) {
  alert("錯誤:無法取得 Firefox 服務。");
}
if(b) {
  while( v == "" ) {
    v = prompt("請輸入要刪除的擴充套件分支:\n(例如「megaupload.toolbar.」)","");
  }
  if( v != null ) {
    var str = "" ;
    var dot = (v.substr(-1)==".");
    var count = 0;
    b = b.getBranch(v);
    b.getChildList("",{}).forEach(function(i) {
      if( dot || i[0]=="." || i=="" ) {
        str += ( "\t" + v + i + "\n" );
        count++;
      }
    });
    if(count==0) {alert("沒有符合的項目。");}
    else if( confirm(count+" 個項目將被刪除:\n" + str + "\n") ) {
      if( count<=50 || confirm("(項目超過 50 個)確定要刪除嗎?") ) {
        b.deleteBranch("");
        alert("已執行。");
      }
    }
  }
}
void 1

小改之前寫的 bookmarklet,現在不限於 Blogger,在 Google SitesGoogle Code 等應該都能通用了。

將 bookmarklet 加進書籤便可使用 → 設定排除用的 Cookie (Google Analytics)

原始碼拆開如下:

if(typeof(_gat)=='undefined'){
  alert("無法在這個網頁運作(我們找不到 _gat),抱歉。");
  return;
}
var leqTracker = _gat._getTracker();
if(leqTracker){
  var v=prompt("請輸入要用於「篩選器模式」的字串:","test_value");
  if(v!=null){
    leqTracker._setVar(v);
    alert("已設定 cookie: "+v);
  }
}

細節請參考前文 銀盾石榴: Google Analytics:按 cookie 內容排除自己的流量

2008-06-30: 修改為不限 blogspot 專用,請參考新版 bookmarklet

官方說明的具體步驟中,首先的 <body onLoad="javascript pageTracker._setVar('test_value');"> 跟後面「設定篩選器」看起來似乎沒關聯:

「無報告」原文推測為 no_report(不知道哪來的)

請注意「篩選器模式」的值是怪異的「無報告」,換成英文版一看:

英文的 Filter Patterm: test_value

變成 test_value 了 …… 跟 pageTracker._setVar('test_value'); 設的值吻合 —— 所以是翻譯沒做好啊。


實際上 pageTracker._setVar('test_value'); 會在電腦上儲存一個 __utmv=123456789.test_value 的 cookie,其中 123456789 每個網站都不一樣。一旦訪客電腦裡有這個 cookie,在 Google Analytics 的「訪客 - 使用者定義」就會出現:

test_value 出現了

然後設定「篩選器模式」為 test_value,該報告就會被排除,不再出現。

如此一來,具體流程「建立會設定 cookie 的網頁 → 造訪該網頁以設定 cookie → 設定篩選器排除具有該 cookie 的電腦」應該很清楚了。


還沒完,blogspot 使用者還有問題:要在哪建立新網頁?

新網頁需要在 body onLoad 動手腳,而且不能讓一般訪客看到(有看到就會被下 cookie 而遭排除)—— 很麻煩。

所以我想改用 bookmarklet 的方式,內容就是找到 pageTracker,請他 ._setVar 即可。原始碼拆開如下:

javascript:
if(pageTracker) {
  var v=prompt(%22請輸入要用於「篩選器模式」的字串:%22,%22test_value%22);
  if(v!=null) {
    pageTracker._setVar(v);alert(%22已設定 cookie: %22+v);
  }
}
void 1

將 bookmarklet 加進書籤便可使用 → 設定排除用的 Cookie (Google Analytics)

完整流程:

  1. 將前述 bookmarklet 加進書籤列。
  2. 打開網站的首頁,然後點前述 bookmarklet,應該會跳出對話框:

    對話框內容
    (如果沒反應,就是找不到 pageTracker 而發生錯誤,不會有任何效果)

  3. 填一個字串進去。不一定要填 test_value
  4. 前往 Google Analytics 設定篩選器,「篩選器模式」填剛才回答的字串。
    我的設定長這樣,請別連名字都照抄:

    篩選器設定

  5. 別忘了把篩選器套用至網站設定檔。

這樣就完成了,我測了兩天,流量有正確尷尬下去,所以應該是有效的。