From 218934fa2ed1c702a6d3923d2aa2cc6b43c48684 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:43:23 -0500 Subject: initial commit --- files/zh-tw/websockets/index.html | 155 ++++++++++++ .../websockets_reference/closeevent/index.html | 145 +++++++++++ .../websockets/websockets_reference/index.html | 25 ++ .../websockets_reference/messageevent/index.html | 80 ++++++ .../websockets_reference/websocket/index.html | 276 +++++++++++++++++++++ .../index.html | 179 +++++++++++++ 6 files changed, 860 insertions(+) create mode 100644 files/zh-tw/websockets/index.html create mode 100644 files/zh-tw/websockets/websockets_reference/closeevent/index.html create mode 100644 files/zh-tw/websockets/websockets_reference/index.html create mode 100644 files/zh-tw/websockets/websockets_reference/messageevent/index.html create mode 100644 files/zh-tw/websockets/websockets_reference/websocket/index.html create mode 100644 files/zh-tw/websockets/writing_websocket_client_applications/index.html (limited to 'files/zh-tw/websockets') diff --git a/files/zh-tw/websockets/index.html b/files/zh-tw/websockets/index.html new file mode 100644 index 0000000000..3cbb630f41 --- /dev/null +++ b/files/zh-tw/websockets/index.html @@ -0,0 +1,155 @@ +--- +title: WebSockets +slug: WebSockets +tags: + - WebSockets +translation_of: Web/API/WebSockets_API +--- +

{{ SeeCompatTable() }}

+

WebSocket 是一種讓瀏覽器與伺服器進行一段互動通訊的技術。這個 API 在不必輪詢(poll)伺服器下,讓使用者傳送訊息至伺服器並接受事件驅動回應。

+ + + + + + + +
+

文件

+
+
+ 製作 WebSocket 客戶端應用程式
+
+ 指導如何製作在瀏覽器上跑 WebSocket 客戶端的教程。
+
+ WebSockets 參考手冊
+
+ 客戶端的 WebSocket API 參考手冊。
+
+ The WebSocket protocol
+
+ WebSocket 協定參考。
+
+ Writing WebSocket servers
+
+ 處理 WebSocket 協定的伺服器端代碼書寫指引。
+
+

所有文件...

+
+

工具

+ +

 

+ +
+
+ AJAXJavaScript
+
+
+

參見

+ +

瀏覽器兼容

+

{{ CompatibilityTable() }}

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
功能ChromeFirefox (Gecko)Internet ExplorerOperaSafari
版本 -76 {{ obsolete_inline() }}6{{ CompatGeckoDesktop("2.0") }}{{ CompatNo() }}11.00 (禁用)5.0.1
協定版本 7{{ CompatNo() }} +

{{ CompatGeckoDesktop("6.0") }}

+
+ 請用 MozWebSocket
+
{{ CompatNo() }}{{ CompatNo() }}{{ CompatNo() }}
協定版本 1014 +

{{ CompatGeckoDesktop("7.0") }}

