鳳

開發經驗談 (六之六)

筆者在 2009 年 6 月時加入 SocialCalc 專案團隊。經過四個月的密集作業,我們在 10 月 19 日(VisiCalc 的 30 周年紀念日)發表了 SocialCalc 1.0 版。

從 WikiCalc 到 SocialCalc,中間經歷了三年的開發。相形之下,最後這四個月只是一段短暫的時間。儘管如此,這段和 Socialtext 同事在 Dan Bricklin 的指導下進行協作的過程,仍使我獲益匪淺。在此,筆者想將那段時間學到的經驗分享給大家。

願景清晰的首席設計師非常重要

在《設計的設計(The Design of Design)》一書中,Fred Brooks 認為,在建構複雜系統時,若能專注於清晰連貫的設計概念,而非各自為政的想法,那麼溝通成本將會大幅下降。

依 Brooks 的想法,這種連貫式設計概念的構想,最好能由一個人來主導:

    概念的完整性是偉大設計中最重要的特性。由於完整的概念只能出自一人或
    少數人的合作構想,因此明智的管理者會大膽委託才華出眾的首席設計師,
    來承擔整個設計任務。

對於 SocialCalc 這個專案來說,讓 Tracy Ruggles 擔任首席用戶體驗設計師,是讓我們成功實現原初構想的關鍵。由於 SocialCalc 的底層引擎十分易於擴展,因此各種新功能的想法往往層出不窮;Tracy 透過草圖進行原型設計的才能,為我們提供了巨大的幫助,讓我們能用最直觀的方式呈現出所有的功能。

共筆確保專案延續

在我加入專案之前,SocialCalc 已有超過兩年的設計和開發歷史,但我卻能在幾天之內立刻趕上進度,開始作出貢獻。

這是因為「所有資訊都在共筆裡」– 從早期的設計草稿到最新的瀏覽器支援清單,整個流程都詳細記載在共筆頁面和 SocialCalc 試算表裡。

通過閱讀該專案的相關記錄,讓我能跳過新成員加入時常見的上手期,快速跟上目前的進度。

這在傳統的開源專案比較少見。傳統專案中大部分的交流都是在 IRC 和郵件列表中進行的,而共筆(如果有使用的話)往往只用來存放相關資源的記錄和鏈結 – 如果有新人加入,要想通過結構混亂的 IRC 紀錄和郵件存檔追趕進度,無疑會更加困難。

善加運用時區差異

Ruby on Rails 的發明人 David Heinemeier Hansson 在加入 37signals 時,曾這樣評價分散式團隊的優勢:

    哥本哈根和芝加哥之間相隔的七個時區,實際上減少了我們受到的打擾,
    讓我們做出更多工作。

在 SocialCalc 的研發過程中,對於台北和帕羅奧圖之間相隔的九個時區,我們亦有同感。

通常我們會在一天 24 小時內,完成一次「設計-開發-品管」反饋循環,每個環節用到某個人的八小時。這種非同步式的協作,促使我們運用能自我描述的完整作品(設計草圖、代碼,以及測試)來溝通,從而大幅增進相互之間的信任。

樂趣最優

筆者在 2006 年於 CONISLI 會議上所作的主題演講「-OFun:樂趣最優(Optimizing for Fun)」裡,將自己領導分散式團隊開發 Perl 6 語言的經驗,總結為幾個重點。其中「隨時保持藍圖清晰」、「寬恕 > 許可」、「打破僵局」、「不求共識,只求創意」,以及「使用代碼描述概念」這幾項,特別適合小型的分散式團隊。

因此,在開發 SocialCalc 時,我們特別重視團隊成員間的知識分享,以確保每個人都不會成為主要瓶頸。

另外,當設計過程出現多種備選方案時,我們會主動將每個方案都進行實做,以深入探勘它們的設計空間,並先一步解決可能出現的衝突。如果在此過程裡發現有更好的設計,我們也不怕將整個原型打掉重寫。

雖然缺乏面對面交流,但這些文化特質幫我們培養相互之間的信任和情誼,並將爭議降到最低,讓 SocialCalc 的開發成為一件樂事。

通過故事測試推動開發工作

在加入 Socialtext 前,我曾倡導「將測試寫進規格裡」的方法,這一點在 Perl 6 語言規格 中就能看到:我們逐段幫規格加上測試,並持續確保兩者之間的同步。

