iPad 的照片管理

iPad 看照片

現在已經很多人會用 iPad 來看照片了,個人覺得拿一台 iPad 當電子相框其實也還蠻划算的,尤其是 new iPad 的視網模螢幕,看起照片感覺真的是很棒,可是我還是必須要說,iPad 的相片管理真是一團混亂。

先不考慮 iCloud 的 photostream,iPad 內的相片來源有兩種,一種是你從電腦用 iTunes 設定同步 iPhoto 相簿上來,另外一種是直接用相機拍或是用 camera kit 傳進去的,這篇文章要說的第一個問題就是這兩種來源的差異,前者,從電腦同步來的,沒辦法在 iPad 上刪除!你只能接回電腦來處理這種相片,我甚至不能在 iPad 上說我要取消同步這個相簿。

第二個問題是 camera kit 來的相片,如果你是拍 RAW+JPG 的話,用 camera kit 只能把兩種檔案都抓進 iPad,你不能選擇只匯入 RAW 或是只要 JPG,這就算了,匯進 iPad 的 RAW 是完全用不到的,基本上就是佔空間,而且你也不能透過任何方法只刪除 RAW 或 JPG,整個就是無法理解的設計啊,我覺得要是 iPad 只支援匯入 JPG 也非常合理,但是就做成要就全要,不要拉倒的狀況...

第三個問題是,本來我以為買了 iPhoto 就可以把內建的那個照片丟到一邊了,結果我發現,iPhoto 不能刪除照片,而且更鳥的事情是,照片裡面不能作範圍選取,所以我要刪除大量照片就要一張一張點,100 張要點 100 次,還不支援 multitouch 連點,真是非常不親切,不知道這些操作上的不方便什麼時候才會修正,至少我在 iOS 6 上測試過還是一樣就是了。

PS: iPhoto 是可以作範圍選取的。


jQuery.Deferred.pipe

這次的 COSCUP 有介紹jQuerydeferred,當時沒講到的 pipe,其實是非常強大的,當我開始會使用 pipe 時,那種衝擊不遜於當初看到 deferred 和 when 的時候。deferred 是用來監聽非同步變數的狀態,簡單說就是拿到變數的時候,程式還不知道它的值是什麼,deferred 常使用於像是 ajax call,使用者回應等等地方,而配合 deferred 的 when 則是用來監聽複數個 deferred 物件,利用 when 還可以處理比較複雜的非同步相依性問題,不過其實光是有這兩個工具,實際開發一些 Web Application 偶爾還是會覺得有不夠的地方。

先舉一個簡單的例子,要做一個登入頁面,然後要支援 one time password(OTP),就是像 battle.net 或是 google 的兩步認證那樣,如果簡單寫的話,用 callback,第一階段的程式碼:

$.post('/api/login', idpw, function (res) {
    if (res.requireOTP) {
        showOTPUI();
    } else {
        loginSuccess();
    }
}, loginFail);

然後接著使用者輸入認證碼後的部份:

$.post('/api/', otp, loginSuccess, loginFai);

這兩段程式碼的流程其實很簡單,就是如果帳號密碼錯,執行 loginFail,如果對的話,看有沒有需要 OTP 驗證,沒需要的話執行 loginSuccess,需要的話再跟使用者要 OTP,然後送去 server 做驗證,結果正確的話執行 loginSuccess,不正確的話執行 loginFail,可以畫成流程圖如下:

no pipe

這個流程基本上沒問題,可是身為一個程式設計師,看到重複的東西出現,就會想要把它拿掉,在這張流程圖中什麼東西重複出現了呢,就是最後的終點,Success 和 Fail 分別出現兩次,看到這種終點出現兩次就會很想修改掉,這時候 pipe 就派上用場了。

Pipe 顧名思義就是管路,和 linux 作業系統命令列介面的 pipeline 很像,一樣是一個程式處理完,結果丟到下一個程式繼續處理,一個接一個這樣,只不過 deferred 的 pipe 處理的是非同步的流程,如果使用 jQuery 的 pipe 來處理這個問題,程式碼大概會變成:

$.post('/api/login', idpw).pipe(function (res) {
    _dfd = $.Deferred();
    if (res.requireOTP) {
        showOTPUI(_dfd);
    } else {
        _dfd.resolve();
    }
    return _dfd;
}).then(loginSuccess, loginFail);

showOTPUI 那邊則要處理使用者輸入認證碼後的行為:

$.post('/api/', otp, _dfd.resolve, _dfd.reject);

這樣的程式碼就可以看到重複的 loginSuccess 和 loginFail 消失了,流程圖則變成像是下面這樣:

pipe

再舉一個例子,假設某個網路服務的使用者資料更新,要同時在前端處理上傳頭像、加密資料等,流程可能會是:

  1. 檢查欄位
  2. 上傳頭像
  3. 跟 server 要求加密用的 key
  4. 加密資料
  5. 把資料上傳

這樣的流程中,有三個動作是跟 server 作溝通的非同步工作,分別是上傳頭像、跟 server 要 key 和最後的把資料上傳,但是這五個動作又要照順序作,這種情形就非常適合使用 pipe,下面是一個大概的範例,先定義三個後面 pipe 裡面會用到函數:

var validator = function ($form) {
    return $.Deferred()[_validate($form)? 'resolve' : 'reject']();
};

var upload = function ($file) {
    var prepared = prepare($file);
    return $.post(prepared);
};

var encrypt = function (data, key) {
    var crypt_data = _encrypt(data, key);
    return $.Deferred().resolve(crypt_data);
};

這些函數在寫的時候要注意到,他們回傳的最好都是 deferred 物件,根據情況可以直接決定它的狀態,接著是重點的,表單的送出事件:

$('#profile-form').on('submit', function () {
    var $form = $(this),
        data = $form.serialize();

    validate($form).pipe(function () {
        return upload($form.find('[name=avatar]'));
    }).pipe(function (avatar_id) {
        data.avatar_id = avatar_id;
    }).pipe(function () {
        return $.get('/api/key');
    }).pipe(function (key) {
        return encrypt(data, key);
    }).pipe(function (crypt_data) {
        return $.post('/api/profile', crypt_data);
    }).done(function () {
        //Do some response to user
    });
});

而除了這類的應用外,還有一個用途,就是處理複雜的動畫效果,在 COSCUP 的 queue 的那部分,最後的例子,要把 #A, #B, #C 照順序 fade out,其實也是可以用 pipe 來處理,而這要多虧 jQuery 的 .promisequeue 和 deferred 可以接在一起,程式碼如下:

$('#A').fadeOut().promise().pipe(function () {
    return $('#B').fadeOut().promise();
}).pipe(function () {
    return $('#C').fadeOut().promise();
});

如果單純只是作動畫,那這樣寫並沒有比較好,不過要是你的動畫會和一些其他的 deferred 物件整合、串接,那這功能就很好用了。

最後下個小結論,deferred 是用來代表非同步的變數,when 是平行處理非同步變數,也可以說是並聯的狀態,pipe 則是處理序列的非同步變數,也可以說是串聯的狀態,並聯和串聯當然可以在自己任意連接,所以就可以兼顧到各種狀況了。


COSCUP 2012 slides

說好的 deferred.pipe 文章應該明天就可以貼出了~


鍵盤攻頂這檔事

認識我的大概多少都會知道我用的鍵盤不太便宜,基本款是 Cherry 的機械軸,通常要價三千左右,想我玩機械式鍵盤其實已經也好幾年了,最初是從鳥窩的團購消息聽聞到 Cherry 鍵盤,之後託親戚朋友從日本買回我第一隻 Cherry G80-3000 青軸,之後又陸陸續續買了好幾隻,整個算是發散期,什麼鍵盤都想弄一隻玩玩看,中間也見證了 PTT 鍵鼠版版的興起,到後來,卻反而收斂了起來,大概是找到最適合自己的鍵盤了,開始出清很多沒在用的鍵盤,最初買的那隻 G80-3000 也送給一位特別的朋友,最後我自己只留了兩隻 Cherry G80-3484 和一隻 Realforce 103。

五百多