+
+ 請用 MozWebSocket
+
HTML5 Labs{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
功能AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
版本 -76 {{ obsolete_inline() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
協定版本 7{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
協定版本 8 (IETF 草案 10){{ CompatUnknown() }}{{ CompatGeckoMobile("7.0") }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+

Gecko 附註

+

Firefox 的 WebSockets 支援正在持續追蹤發展中的 WebSocket 規範。Firefox 6 實作底層協定版號 7,Firefox 7 實作協定版號 8(IETF 草案 10 的內容)。Firefox mobile 在 7.0 版支援 WebSocket。

+
+

{{ gecko_callout_heading("6.0") }}

+

Gecko 6.0 {{ geckoRelease("6.0") }} 之前,不該存在的 WebSocket 物件使得某些開發者認為 WebSocket 服務沒有前輟,此物件已被更名為 MozWebSocket

+
+
+

{{ gecko_callout_heading("7.0") }}

+

自從 Gecko 7.0 {{ geckoRelease("7.0") }},偏好設定 network.websocket.max-connections 可以用來設定 WebSocket 連線同時開啟的最大個數。預設值為 200。

+
+
+ 警告:雖然不是唯一的理由,但是目前 WebSockets 被 Firefox 4 與 5 禁用的關鍵原因是一個協定設計上的安全問題,因此不建議在生產環境下使用這些 Firefox 版本的 WebSockets。若仍想測試 WebSockets,你可以開啟 about:config 並設定 network.websocket.enabled 的取值至 true,並需要同時設定 network.websocket.override-security-block 的取值至 true 才能允許 WebSocket 連線的初始化。
+

{{ HTML5ArticleTOC() }}

+

{{ languages ( {"en": "en/WebSockets", "es": "es/WebSockets"} ) }}

diff --git a/files/zh-tw/websockets/websockets_reference/closeevent/index.html b/files/zh-tw/websockets/websockets_reference/closeevent/index.html new file mode 100644 index 0000000000..0a6d0977ff --- /dev/null +++ b/files/zh-tw/websockets/websockets_reference/closeevent/index.html @@ -0,0 +1,145 @@ +--- +title: CloseEvent +slug: WebSockets/WebSockets_reference/CloseEvent +tags: + - WebSockets +translation_of: Web/API/CloseEvent +--- +

{{ draft() }}

+

當 WebSocket 連線關閉時,客戶端會收到一個 CloseEvent,由 WebSocket 物件 onclose 屬性表示的監聽器接收。

+

屬性

+ + + + + + + + + + + + + + + + + + + + + + + +
屬性形態描述
codeunsigned longWebSocket 伺服器給予的連線關閉代碼。「狀態代碼」列有所有可能值。
reason{{ domxref("DOMString") }}表示伺服器關閉連線的原因,這因不同的伺服器與子協定而定。
wasCleanboolean表示連線關閉情況是否乾淨。
+

狀態代碼

+

以下列有所有合法的狀態代碼。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
狀態代碼描述
0-999尚未使用的保留值。
1000正常關閉,連線成功地達到建立時的目標。
1001端點去離,伺服器故障或是瀏覽器從開啟連線的頁面離去的情形。
1002因協定錯誤造成連線被端點消滅。
1003因端點接收不能處理的資料形態(舉例來說,文字端點收到二進制資料)而消滅連線。
1004端點收到過大的資料幀而消滅連線。
1005保留值。表示意外地未給予狀態代碼的情形。
1006保留值。用以表示在預期收到狀態代碼的情形下不正常(即未送關閉幀)的連線關閉。
1007-1999保留以作為未來的 WebSocket 標準之用。
2000-2999保留以作為 WebSocket 擴展之用。
3000-3999程式庫與框架使用的值,應用程式可不使用。
4000-4999應用程式使用的值。
+

參見

+ +

瀏覽器兼容

+

{{ CompatibilityTable() }}

+
+ + + + + + + + + + + + + + + + + + + +
功能ChromeFirefox (Gecko)Internet ExplorerOperaSafari
基本支援{{ CompatUnknown() }}{{ CompatNo() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+
+ + + + + + + + + + + + + + + + + + + +
功能AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
基本支援{{ CompatUnknown() }}{{ CompatNo() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+

Gecko 備註

+

此時此刻,Gecko 送至監聽器的 "close" 事件僅是簡單事件。

+

{{ languages ( {"en": "en/WebSockets/WebSockets_reference/CloseEvent"} ) }}

diff --git a/files/zh-tw/websockets/websockets_reference/index.html b/files/zh-tw/websockets/websockets_reference/index.html new file mode 100644 index 0000000000..19621bc3b2 --- /dev/null +++ b/files/zh-tw/websockets/websockets_reference/index.html @@ -0,0 +1,25 @@ +--- +title: WebSockets 參考 +slug: WebSockets/WebSockets_reference +tags: + - WebSockets +translation_of: Web/API/WebSockets_API +--- +

{{ draft() }}

+

下述文章是 WebSocket API 各介面的說明文件,本頁面為暫時的佔位文件。

+
+
+ WebSocket
+
+ 與 WebSocket 伺服器連接,傳送、接收資料的主介面。.
+
+ CloseEvent
+
+ 當連線關閉時 WebSocket 物件送出的事件。
+
+ MessageEvent
+
+ 當伺服器傳來資料時 WebSocket 物送送出的事件。
+
+

 

+

{{ languages ( {"en": "en/WebSockets/WebSockets_reference"} ) }}

diff --git a/files/zh-tw/websockets/websockets_reference/messageevent/index.html b/files/zh-tw/websockets/websockets_reference/messageevent/index.html new file mode 100644 index 0000000000..f5c0212f78 --- /dev/null +++ b/files/zh-tw/websockets/websockets_reference/messageevent/index.html @@ -0,0 +1,80 @@ +--- +title: MessageEvent +slug: WebSockets/WebSockets_reference/MessageEvent +tags: + - WebSockets +translation_of: Web/API/MessageEvent +--- +

{{ draft() }}

+

當伺服器傳來資料時,客戶端會收到一個 MessageEvent,由 WebSocket 物件 onmessage 表示的監聽器接收。

+

屬性

+ + + + + + + + + + + + + +
屬性形態描述
data{{ domxref("DOMString") }} | {{ domxref("Blob") }} | ArrayBuffer伺服器傳來的資料。
+

參見

+ +

瀏覽器兼容

+

{{ CompatibilityTable() }}

+
+ + + + + + + + + + + + + + + + + + + +
功能ChromeFirefox (Gecko)Internet ExplorerOperaSafari
基本支援{{ CompatUnknown() }}{{ CompatGeckoDesktop("2.0") }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
基本支援{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+

Gecko 備註

+
+

{{ gecko_callout_heading("6.0") }}

+

此時此刻,Gecko 不支援 ArrayBuffer 或 {{ domxref("Blob") }} 作為 data

+
+

{{ languages ( {"en": "en/WebSockets/WebSockets_reference/MessageEvent"} ) }}

diff --git a/files/zh-tw/websockets/websockets_reference/websocket/index.html b/files/zh-tw/websockets/websockets_reference/websocket/index.html new file mode 100644 index 0000000000..8acd8d03d5 --- /dev/null +++ b/files/zh-tw/websockets/websockets_reference/websocket/index.html @@ -0,0 +1,276 @@ +--- +title: WebSocket +slug: WebSockets/WebSockets_reference/WebSocket +tags: + - WebSockets +translation_of: Web/API/WebSocket +--- +

{{ SeeCompatTable() }}

+ +

{{ draft() }}

+ +

WebSocket 物件提供了建立、管理 WebSocket 伺服器連線的 API,它也有在連線中傳送、接收資料的能力。

+ +

方法一覽

+ + + + + + + + + + +
void close(in optional unsigned long code, in optional DOMString reason);
void send(in DOMString data);
+ +

屬性

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
屬性形態描述
binaryType{{ DOMXref("DOMString") }}表示連線傳輸的二進制資料形態的字串,若使用 {{ domxref("Blob") }} 物件則為 "blob",使用 ArrayBuffer 物件則為 "arraybuffer"。
bufferedAmountunsigned long呼叫 {{ manch("send") }} 隊列但尚未傳輸至網路上資料的位元數。連線關閉時此值不會重設為零。連續呼叫 {{ manch("send") }} 會讓此值不斷上升。唯讀
extensions{{ DOMXref("DOMString") }}伺服器選擇的擴展。目前僅有空字串或表示資料經過壓縮的 "deflate-stream"。唯讀
onclose{{ domxref("EventListener") }}當 WebSocket 連線的 readyState 切換至 CLOSED 時呼叫的事件監聽器。監聽器接收命名為 "close" 的 CloseEvent
onerror{{ domxref("EventListener") }}當錯誤發生時呼叫的事件監聽器。事件為命名 "error" 的簡單事件。
onmessage{{ domxref("EventListener") }}當瀏覽器接收伺服器的訊息時呼叫的事件監聽器。監聽器接收命名為 "message" 的 MessageEvent
onopen{{ domxref("EventListener") }}當 WebSocket 連線的 readyState 切換至 OPEN 時呼叫的事件監聽器,表示連線已準備傳送、接收資料。事件為命名 "open" 的簡單事件。
protocol{{ DOMXref("DOMString") }}伺服器選擇的子協定,這是建立 WebSocket 物件時 protocols 參數裡的其中一個字串。
readyStateunsigned short連線的目前狀態,是就緒狀態常數的其中一個。唯讀
url{{ DOMXref("DOMString") }}建構方法解析出來的 URL,總是絕對 URL。唯讀
+ +

常數

+ +

就緒狀態常數

+ +

readyState 屬性使用以下常數描述 WebSocket 的連線狀態。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
常數描述
CONNECTING0連線尚未打開。
OPEN1連線已打開,可以進行通訊。
CLOSING2連線正在進行關閉程序。
CLOSED3連線已關閉/連線不能打開。
+ +

方法

+ +

close()

+ +

關閉 WebSocket 連線/連線嘗試,若連線已為 CLOSED,此方法沒有作用。

+ +
void close(
+  in optional unsigned short code,
+  in optional DOMString reason
+);
+
+ +
參數
+ +
+
code {{ optional_inline() }}
+
表示狀態代碼,狀態代碼用以解釋連線關閉的原因。若未指定參數,預設值為 1000(表示正常的「事務完結(transaction complete)」關閉)。請參考 CloseEvent 頁面的狀態代碼列表,有所有的合法值。
+
reason {{ optional_inline() }}
+
解釋連線關閉原因的人類可讀字串,字串必不可大於 123 個 UTF-8 字符。
+
+ +
可丟例外
+ +
+
INVALID_ACCESS_ERR
+
指定不合法的 code
+
SYNTAX_ERR
+
reason 字串太長或是含有未配對的代理對。
+
+ +

send()

+ +

透過 WebSocket 連線傳輸資料至伺服器。

+ +
void send(
+  in DOMString data
+);
+
+void send(
+  in ArrayBuffer data
+);
+
+void send(
+  in Blob data
+);
+
+ +
參數
+ +
+
data
+
要傳送至伺服器的字串。
+
+ +
可丟例外
+ +
+
INVALID_STATE_ERR
+
目前連線不為 OPEN
+
SYNTAX_ERR
+
資料為帶有未配對代理對的字串。
+
+ +
註釋
+ +
+

{{ gecko_callout_heading("6.0") }}

+ +

Gecko send() 方法的實作與 Gecko 6.0 的規範有差別。Gecko 回傳一個 boolean 以表示連線是否仍處於開啟狀態(且資料成功隊列/傳輸)。另外,此時此刻,Gecko 不支援 ArrayBuffer 或 {{ domxref("Blob") }} 作為資料形態。

+
+ +

參見

+ + + +

瀏覽器兼容

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
功能ChromeFirefox (Gecko)Internet ExplorerOperaSafari
基本支援{{ CompatUnknown() }}{{ CompatGeckoDesktop("2.0") }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
子協定支援{{ CompatUnknown() }}{{ CompatGeckoDesktop("6.0") }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
功能AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
基本支援{{ CompatUnknown() }}{{ CompatGeckoMobile("7.0") }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
子協定支援{{ CompatUnknown() }}{{ CompatGeckoMobile("7.0") }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+ +

Gecko 備註

+ +

自從 Gecko 6.0,建構方法有前輟,須使用 MozWebSocket()

+ +
var mySocket = new MozWebSocket("http://www.example.com/socketserver");
+
+ +

{{ languages ( {"en": "en/WebSockets/WebSockets_reference/WebSocket"} ) }}

diff --git a/files/zh-tw/websockets/writing_websocket_client_applications/index.html b/files/zh-tw/websockets/writing_websocket_client_applications/index.html new file mode 100644 index 0000000000..8f1299379f --- /dev/null +++ b/files/zh-tw/websockets/writing_websocket_client_applications/index.html @@ -0,0 +1,179 @@ +--- +title: 製作 WebSocket 客戶端應用程式 +slug: WebSockets/Writing_WebSocket_client_applications +tags: + - WebSockets +translation_of: Web/API/WebSockets_API/Writing_WebSocket_client_applications +--- +

{{ draft() }}

+ +

WebSocket 是一種讓瀏覽器與伺服器進行一段互動通訊的技術。使用這項技術的 Webapp 可以直接進行即時通訊而不需要不斷對資料更改進行輪詢(polling)。

+ +
注:當我們的系統架構可以寄存 WebSocket 範例之後,我們會提供聊天/伺服器系統實例的幾個範例。
+ +

哪裡有 WebSocket

+ +

若 JavaScript 代碼的範疇是 {{ domxref("Window") }} 物件或是實作 {{ domxref("WorkerUtils") }} 的物件,則可使用 WebSocket API。也就是可以從 Web Workers 使用 WebSocket。

+ +
注:WebSockets API(與底層協定)的開發還在進展中,且目前不同瀏覽器(甚至瀏覽器的不同版本)有很多兼容問題。
+ +

建立一個 WebSocket 物件

+ +

你必須建立一個 WebSocket 物件才能讓瀏覽器/伺服器得以以 WebSocket 協定進行通訊,此物件在被建立之後會自動與伺服器連線。

+ +
注:別忘記在 Firefox 6.0 中 WebSocket 物件仍有前輟,所以在這裡須改成 MozWebSocket
+ +

WebSocket 的建構方法有一個必要參數與一個選擇參數:

+ +
WebSocket WebSocket(
+  in DOMString url,
+  in optional DOMString protocols
+);
+
+WebSocket WebSocket(
+  in DOMString url,
+  in optional DOMString[] protocols
+);
+
+ +
+
url
+
連線用的 URL,WebSocket 伺服器會回應這個 URL。
+
+ 根據網際網路工程任務小組(Internet Engineering Task Force,IETF)定義之規範, URL 的協議類型必須是 ws:// (非加密連線)或是 wss:// (加密連線)
+
protocols {{ optional_inline() }}
+
一個表示協定的字串或者是一個表示協定的字串構成的陣列。這些字串可以用來指定子協定,因此一個伺服器可以實作多個 WebSocket 子協定(舉例來說,你可以讓一個伺服器處理不同種類的互動情形,各情形以 protocol 分別)。若不指定協定字串則預設值為空字串。
+
+ +

此建構方法可能拋出以下例外:

+ +
+
SECURITY_ERR
+
連線使用的埠被阻擋。
+
+ +

範例

+ +

此簡單範例建立了一個新的 WebSocket,連到位於 http://www.example.com/socketserver 的伺服器。指定的子協定是 "my-custom-protocol"。

+ +
var mySocket = new WebSocket("ws://www.example.com/socketserver", "my-custom-protocol");
+
+ +

回傳之後,mySocketreadyState 會變成 CONNECTING。當連線已可以傳輸資料時 readyState 會變成 OPEN

+ +

要建立一個連線但不指定單一個特定協定,可以指定一個協定構成的陣列:

+ +
var mySocket = new WebSocket("ws://www.example.com/socketserver", ["protocol1", "protocol2"]);
+
+ +

當連線建立的時候(也就是 readyState 變成而 OPEN 的時候),protocol 屬性會回傳伺服器選擇的協定。

+ +

傳資料給伺服器

+ +

連線開啟之後即可開始傳資料給伺服器。呼叫 WebSocketsend() 來發送訊息:

+ +
mySocket.send("這是伺服器正迫切需要的文字!");
+
+ +

可以被傳送的內容包括字串、Blob 或是 ArrayBuffer

+ +
注:Firefox 目前只支援字串傳送。
+ +

用 JSON 傳輸物件

+ +

有一個很方便的方法是用 JSON 傳送複雜的資料給伺服器,舉例來說,聊天程式可以設計一種協定,這個協定傳送以 JSON 封裝的資料封包:

+ +
// 透過伺服器傳送文字給所有使用者
+
+function sendText() {
+  var msg = {
+    type: "message",
+    text: document.getElementById("text").value,
+    id: clientID,
+    date: Date.now()
+  };
+
+  mySocket.send(JSON.stringify(msg));
+  document.getElementById("text").value = "";
+}
+
+ +

這份代碼先建立一個物件:msg,它包含伺服器處理訊息所需的種種資訊,然後呼叫 JSON.stringify() 使該物件轉換成 JSON 格式並呼叫 WebSocket 的 send() 方法來傳輸資料至伺服器。

+ +

從伺服器接收訊息

+ +

WebSockets 是一個事件驅動 API,當瀏覽器收到訊息時,一個「message」事件被傳入 onmessage 函數。使用以下方法開始傾聽傳入資料:

+ +
mySocket.onmessage = function(e) {
+  console.log(e.data);
+}
+
+ +

接收並解讀 JSON 物件

+ +

考慮先前在「用 JSON 傳輸物件」談起的聊天應用程式。客戶端可能收到的資料封包有幾種:

+ + + +

用來解讀傳入訊息的代碼可能像是:

+ +
connection.onmessage = function(evt) {
+  var f = document.getElementById("chatbox").contentDocument;
+  var text = "";
+  var msg = JSON.parse(evt.data);
+  var time = new Date(msg.date);
+  var timeStr = time.toLocaleTimeString();
+
+  switch(msg.type) {
+    case "id":
+      clientID = msg.id;
+      setUsername();
+      break;
+    case "username":
+      text = "<b>使用者 <em>" + msg.name + "</em> 登入於 " + timeStr + "</b><br>";
+      break;
+    case "message":
+      text = "(" + timeStr + ") <b>" + msg.name + "</b>: " + msg.text + "<br>";
+      break;
+    case "rejectusername":
+      text = "<b>由於你選取的名字已被人使用,你的使用者名稱已被設置為 <em>" + msg.name + "</em>。";
+      break;
+    case "userlist":
+      var ul = "";
+      for (i=0; i < msg.users.length; i++) {
+        ul += msg.users[i] + "<br>";
+      }
+      document.getElementById("userlistbox").innerHTML = ul;
+      break;
+  }
+
+  if (text.length) {
+    f.write(text);
+    document.getElementById("chatbox").contentWindow.scrollByPages(1);
+  }
+};
+
+ +

這裡我們使用 JSON.parse() 使 JSON 物件轉換成原來的物件,檢驗並根據內容採取行動。

+ +

關閉連線

+ +

當你想結束 WebSocket 連線的時候,呼叫 WebSocket 的 close() 方法:

+ +
mySocket.close();
+
+ +

參考資料

+ +

IETF: The WebSocket protocol draft-abarth-thewebsocketprotocol-01

+ +

 

+ +

{{ languages ( {"en": "en/WebSockets/Writing_WebSocket_client_applications"} ) }}

+ +
+
-- cgit v1.2.3-54-g00ecf