然而 SocialCalc 專案品管團隊的成員 Ken Pier 和 Matt Heusser 卻讓我大開眼界 – 原來這種做法還可以更進一步,將測試提昇到「可以執行的規格」這個境界。

在《美妙的測試(Beautiful Testing)》一書第 16 章,「剝開 Socialtext 的玻璃洋蔥」裡,Matt 用如下方式解釋了我們由「故事測試」推動的開發流程:

    工作的最基本單元是一系列「故事」,也就是一系列非常輕量級的需求文件。

    每個故事只包含對一個功能的簡要描述,以及此功能的運作實例,用最直白的
    文句進行描述。我們稱這些實例為「接納度測試」。

    在故事形成初期,設計師會先寫出初步的接納度測試,隨後由開發人員和測試
    人員進行討論,之後開發人員才開始編寫代碼。

隨後這些故事測試會被轉換為「共筆測試(wikitests)」。這是一種基於表格的語言,脫胎自 Ward Cunningham 的 FIT 框架。用此語言寫成的測試存進共筆系統後,會自動連接到 Test​::WWW​::Mechanize 和 Test​::WWW​::Selenium 等測試框架,成為自動測試流程的一環。

用故事測試作為「表達需求」和「驗證需求」的共通語言,這種做法的好處一言難盡。它不但大幅節省了溝通成本,也讓我們能每個月對系統進行一次改版時,不用擔心已經修復的瑕疵會再次出現。

CPAL 開源授權

最後,我們針對 SocialCalc 而設計的開源授權,也令我們受益匪淺。

Socialtext 為了 SocialCalc,設計出通用公共授權(Common Public Attribution License)。CPAL 以 Mozilla 公共授權為基礎,並讓原作者可以要求在軟體的界面上顯示自己的標誌。CPAL 還加上了「網絡使用條款」,讓衍生作品在透過網路提供服務時,也授予一般使用者取得源碼的權利。

在獲得開放源碼促進會和自由軟體基金會的認可後,已經有不少知名網站(例如 Facebook 和 Reddit)選擇用 CPAL 方式發布自己的平台源碼,這對我們無疑是莫大的鼓勵。

因為 CPAL 屬於「較弱型著佐權(weak copyleft)」授權,因此開發人員可以將它整合進任何自由軟體或私有軟體裡,只需要分享出針對 SocialCalc 本身的修改。這讓各個社群都可以採用 SocialCalc ,並對其進行改進。

底下是一些以 SocialCalc 為基礎的開源專案:

  • Drupal 的 Sheetnode 專案,及其自行維護的 SocialCalc 分支。
  • Luke Closs 發起的 OLPC/XOCOM 平台移植版。
  • SEETA 的 OLPC/Sugar 平台移植版,由 Luke 的版本衍生而來。
  • SEETA 的 Palm Pre 平台移植版。
  • Ramu Ramamurthy 的 Scala/Java 試算表伺服器專案。

本章中的範例,包括豐富文本和協作編輯等功能,都能在 http://github.com/audreyt/socialcalc 下載。WikiCalc 1.0 代碼的歷史存檔,則可由http://github.com/audreyt/wikicalc 取得。

開源的網頁試算表引擎,有許多有趣的應用可能。如果您能將 SocialCalc 嵌入到您自己的專案中,這絕對是我們喜聞樂見的。

Happy Hacking!

唐鳳

 

March 05, 2011 at 10:43 AM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

即時多人協作 (六之五)

我們接下來研究的一個例子,是在共享的試算表裡進行多人即時協同編輯。

這乍看之下也許有點複雜,但是感謝 SocialCalc 的模組化設計,我們只需讓每位使用者將自己的指令傳播給其他參與者執行即可。

為了對本地指令與遠端指令作出區分,我們為 ScheduleSheetCommands 方法增加了 isRemote 參數:

    SocialCalc.ScheduleSheetCommands =
        function(sheet, cmdstr, saveundo, isRemote) {
            if (SocialCalc.Callbacks.broadcast && !isRemote) {
                SocialCalc.Callbacks.broadcast('execute', {
                    cmdstr: cmdstr,
                    saveundo: saveundo
                });
            }
            // ...original ScheduleSheetCommands code here...
        };