不過即使收斂至此,我心中卻一直有一隻想要的夢幻規格鍵盤,沒有上市過,我想要的是 Realforce 全 30g 版的標準英文配置,一般的 Realforce 鍵盤其實是有看指位來決定按鍵配重的,分別有 30g、45g、55g,只有非常少的部份是用 30g 配重,我非常想要全 30g 配重的鍵盤,不過卻只有一隻 108 鍵日本語配置的是所有按鍵都是 30g,我因為嘗試過日語配置鍵盤,了解到我用不來,所以一直不敢貿然出手。因為已經有點淡出鍵盤市場,所以直到前陣子我才注意到我想要的夢幻規格竟然有了,是只有在大陸上市的 Realforce Pro。

Realforce Pro

看到這東西後,幾乎是沒什麼考慮就決定要買一隻了,花了一些時間總算是從淘寶買到,買的時候發現,全 30g 版本的數量比較少,價錢也比較貴,果然是稀少價值~

Realforce Pro

Realforce Pro

拿到這隻鍵盤的感想,不考慮到花了多少錢的話,我覺得實在是很棒,放在公司用一陣子後,回家用普通的 Realforce 都開始覺得按鍵有些重了,而且黑灰配色還蠻耐看的,小缺點是 Pro 鍵對我來說沒什麼用了。

Realforce Pro 對我來說,應該就是最後的一把鍵盤了吧。

btw. 這把鍵盤也是我所知道有量產的鍵盤中第二貴的,第一貴的是 Optimus Keyboard


Side Effect Free Function

在 Functional Programming 裡面,有個名詞叫做 pure function,要稱為 pure function 要滿足兩個條件:

  1. 不管在什麼情況下,用什麼方法執行,相同的輸入參數一定會產生相同的輸出。
  2. 執行這個 function 不會產生任何副作用,副作用指的是像變數的污染等。

哪些 function 是 pure function 呢,簡單來說,大部分你所見過的數學函數都是 pure function,像是三角函數,sin、cos ,不管你什麼時候執行,用什麼方法執行,只要給同樣的輸入,輸出的結果就一定是一樣的,而且這些函數本身也不會對外部有任何的影響。

再來,function 執行會有什麼副作用呢?其實就是去存取其他外部的變數或函式時,改變了外部變數的數值,如果該變數有其他地方會使用,那可能會因為這些改變,造成程式的執行結果和預期的有出入,也就是產生了 bug,這其實也是為什麼會說要避免使用全域變數的原因。

要避免改變到外部變數其實還算簡單,除了做這事情本來就是目標之一的情形外(也就是你的 function 或是物件和其他東西會有相依性),程式在設計的時候有注意到應該都可以避免,那還有什麼情形可能造成意外的副作用呢?事實上,function 的執行方法的不同會有機會產生副作用:

var neko = {
    meow: function () {
        console.log(this);
    }
};

neko.meow(); // neko

var func = neko.meow;
func(); // window

上面的範例中,我定義了一個物件,並且給了它一個 function 作為 method,接著用兩種不同的方法來執行這個 method,然後會發現兩種執行方式會讓 function 內的 this 是不一樣的,而且很不巧的,this 在物件導向程式設計上,其實還蠻常會需要它的,因為你會需要存取該物件的屬性,最簡單的方法就是用 this 來代表該物件,設計上合理,語意上也合理,可是物件的 method 的執行方法的不同卻會讓它存取到不同的 this,結果就是會有意外的副作用,JavaScript 的這種特性其實也不全然是壞處,有種稱為 "borrowing method" (或是 code reuse)的 pattern 就可以利用這個特性,現在最可靠的判斷某變數是否是陣列的 方法,也是一種 "borrowing method" 的應用。

要確保 this 不變,有不少方法,像是多包一層用 applycall 來執行,或是用新的 bind 來指定好 this 的值,一些 JavaScript Library 也有對應的功能可以利用,像是 jQuery 的 proxy,不過用 apply 或是 call 來執行其實效率上會比較差,所以我想介紹的是另一種方法,利用 JavaScript 的另外一種特性:closure,closure 指的是,在多層的 variable scope 環境下,內層的 scope 可以去存取外層 scope 的變數,一個簡單例子:

var Dog = function () {
    var gender = 'male';

    this.getGender = function () {
        return gender;
    };
};

dog = new Dog();
dog.getGender(); // male

dog 這個物件會得到一個 method 叫做 getGender,而它會回傳在這個 method 外一層,也就是 dog 物件的建構函式裡面定義的 gender 變數,這個 method 並不會因為他的 scope 內沒有 gender 變數就噴出錯誤訊息,取而代之的,它會往上一層的 variable scope 找同樣名稱的變數,一直找到最外層,也就是 root 物件那層,以網頁應用的話,root 物件就是 window 了,如果還是找不到才有機會出現錯誤訊息,利用這個特性,就可以完全的避免使用到 this 這個關鍵字來建立物件,不使用 this 的話自然就沒有我上面說的副作用了,這樣該物件的 method 不管是怎樣執行的,都不會影響到內部去存取的變數。

jQuery 裡面也有使用到這種技巧, jQuery 的 Callbacks 就是這樣子設計的物件,所以你可以在使用 Callbacks 的 Deferred 裡面看到這樣的程式碼

deferred[ tuple[0] ] = list.fire;
deferred[ tuple[0] + "With" ] = list.fireWith;

前面的 tuple[0] 是 'resolve', 'reject' 或 'notify',而 list 就是相對應的 Callbacks 物件,這段程式碼實際上就是在定義 Deferred 物件的 resolve, resolveWith, reject, rejectWith... 等屬性,可以看到他的指派方法就是直接把 Callbacks 物件的 fire 和 fireWith method 借給 Deferred 物件,所以執行 Deferred.resolve 其實就等同於執行對應 Callbacks 物件的 fire 方法,而由於 Callback 內部沒有使用到 this ,所以這樣的使用完全是沒有問題的。

這樣子的用法有什麼好處呢?我們可以先反過來看看要確保 this 正確的話,程式碼要改成怎樣:

deferred[ tuple[0] ] = function () {
    list.fire();
};
deferred[ tuple[0] + "With" ] =  function () {
    list.fireWith();
};

這樣子寫可以確保 list 裡面方法的 this 就是 Callbacks 物件本身,不過其實這樣改會造成 jQuery 的 Chain Ability 爛掉,請不要真的去改。這樣的寫法有兩個缺點:

  1. 多了一層 scope,雖然現在瀏覽器的 JavaScript 引擎讓 scope 層數和 performance 之間的影響比以前小很多了,不過還是能少就少。
  2. 程式碼變得比較不漂亮,程式碼漂不漂亮和好不好讀、好不好維護息息相關。

Side effect free 的 function 還有哪些地方可以用呢,除了像 jQuery 這樣供物件之間呼叫執行,最多的還是作為 callback function 了吧,不管是事件的 callback function 還是 XHR 的 callback function,都可以利用到這些好處,讓程式碼更好看,也減少 scope chain 的層數。


WHATWG and W3C 版 HTML5 分家

WHATWG Blog 兩週前發出的 文章 正式宣告 WHATWG 和 W3C 的 HTML5 分家了,主編 Ian Hickson 有在 mainling list 發出一篇比較完整的 說明 ,主要還是兩邊方向不一樣,不過這篇文章也把一些過去的事情說的比較清楚,像是 WHATWG 的標準以前正式名稱是 Web Application,HTML5 只是別名,而現在的本版正式名稱則改成 HTML Living Standard ,還有把裡面一些標準拆分成子標準則是 W3C 那邊這樣做,WHATWG 則是一直維持全部東西塞在一起的作法,而最重要的差異,當然還是 WHATWG 不會讓 HTML 標準穩定下來,未來會有一個確定版本的 HTML5 會是 W3C 的, WHATWG 版的會隨時更新,只要有新需求,有錯誤。


改版

Responsive

改版算是一個段落了吧,這次主要是套用 Twitter Bootstrap ,然後直接用它來排版,同時也支援 responsive design ,所以用手機、平板電腦看這邊的效果也比以前好很多,同時拿掉很多畫面上的元件,一些 JavaScript 不是那麼必要的也都拿掉了,搜尋也改成用 Google 搜尋,越來越單純化了吧~


更之前的文章