沒穿方服

索引

顯示╱隱藏內文

巴哈哈啦區有個白痴功能,就是點連結會跳出對話框詢問是否確定,是的話便在新視窗開啟——相當多此一舉而且(亂開窗)很落伍;然後為了做這效果,還把網址改成 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);
  }

}

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

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

輸出文字時使用 nl2brURL Helper 裡的 auto_link,例如 nl2br(auto_link($text));
會造成 JavaScript 的 syntax error:

else document.write(unescape(l[i]));}<br />\n

原因是 auto_link 會用 URL Helper 的 safe_mailto 來替換 mailto 鏈結,插入一段 JavaScript,然後裡面有一個換行符號 \n 會被換成 <br /> 找麻煩。

解決方法是 auto_link(nl2br($text)); 順序倒過來即可……

CI 1.7.1 原裝的 auto_link 沒有選項把 safe_mailto 換成 mailto。

下面是附加元件 Noise 的使用例。套用 ACQUIRE 遊戲的音效,算是湊個 theme 吧。
※播放中覺得吵是正常的,因為真的有點吵……

YouTube - Firefox 聲音主題 - ACQUIRE Noise (add-on: Noise)

這份設定檔也放在 noise_theme_acquire_rdf.7z (DivShare)——僅 RDF,不分享聲音檔。

功能類似 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();

天誅4炸完日本要去炸歐美了,這篇是 1UP.com 訪談摘要,只節錄我自己想留的部分,旨在記錄,不按原文。受訪對象是 ACQUIRE 遠藤琢磨和金山圭輔……哀哉,我心目中的英雄,做出這樣的遊戲也只能窩囊啊。

訪談原文:Tenchu: Shadow Assassins Preview for the Wii from 1UP.com


  • 1UP:你們從 K2 手中接回天誅的開發,有什麼關鍵元素,是4想要帶回來的?
    金山:進行任務、達成目標的自由度。在轉手後可能被忽視了,這次真的很想加回來。
  • 1UP:對過去的天誅,這次最想改變的是什麼?
    金山:增加親和力。人人都能輕易上手,又不失深度。
  • 1UP:對轉手開發後,故事中大量的鬼怪(mythology)元素有何看法?
    金山:其實一直都有超自然元素,只是變得比較多,導致我們更想讓4的故事偏向真實、傳統。另外對於大量角色進出、大量穿插劇情的作法,這次也改回注重力丸、彩女的人生相貌。
  • 1UP:天誅 Z(千乱)的角色自訂功能,似乎較少見於日製遊戲,對此有何看法?
    金山:以天誅4而言,我們試著強調任務攻略的自訂性,例如 "I'd like to play this way, so I'm going to go up on the rooftops(屋頂)."
  • 1UP:力丸在原本的故事中,是以被落石掩埋為結局;這次你們終於可以揭露接下來的發展,相關事件會出現在4裡面嗎?
    遠藤:天誅3之後我們就沒有參與,故事也大幅偏離我們的方向;我們不試著把其他作品漏失的劇情連上,而是專注於制定一個充實的故事,分成三部作,希望在續作繼續下去。
  • 1UP:所以基本上力丸在天誅1結局中活了下來。
    遠藤:這個本來是我們想在3揭露的,我們有自己的想法,不過顯然事情在我們的控制之外……既然已經做了,我們會延續既定的故事——力丸沒有解釋地活著。
  • 1UP:From Software 是如何接洽你們來製作天誅的?
    遠藤:一開始沒有具體的對談,而是朝倉紀行先生開始跟 From 提起讓我們繼續製作天誅……既然對話開始了,我們也認為是個好主意。
  • 1UP:你們如何區隔天誅和忍道戒?
    遠藤:我不知道,有個差別是我們不會再製作新的忍道遊戲了。
  • 1UP:為甚麼那個?
    遠藤:發行商 Spike 認為該系列失敗了。我們提出開發新的「忍道」時,便遭到嚴重拒絕,取而代之的是要我們製作侍道。
  • 1UP:忍道是你們停手天誅、著手侍道那時候製作的,你們真的很想開發 Tenchu-like 的遊戲吧?
    遠藤:確實沒錯,那時候 From Software 對我們開發忍者遊戲很不爽。
  • 1UP:忍道 fans 會很失望。
    遠藤:寄數不清的 email 和請願書讓 spike 知道,也許他們會改變主意。不過既然我們正在製作天誅,當然還是要專注在天誅上面。

對天誅4內容的說法跟先前宣傳差不多——自由度跟親和力,就是這次做不好的東西啊!
「製作跟說的方向不符」其實侍道3也有相同狀況……很荒唐的問題。