現在只需定義一個合適的 SocialCalc.Callbacks.broadcast 回喚函數,即可讓所有連入此試算表的客戶端執行相同的指令。

當 SEETA Sugar Labs 於 2009 年在 OLPC 上首次實作這項功能時,broadcast 函式是用 XPCOM 框架寫成,並在 OLPC/Sugar 的標準傳輸層 D-Bus/Telepathy 網路上運行:

這樣的運行方式,讓 Sugar 網路裡的 XO 電腦能對共同的 SocialCalc 試算表進行協作,但也只能在 Mozilla/XPCOM 瀏覽器平台與 D-Bus/Telepathy 訊息平台上適用。

跨瀏覽器傳輸

為了達成跨瀏覽器、跨作業系統的目標,我們使用 Web::Hippie 框架,作為 JSON 在 WebSocket 上傳輸的抽象層。它提供方便的 jQuery 綁定,並且當 WebSocket 不適用時,也能利用 MXHR (multipart XMLHttpRequest) 作為備用傳輸機制.

對於安裝了 Adobe Flash 插件但沒有原生 WebSocket 支持的瀏覽器,我們使用 web_socket.js 專案的 Flash WebSocket 模擬器,這通常比 MXHR 更快也更可靠。

運作流程看上去是這樣的:

客戶端的 SocialCalc.Callbacks.broadcast 函式定義如下:

    var hpipe = new Hippie.Pipe();

    SocialCalc.Callbacks.broadcast = function(type, data) {
        hpipe.send({ type: type, data: data });
    };

    $(hpipe).bind("message.execute", function (e, d) {
        var ss = SocialCalc.CurrentSpreadsheetControlObject;
        ss.context.sheetobj.ScheduleSheetCommands(
            d.data.cmdstr,
            d.data.saveundo,
            true // isRemote = true
        );
        break;
    });

儘管這已經能順利運行,我們仍然有兩個問題需要解決。

衝突解決

第一個就是為了執行指令而產生的爭用狀態:如果使用者 A 與 B 同時執行某個影響相同儲存格的操作,之後才接收到與對方傳播出來的指令,那麼雙方將會停在不同的狀態:

我們可以通過 SocialCalc 內置的還原/重作機制來解決這個問題,如下圖所示:

  • 當客戶傳播出一個指令時,會將指令添加到待辦佇列。
  • 當客戶接收到一個指令時,檢查待辦佇列:
    • 如果待辦佇列為空,則直接執行這項遠端指令。
    • 如果它符合待辦佇列裡的本地指令,則將它將從佇列中移除。
    • 否則,檢查佇列中是否有指令與接收到的指令相衝突:
      • 如果存在衝突指令,則先還原這些指令,並將其標記為稍後重作。
      • 在還原所有的衝突指令之後,將遠端指令按正常狀態執行。
  • 當從伺服器上接收到標記為重做的指令時,客戶端將再次執行指令,再從佇列中將其移除。

遠端游標

盡管爭用狀態已經得到解決,但「偶爾會覆蓋掉其他使用者正在編輯的儲存格」這樣的不理想情形還是存在。我們可以更進一步,將每位使用者的游標位置傳播給其他使用者,讓每個人都能看到有哪些儲存格正在編輯。

為了實做這一想法,我們為 MoveECellCallback 事件添加了另一個 broadcast 處理程序:

    editor.MoveECellCallback.broadcast = function(e) {
        hpipe.send({
            type: 'ecell',
            data: e.ecell.coord
        });
    };

    $(hpipe).bind("message.ecell", function (e, d) {
        var cr = SocialCalc.coordToCr(d.data);
        var cell = SocialCalc.GetEditorCellElement(
            editor, cr.row, cr.col
        );
        // ...decorate cell with styles specific
        //    to the remote user(s) on it...
    });

在試算表中標記儲存格焦點時,通常會使用帶有顏色的邊框。但是,該儲存格也許已經有自己的 border 屬性了。而由於 border 為單一顏色,因此在相同的儲存格只能表現一個游標。

因此,在支援 CSS3 的瀏覽器上,我們使用 box-shadow 功能來表現多個游標:

    /* Two cursors on the same cell */
    box-shadow: inset 0 0 0 4px red, inset 0 0 0 2px green;

