20120323

開發自己的 Firefox add-on 附加元件(四) Panel 模組

Panel 是一個對話框(dialog),它的內容被指定為HTML,同時也可以在其中執行scripts。因此Panel的內容和行為是根據我們可以用 HTML、 CSS 和 JavaScript 做什麼而定。

以下的截圖畫面顯示的 panel的內容是由目前所開啟的瀏覽器分頁的清單所組成:



Panel 適合用在顯示臨時的介面給使用者,比modal dialog更方便讓使用者忽略或關閉,因為只要使用者和該應用程式介面以外的部分互動,panel就會隱藏。

Panel的內容會在其被建立的同時、panel顯示(show)之前載入,且在panel被隱藏時仍會進行內容載入的動作,因此可以做到將panel維持在背景而更新其內容為下次顯示所需。

我們可以藉由監聽(listen)panel的show 和hide 事件,來使 add-on 接收panel顯示或隱藏的通知。

Panel 的內容
Panel的內容被指定為HTML,藉由panel 的建構子中contentURL屬性來指定HTML檔案的URL並載入。

可以載入遠端的URL:
var panel = require("panel").Panel({
  width: 180,
  height: 180,
  contentURL: "https://en.wikipedia.org/w/index.php?title=Jetpackuseformat=mobile"
});
 
panel.show();

另外,也可以載入封裝在我們的add-on中的HTML檔,這通常是建立dialog的方式。
若要載入封裝在add-on中的HTML,我們要將HTML放在add-on的data目錄下,並用self模組的 data.url() 方法,如下:
var panel = require("panel").Panel({
  contentURL: require("self").data.url("myFile.html")
});
 
panel.show();

Scripting Panel Content
我們不能從main add-on 程式直接存取panel的內容,而必須載入script到panel中。在SDK中,這種scripts稱為"content scripts",因為content scripts是專門用來處理web content。

content scripts 可以存取他們所附加上的內容,但他們不能使用SDK's APIs。
因此實作一個完整的解決方案通常代表,我們必須在content script 和 the main add-on code之間傳送訊息。
  • 我們可以透過Panel()建構子中的contentScript或contentScriptFile,以指定一個或多個content scripts來載入到一個panel中。
  • 我們可以藉由postMessage() API 或 (較常用的) the port API來和script溝通。
舉例來說,有一個add-on的content script監聽滑鼠點擊panel中的連結的事件,並將目標URL傳送給main add-on code。content script 使用self.port.emit()來傳送訊息,而add-on script使用panel.port.on()來接收這些訊息。
var myScript = "window.addEventListener('click', function(event) {" +
               "  var t = event.target;" +
               "  if (t.nodeName == 'A')" +
               "    self.port.emit('click-link', t.toString());" +
               "}, false);"
 
var panel = require("panel").Panel({
  contentURL: "http://www.bbc.co.uk/mobile/index.html",
  contentScript: myScript
});
 
panel.port.on("click-link", function(url) {
  console.log(url);
});
 
panel.show();
這個例子使用了contentScript來以string提供 script。通常情況下,使用contentScriptFile來用URL來指向存放在add-on中的data資料夾裡的script 檔案會比較好,因為這樣程式較為簡潔易懂,也較易debug。

程式碼中第 13 行的 Console 用法請見這裡

取得使用者輸入
下面的 add-on 加入一個 widget ,當其被點擊時會顯示panel。panel中只包含一個 textarea 元件:當使用者按下return key,textarea 裡的內容就會被傳送給main add-on code。

這個 add-on 包含三個檔案:
  • main.js: the main add-on code, that creates the widget and panel
  • get-text.js: the content script that interacts with the panel content
  • text-entry.html: the panel content itself, specified as HTML
"main.js" 儲存在 add-on 專案中的 lib 目錄,而其他兩個檔案在 add-on 專案中的 data 目錄,結構如下:
my-addon/
-data/
--get-text.js
--text-entry.html
-lib/
--main.js

"main.js":
var data = require("self").data;
 
// Create a panel whose content is defined in "text-entry.html".
// Attach a content script called "get-text.js".
var text_entry = require("panel").Panel({
  width: 212,
  height: 200,
  contentURL: data.url("text-entry.html"),
  contentScriptFile: data.url("get-text.js")
});
 
// Send the content script a message called "show" when
// the panel is shown.
text_entry.on("show", function() {
  text_entry.port.emit("show");
});
 
// Listen for messages called "text-entered" coming from
// the content script. The message payload is the text the user
// entered.
// In this implementation we'll just log the text to the console.
text_entry.port.on("text-entered", function (text) {
  console.log(text);
  text_entry.hide();
});
 
// Create a widget, and attach the panel to it, so the panel is
// shown when the user clicks the widget.
require("widget").Widget({
  label: "Text entry",
  id: "text-entry",
  contentURL: "http://www.mozilla.org/favicon.ico",
  panel: text_entry
});
content script "get-text.js":
self.port.on("show", function (arg) {
  var textArea = document.getElementById('edit-box');
  textArea.focus();
  // When the user hits return, send a message to main.js.
  // The message payload is the contents of the edit box.
  textArea.onkeyup = function(event) {
    if (event.keyCode == 13) {
      // Remove the newline.
      text = textArea.value.replace(/(\r\n|\n|\r)/gm,"");
      self.port.emit("text-entered", text);
      textArea.value = '';
    }
  };
});
最後,"text-entry.html"定義textaream元件:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> 
<head>
  <style type="text/css" media="all">
    textarea {
      margin: 10px;
    }
  </style>
</head> 
<body>
  <textarea rows="10" cols="20" id="edit-box"></textarea>
</body> 
</html>


設定被信任的panel內容(trusted panel content)的樣式
像先前所說的,我們可以將HTML打包到add-on的data目錄底下,然後用這個HTML檔案來定義panel的內容。
我們稱這樣的檔案叫作"trusted" content,因為跟從外部來源載入add-on的內容不同,add-on的作者很清楚的知道這個trusted content是在做什麼的。和trusted content互動,我們不必使用content scripts:可以直接將script include到HTML檔案中來使用。

當我們使用data目錄下的HTML檔案來表示panel的內容時,我們可以使用CSS來設定樣式。
可以直接將CSS寫在HTML檔案中,或在HTML中參考data目錄下的CSS檔案。

若未指定樣式,則panel的預設顯示樣式會依作業系統而異:

不同OS下panel預設CSS顏色

由於這些預設的樣式,設計自己的樣式時要注意,例如在OS X環境下,如果將background-color設為白色,卻沒有設定color的話,panel的文字會看不見(因為背景色和字體顏色皆為白)。

了解更多Panel類別的詳細建構子、方法、屬性、事件,請參考這裡

沒有留言:

張貼留言