如此一來,當四人編輯同一個試算表時,螢幕看起來會像這樣:

(未完,待續。)

March 04, 2011 at 01:59 PM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

豐富文本編輯 (六之四)

我們對 SocialCalc 所做的第一項改進,是讓文字儲存格能夠使用共筆語法,以及直接在表格編輯器中實現的豐富文本繪製:

在 1.0 版發布之後不久,為了回應用戶希望通過統一語法插入圖片、鏈接,以及文字標記的需求,我們幫 SocialCalc 加上了這項功能。由於 Socialtext 提供自己的開源共筆平台,因此自然會希望能在試算表中直接使用相同的語法。

為了實現此功能,我們需要將預設的文字儲存格格式(即 textvalueformat 屬性)設為 text-wiki,並為它提供自定的繪製器。

至於 textvalueformat 屬性是什麼呢?請見下文。

類型與格式

在 SocialCalc 中,每個儲存格都有一個 datatype 及一個 valuetype 屬性。包含文字/數字資料的儲存格分別對應到文字/數字值類型,而具備 datatype="f" 的公式儲存格則可能會生成數字或文字值。

在前一節介紹的繪製步驟中,Sheet 物件會為每個儲存格生成 HTML。為此,它會檢查每個儲存格的 valuetype:如果以 t 開頭,則該儲存格的textvalueformat 屬性會決定如何進行生成。如果以 n 開頭,則使用 nontextvalueformat 屬性進行判斷。

如果該儲存格的 textvalueformat 或 nontextvalueformat 屬性沒有定義,則會通過其 valuetype 屬性查詢預設格式,如下圖所示:

對 text-wiki 值格式的支援,寫在 SocialCalc.​format_text_for_display 中:

     if (SocialCalc.Callbacks.expand_wiki
         && /^text-wiki/.test(valueformat)
     ) { 
         // do general wiki markup 
         displayvalue = SocialCalc.Callbacks.expand_wiki(
             displayvalue, sheetobj, linkstyle, valueformat
         ); 
     } 

此處我們並非將「共筆文字轉 HTML」的轉換器嵌入到 format_text_for_display 裡,而是在 SocialCalc.​Callbacks 裡定義一個新的掛鉤。這是 SocialCalc 代碼裡推薦的方法:這種模組化的設計,讓應用程式可以支援各種不同共筆文字的語法。如果應用程式用不到 text-wiki 格式,也可以直接忽略 expand_wiki 掛鉤。

繪製共筆文字

隨後,我們使用了 Wikiwyg,這是一個 JavaScript 程式庫,可在共筆文字和 HTML 之間提供雙向轉換。

我們定義下列 expand_wiki 函式,將儲存格的值透過 Wikiwyg 的 Wikitext 解析器和 HTML 產生器轉換成 HTML:

     var parser = new Document.Parser.Wikitext(); 
     var emitter = new Document.Emitter.HTML(); 
     SocialCalc.Callbacks.expand_wiki = function(val) { 
         // Convert val from Wikitext to HTML 
         return parser.parse(val, emitter); 
     } 

最後一個步驟則需要在試算表初始化完畢後,將 set sheet defaulttextvalueformat text-wiki 加入命令佇列:

     // Assume there's a <div id="tableeditor"/> in DOM 
     var spreadsheet = new SocialCalc.SpreadsheetControl(); 
     spreadsheet.InitializeSpreadsheetControl(
         "tableeditor", 0, 0, 0
     ); 
     spreadsheet.ExecuteCommand(
         'set sheet defaulttextvalueformat text-wiki'
     ); 

將上述部份搭配起來後,繪製步驟的工作流程將類似這樣:

大功告成!改進後的 SocialCalc 能夠支持豐富的共筆標記語法:

    *bold* _italic_ `monospace` {{unformatted}}
    > indented text
    * unordered list
    # ordered list
    "Hyperlink with label"<http://softwaregarden.com/>
    {image: http://www.socialtext.com/images/logo.png}

請嘗試在 A1 中輸入 *bold* _italic_ `monospace`,隨後即可看到繪製後的豐富文本內容:

(未完,待續。)

March 03, 2011 at 01:54 PM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

SocialCalc (六之三)

這是 SocialCalc 在運行過程中的畫面:

底下則是它的類型圖:

相較於 WikiCalc,伺服器的角色已大幅減輕。現在伺服器只需要負責回應 HTTP GET 請求,提供整份表格內容的序列化字串即可。瀏覽器在收到資料後,所有計算、變動追蹤,以及使用者的互動都是通過 JavaScript 達成的。

JavaScript 元件在設計上使用了層次式 MVC(模型/視圖/控制器)樣式,每個類型都只專注於某部分的功能:

  • Sheet 是資料模型,代表試算表在記憶體中的結構。 

    模型中包含從座標指向 Cell 物件的字典,每個物件代表一個儲存格。空儲存格所在的座標不需要有對映的物件,因此完全不占用記憶體。

  • Cell 代表儲存格的內容和格式。 

    下面列出的是一些常見的 Cell 物件屬性:

        datatype    t
        datavalue   1Q84
        color       black
        bgcolor     white
        font        italic bold 12pt Ubuntu
        comment     Ichi-Kyu-Hachi-Yon
  • RenderContext 用於實現視圖,需要負責將表格繪製為相應的 DOM 物件。

  • TableControl 則是主控制器,負責接收滑鼠和鍵盤事件。 

    在接收到視圖事件,例如滾動和調整大小後,就會對相關 RenderContext 物件進行更新。

    如果收到應用於試算表內容的更新事件,則會在試算表的指令佇列中加入新的指令。

  • SpreadSheetControl 負責繪制頂層界面,包括工具欄、狀態欄、對話框,以及顏色選擇器。

  • SpreadSheetViewer 是另一套頂層界面,主要提供唯讀的互動視圖。

我們採用了基於類型的輕量級物件系統,僅使用了簡單的組成/委派機制,完全沒有使用繼承或物件原型。所有符號都位於 SocialCalc.* 名稱空間裡,以避免命名衝突。

對試算表的全部更新都需要通過 ScheduleSheetCommands 方法進行,因此需要通過指令字串來代表編輯操作。常用的指令如下:

    set     sheet defaultcolor blue
    set     A width 100
    set     A1 value n 42
    set     A2 text t Hello
    set     A3 formula A1*2
    set     A4 empty
    set     A5 bgcolor green
    merge   A1:B2
    unmerge A1
    erase   A2
    cut     A3
    paste   A4
    copy    A5
    sort    A1:B9 A up B down
    name    define Foo A1:A5
    name    desc   Foo Used in formulas like SUM(Foo)
    name    delete Foo
    startcmdextension UserDefined args

嵌入 SocialCalc 的應用程序可以自行定義額外的指令,只需要將命名的回調函數添加到 SocialCalc.​SheetCommandInfo.​CmdExtensionCallbacks 物件,即可使用 startcmdextension 指令進行呼叫。

指令的循環運行

為改善回應速度,SocialCalc 會在背景執行全部的重算和 DOM 更新,因此在使用者對多個儲存格進行修改時,試算表引擎會同時在指令佇列裡處理先前的改動。

在運行指令時,TableEditor 物件會將其 busy 屬性設為 true;後續指令則需加入到 deferredCommands 佇列,以確保指令能循序執行。事件循環看起來像這樣:

如上圖所示,Sheet 物件會持續發送 StatusCallback 事件,以提醒使用者當前的指令執行狀態,這一過程都可以分為下列四個步驟:

  • 執行指令 

    啟動時發送 cmdstart,執行完成後則發送 cmdend。

    如果指令間接更改了某儲存格的值,則進入重算步驟。

    否則,如果指令更改了一個或多個已在螢幕上顯示的儲存格的視覺外觀,則進入繪製步驟。

    如果上述情況都不符合(例如在使用copy指令時),則跳到位置計算步驟。

  • 重算(如果需要的話) 

    啟動時發送 calcstart,在檢查儲存格的依存鏈時每隔 100ms 發送 calcorder,完成檢查時則發送 calccheckdone,並在所有受影響儲存格獲得重算後的值後發送 calcfinished。

    這一步驟之後總是需要執行繪製步驟。

  • 繪製(如果需要的話) 

    啟動時發送 schedrender,如果使用格式化後的儲存格更新了 <table> DOM 物件,則發送 renderdone。

    這一步驟之後總是需要執行位置計算步驟。

  • 位置計算 

    啟動時發送 schedposcalc,並在更新了滾動條、目前儲存格游標,以及 TableEditor 的其他視覺組件後發送 doneposcalc。

因為所有指令在執行後即被保存,因此等於對所有操作都可獲得執行紀錄。為實現稽核追蹤,Sheet.CreateAuditString 方法會傳回以換行隔開的字符串,每行內容對應到一個指令的相關記錄。

ExecuteSheetCommand 還可為執行的每個指令創建還原指令。舉例來說,如果儲存格 A1 包含 Foo,而使用者執行了 set A1 text Bar,則還原指令set A1 text Foo 會被推送到 UndoStack 裡。如果使用者進行還原操作,則會通過執行還原指令來讓 A1 的內容回到原先的值。

試算表編輯器

接著一起來看看 TableEditor 層。該層可計算 RenderContext 的螢幕顯示內容,並通過兩個 TableControl 實例來管理水平/垂直捲動軸。

視圖層主要由 RenderContext 類型負責。與 WikiCalc 的設計不同的是,我們並非將每個儲存格對映到一個 <td> 元素,而是直接創建固定大小的 <table>,使它充分填滿瀏覽器的可視區域,並為其預先填充 <td> 元素。

當使用者通過自定義的滾動條拖動試算表後,可以動態更新預先繪制的 <td> 元素的 innerHTML。這意味著在很多常見情況下,我們並不需要創建/刪除任何 <tr> 或 <td> 元素,因此大幅提升了回應速度。

因為 RenderContext 只繪製可視區域,所以無論試算表多大,執行效能也不受影響。

TableEditor 還包含一個 CellHandles 物件,可用於處理附加到目前儲存格(即ECell)右下角的圓盤形填充/移動/滑動選單指令:

輸入框則由兩個類型負責管理:InputBox 和 InputEcho。前者主要管理網格上的編輯行,後者主要用於在輸入內容時提供及時更新的預覽層,並覆蓋ECell的內容。

通常,SocialCalc 引擎只有在打開試算表並進行編輯時,以及將內容保存回伺服器時才需要與伺服器通訊。因此,Sheet.ParseSheetSave 方法可在Sheet 物件中解析儲存格式字串,而 Sheet.CreateSheetSave 方法可將 Sheet 物件序列化為儲存格式。

通過使用 URL,公式可引用任何遠端試算表中的值。recalc 指令會重新抓取被引用的外部電子試算表,並使用 Sheet.ParseSheetSave 對其進行解析,然後將其存儲在暫存區中,這樣使用者即可在不重新抓取內容的情況下,直接引用相同遠端表格中其他儲存格的內容。

儲存格式

儲存格式是一種標準的 MIME multipart/mixed 格式,主要由四個 text/plain; charset=UTF-8 部件組成,每部件包含以換行隔開的文字,並用冒號劃分資料欄位。這些部件包括:

  • meta 部件列出其他部件的型別。
  • sheet 部件列出每個儲存格的格式和功能、每個列的寬度(如果不是預設寬度)、表格的預設格式,以及該試算表中用到的字體、顏色,及邊框列表。
  • 可選的 edit 部件可保存 TableEditor 的編輯狀態,包括 ECell 的最後一個位置,以及行/列窗格的固定大小。
  • 可選的 audit 部件包含上一次編輯會話中執行過的指令歷史記錄。

舉例來說,下面是一個包含三個儲存格的試算表,A1 作為 ECell,其內容為 1874,A2 中是公式 2^2*43,A3 中的公式 SUM(Foo) 則顯示為粗體字,代表命名範圍從 Foo 到 A1:A2:

這份試算表經過序列化後,儲存格式會像這樣:

    socialcalc:version:1.0
    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary=SocialCalcSpreadsheetControlSave
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    # SocialCalc Spreadsheet Control Save
    version:1.0
    part:sheet
    part:edit
    part:audit
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    version:1.5
    cell:A1:v:1874
    cell:A2:vtf:n:172:2^2*43
    cell:A3:vtf:n:2046:SUM(Foo):f:1
    sheet:c:1:r:3
    font:1:normal bold * *
    name:FOO::A1\cA2
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    version:1.0
    rowpane:0:1:14
    colpane:0:1:16
    ecell:A1
    --SocialCalcSpreadsheetControlSave
    Content-type: text/plain; charset=UTF-8

    set A1 value n 1874
    set A2 formula 2^2*43
    name define Foo A1:A2
    set A3 formula SUM(Foo)
    --SocialCalcSpreadsheetControlSave--

上述格式在設計上可讓人直接讀取,並且也很容易通過程式生成。因此,Drupal 項目的 Sheetnode 插件即可使用 PHP 在此格式以及其他流行的試算表格式,例如 Excel (.xls) 及 OpenDocument (.ods) 之間進行轉換。

至此,我們已經簡要介紹了 SocialCalc 各個元件和組成方式。接下來,讓我們透過兩個實際的範例,一起來看看如何擴展 SocialCalc 的功能。

(未完,待續。)

March 02, 2011 at 05:02 AM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

WikiCalc (六之二)

WikiCalc 一問世就帶來許多獨特的功能,是同時期的試算表所沒有的:

  • 純文字、HTML,以及共筆式的文本標記支援。
  • 共筆文字包含插入鏈接、圖片,以及和從儲存格引用值的功能。
  • 公式儲存格可以引用放在其他網站的 WikiCalc 網頁裡的值。
  • 支援輸出到靜態網頁,以及將動態資料內嵌至其他網頁。
  • 儲存格能使用 CSS 來改變樣式。
  • 記錄所有編輯操作,以供稽核紀錄。
  • 和共筆系統一樣,保留每一個版本,並可以隨時回復。

從幾個小型試算表組建一個主試算表的能力,是 WikiCalc 的一大強項。舉例來說,每位銷售員可以把營業額放在自己的試算表頁面裡;然後銷售經理可以綜合這些資料到該區的試算表中,之後銷售副總再綜合各區域的數字,構成主試算表。

每次有試算表更新時,所有綜合它的試算表都會即時反映出這次更新。如果主試算表的讀者想要瞭解更多細節,只需要點擊鏈結,即可查看試算表後面的試算表。這項功能讓使用者不再需要在多個地方更新數字,從而減少了多餘而容易出錯的操作,並確保所有資訊的視圖總在最新狀態。

為了即時重新計算的需求,WikiCalc 採用了輕客戶端的設計,將所有要顯示的資訊都放在伺服器端。每個試算表在瀏覽器上以 <table> 元素呈現;編輯儲存格時,瀏覽器會發送一個 ajaxsetcell 指令到伺服器,然後伺服器告訴瀏覽器哪個儲存格需要更新。

當然,這項設計依賴於瀏覽器與伺服器之間的快速連接。當網路出現延遲的時候,使用者在更新儲存格和看到它的新內容之間,會看到 Loading... 訊息頻繁出現;這問題對於需要即時調整、預覽公式結果的使用者特別嚴重。

此外,因為 <table> 元素與試算表有著相同大小,一個 100x100 試算表會在 DOM 裡創建上萬個 <td> 元素,大量消耗瀏覽器的記憶資源,進一步限制頁面的大小。

由於這些缺點,雖然 WikiCalc 作為在本地主機運行的獨立伺服器時尚稱實用,但要當作網頁內容管理系統的一部分,卻超出了它的能力。

在 2006 年,Dan Bricklin 與 Socialtext 團隊開始開發 SocialCalc 專案,用 JavaScript 語言對 WikiCalc 原本的 Perl 源碼作出改寫,目標是能支援大型試算表、分散式協作流程,以及與桌面應用程式一樣的操作界面。

這裡是 SocialCalc 的一些設計目標:

  • 處理十萬個儲存格的能力。
  • 進行編輯操作時提供快速回應。
  • 客戶端的稽核紀錄和還原/重作支援。
  • 善用 Javascript 和 CSS ,提供完整的視覺呈現功能。
  • 用 Javascript 提昇效能,並加強對各種不同瀏覽器的支援。

經過三年的開發和發佈許多次測試版之後,Socialtext 在 2009 年釋出 SocialCalc 1.0,成功實現了設計目標。現在,讓我們來看看 SocialCalc 的系統架構。

(未完,待續。)

March 01, 2011 at 09:33 AM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

SocialCalc: 緣起 (六之一)

(This is the first part in a series of Chinese translations of the SocialCalc chapter in the upcoming book, The Architecture of Open Source Applications.)
(本文採用 CC0 條款,屬於公共領域。作者的全部版稅將捐贈給國際特赦組織。)


試算表的應用歷史,已經超過 30 年了。

第一個試算表程序 VisiCalc 是由 Dan Bricklin 在 1978 年著手開發,於 1979 年問世。

最初的設計非常直接了當:可以在兩個維度上任意擴展的表格,其中每個儲存格可以存放文字、數字,以及公式。公式由普通的數學運算操作以及各種內建函數組成,並能使用其他儲存格的值作為參數。

試算表的理念看似簡單,但卻可應用於各種不同的實務領域:會計、庫存清單、建模預測、列表管理... 不同用法有著幾乎無限的可能性。這些應用使得 VisiCalc 成為第一款個人電腦領域裡的「殺手級程序」。

在隨後的幾十年里,雖然 Lotus 1-2-3 及 Excel 等後繼者提出了各種改進,但核心原理都是相同的;大部分試算表都以硬碟檔案的形式保存,並在打開或編輯時讀入記憶體。

在基於檔案的模式下,協作顯得異常困難,主要難點體現在:

  • 每位使用者都需要安裝特定版本的試算表編輯器。
  • 電子郵件往來、共享文件夾,或安裝一套專用的版本控制系統,都會增加額外的管理成本。
  • 變動追蹤功能非常有限;舉例來說,Excel 無法對格式和單元格注釋內容的變動保留歷史記錄。
  • 更新模板中的格式或公式後,還需對使用該公式的所有試算表文件進行更繁瑣的手工更新。

好在一種新的協作模式出現了,可以用非常簡單的方式解決這些問題。這就是共筆模式,由 Ward Cunningham 於 1994 年開發,並由維基百科在 21 世紀前期使其廣為人知。

與檔案模式不同,共筆模式以保存在伺服器上的頁面為基礎,不需要任何額外的軟體即可在瀏覽器中編輯。這些超文本頁面可以直接相互鏈接,甚至可以包含某個大型頁面中的部分內容。預設情況下,所有參與者都可查看並編輯最新版本,並可在伺服器上自動保存版本修訂的歷史紀錄。

受到共筆模式的啟發,Dan Bricklin 從 2005 年開始著手開發 wikiCalc。它結合了共筆系統的易於創建、多人編輯等特性,而仍然保有試算表系統中常用的視覺格式和計算理念。

(未完,待續。)

February 28, 2011 at 01:41 PM in Books | Permalink | Comments (0) | TrackBack (0)

| Digg This | Save to del.icio.us |

About

Map

  • Locations of visitors to this page

Recent Posts

  • [活動] 7/22 下午【攻殼機動隊】
  • Socialtext 應用 Scrum 方法的三年(下)
  • Socialtext 應用 Scrum 方法的三年(上)
  • 開發經驗談 (六之六)
  • 即時多人協作 (六之五)
  • 豐富文本編輯 (六之四)
  • SocialCalc (六之三)
  • WikiCalc (六之二)
  • SocialCalc: 緣起 (六之一)
  • 企業「人際層」: 化願景為現實(下)

Recent Comments

  • audreyt on Socialtext 應用 Scrum 方法的三年(上)
  • OOBE on Socialtext 應用 Scrum 方法的三年(上)
  • Ali on Socialtext 應用 Scrum 方法的三年(上)
  • randomly on 鳳たんです!
  • audreyt on 心慟凡例 / An Instance of Sinthome
  • audreyt on 心慟凡例 / An Instance of Sinthome
  • Johann Chiang on Our paroqial fermament, one tide on another.
  • Kim Feraday on Our paroqial fermament, one tide on another.
  • twitter.com/lazinet on 文章造天下,功業還蒼生。
  • Eric Hellman on Our paroqial fermament, one tide on another.

Categories

  • Books
  • Craft
  • Current Affairs
  • Film
  • Lingua
  • Meta
  • People
  • Trans
  • Web/Tech

July 2012

Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Archives

  • July 2012
  • April 2011
  • March 2011
  • February 2011
  • July 2010
  • June 2010
  • May 2010
  • April 2010
  • March 2010
  • November 2009
Subscribe to this blog's feed

License


  • Public Domain Dedication
    This work is dedicated to the Public Domain.

Audrey

  • Audrey060602
My Photo

Twitter

    follow me on Twitter