[非關影像] Diana F+ 入手

今天女友神神秘秘的下班跑去誠品,去接她的時候說是買表弟的生日禮物,結果提了兩大袋,我還在想說是甚麼東西這麼一大包。回到家之後才笑笑的說是我的生日禮物(大驚)。

就是他啦,SNOWCAT Diana F+。


背面有一隻貓。


套裝版本,有閃光燈。


再來一張。


就降 幹得好

Posted by hina

2009/10/23 23:25 2009/10/23 23:25
, , , , , , , , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/807

寫到這裡,我想說一句公道話,那就是,我之前寫的三篇都可以直接扔進垃圾桶,是一般垃圾不用回收了(翻桌)。編輯器最重要的一個部分,就是自訂的對話框(Dialog),編輯器預設的對話框,跟 FCKeditor 有些許的差異,像是上傳檔案的對話框,在 CKeditor 裡面就沒有在預設工具列內(主要是要提倡跟自家的 CKFinder 合併使用)。

首先呢,在 CKeditor 裡面,所有的對話框都是使用 JavaScript 來產生,所以在設定上並不像以前 FCKeditor 那樣改改他的原始的 html 檔案就能更動對話框,所以關於這一點,對於 JavaScript 不是很熟悉的人恐怕會遇到很多地雷。但是,我看了看還是覺得,這次在 CKeditor 裡面,還是必須要稍微看過原始碼,才能真的有效控制整個 Dialog 的狀況啊(攤手)。

我們繼續來看 Dialog 與 UI,這兩個部分其實是互相綁定的,所以如果把 UI 的部分拿掉,Dialog 可能會半殘。從原始碼看來,UI 有絕大部分是給 Dialog 使用的(CKEDITOR.ui.dialog),所以如果把 UI 的 Core 拿掉的話,那可想而知會發生什麼事情,而 CKEDITOR.dialog 系列的類別,其實是用來做設定(dialogDefinition)的,跟產生對話框並沒有直接的關係。

接著,我們先來看看設定的部分在做些甚麼事情。
  1. CKEDITOR.dialog
    用來產生與修改對話框,前提是必須要有已經設定(註冊)好的對話框按鈕元件。
  2. CKEDITOR.dialog.add
    加入(註冊)一個對話框,對話框的對象必須是已定義好的元件(Object)。
  3. CKEDITOR.dialog.addIframe、CKEDITOR.dialog.addUIElement
    歐雷~歐雷~歐雷,目前還無法使用 Iframe,而 UIElement 在 builder 有莫名奇妙的雷。
  4. CKEDITOR.dialog.exists
    檢查對話框是否已存在(已註冊)。
只有一個?那其他的呢?其他的 Class(buttonDefinition、contentDefinition、contentDefinitionObject、dialogDefinition、dialogDefinitionObject、uiElementDefinition、dialogCommand)並不是能夠直接呼叫的 API,而是要在設定中取得元件之後所能夠使用的方法,具體的使用方法依照官方範例如下:

[code javascript]
CKEDITOR.on( 'dialogDefinition', function( evt )
{
    var definition = evt.data.definition;
    var content = definition.getContents( 'page1' );
    ...
} );
[/code]在這裡的 dialogDefinition 是 CKEDITOR 預設的監聽事件,由監聽事件所傳入的 data 可以提取整個 UI 跟 Dialog 的 Definition,由這裡來做到上述的 Definition Class 的設定與控制。

至於大家很關心的 Uploader 與 FileBrowser 的部分,官方的簡單說明是這樣的(真的非常簡單)。雖然他也提供了另外一種說明,但是他依舊非常的 CKEDITOR,令人完全看不懂啊!所以說,檔案上傳到底怎麼辦呢?這裡暫時先不提,等到我說完如何加入新的按鈕之後再補上。首先,我們來看看如何在 Toolbar 裡面加入按鈕,底下是一個典型的 CKEDITOR 編輯器的 Toolbar 設定:

[code javascript]
var editor = CKEDITOR.replace( 'editor1', {
                toolbar: [
                    ['Source','-','Save','NewPage','Preview','-','Templates'],
                    ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],
                    ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
                    ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],
                    '/',
                    ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
                    ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
                    ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
                    ['Link','Unlink','Anchor'],
                    ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak'],
                    '/',
                    ['Styles','Format','Font','FontSize'],
                    ['TextColor','BGColor'],
                    ['Maximize', 'ShowBlocks','-','About','MyButton']
                ]
            });
[/code]以上是一個完整的 CKEDITOR 的按鈕,裡面有個 MyButton 是我們要加入的按鈕。是的,我們要加入的按鈕必須要在 Toolbar 裡面先定義,不然 Dialog.add 會找不到對象。接下來就是,加入一個新的 Dialog,並綁定到這個按鈕上面。

[code javascript]
// 必須要等待編輯器將 pluginsLoaded 做完才可以繼續做 Dialog 設置與綁定動作。
editor.on( 'pluginsLoaded', function( ev )
{
    // 檢查 Dialog 是否已經存在,不存在才要建立
    if ( !CKEDITOR.dialog.exists( 'myDialog' ) )
    {
        // 註冊一個新的 Dialog,名稱就叫做 'myDialog'
        CKEDITOR.dialog.add( 'myDialog', function( editor )
        {
        // 這裡所返回的,就是 dialogDefinition 的設定。
            return {
                title : 'My Dialog',
                minWidth : 400,
                minHeight : 200,
                contents : [
                    {
                    // 這裡是定義 Dialog 的內容,所使用到的都是那些 UI 與 Dialog 的 Definition。
                    // 裡面的東西有複雜到,待會我會直接寫出檔案上傳的範例。
                   }
                ]
            };
        } );
    }

    // 建立一個新的編輯器命令,並將這個編輯器命令綁定在剛剛建立好的 myDialog 上面。
    editor.addCommand( 'myDialogCmd', new CKEDITOR.dialogCommand( 'myDialog' ) );

    // 綁定剛剛 Toolbar 所設定的 'MyButton' 並且將命令執行綁定在剛剛的編輯器命令上。
    editor.ui.addButton( 'MyButton',
        {
            label : 'My Dialog',
            command : 'myDialogCmd'
        } );
});
[/code]以上就是在編輯器中建立一個按鈕並實際呼叫出對話框的操作模式。中間的 contents 我沒有寫的原因是,那個部分非常的複雜,而且以官方的 API 來看,根本無法知道架構與相依狀況到底是怎麼回事(反正我就是慧根差)。接著,我直接來舉一個檔案上傳的 contents 給各位:

[code javascript]
id : 'Upload',
hidden : true,
filebrowser : 'uploadButton',
label : editor.lang.image.upload,
// 在 Dialog 中註冊元件
elements :
    [
        {
            type : 'file',
            id : 'upload',
            label : editor.lang.image.btnUpload,
            style: 'height:40px',
            size : 38
        },
        {
            type : 'fileButton',
            id : 'uploadButton',
            filebrowser : 'info:txtUrl',
            label : editor.lang.image.btnUpload,
            'for' : [ 'Upload', 'upload' ]
        }
    ]
[/code]以上就是在 Dialog 中產生一個檔案上傳的選擇檔案輸入框與檔案上傳按鈕,實際的執行狀況如下圖所示:



那檔案會上傳到哪裡呢?好問題!檔案上傳的資料要在 Toolbar 設定完之後另外設定,官方有說明,其實也就是這樣而已:

[code javascript]
var editor = CKEDITOR.replace( 'editor1', {
               toolbar: [
                    [...略...],'MyButton']
               ],
                filebrowserUploadUrl : '/uploader/upload.php'
           });
[/code]上述的 '/uploader/upload.php' 就是檔案上傳的路徑,而他會預設傳幾個值進去(以 GET 的方式),所以在檔案上傳時,真正完整的上傳路徑是:

/uploader/upload.php?CKEditor=editor1&CKEditorFuncNum=1&langCode=en

而這個上傳所要用到的監聽很特殊,需要用 CKEDITOR.tools.callFunction 呼叫並取得返回值,而這個 API 的說明是""!根據我明查暗訪的結果,這個 callFunction 還必須要先 addFunction,而 addFunction 需要有一個依照 CKEDITOR 所建立的元素(instance),然後這一整串流程在 API 中的說明是""!

所以,CKEDITOR 的 Dialog 到底是改得更順手,還是更麻煩了呢?各位看倌你覺得呢(茶),其他的 Dialog 跟 UI 我會繼續在找時間補上,畢竟這兩個區塊才是我們真正關心的部分啊!

Posted by hina

2009/10/09 22:35 2009/10/09 22:35

我現在的心情可以這樣表示(爆炸爆炸爆炸)這樣有夠貼切嗎!這個 CKeditor 有些地方實在是超乎我的想像,他做到了一些並不是編輯器迫切需要的功能,舉例來說,他可以控制 DOM 元件。也許你會覺得,我在編輯器裡面可以控制 DOM 元件並不是什麼壞事,偏偏,他控制的是編輯器以外的 DOM 元件

當我在測試 Event 的時候,發現這個東西並不只有一種,換句話說,他的 Event 控制分成兩組,一組是自訂的監聽與觸發(類似 ActionScript 那樣),另一組是針對 DOM 元件的特定 events 作觸發(諸如 onClick 等)。這兩種 Event 控制分屬不同的類,自定監聽與觸發是單獨屬於 CKEDITOR.event,而針對 DOM 元件的部分則屬於 CKEDITOR.dom.element(等同於 CKEDITOR.document)。

這裡先講自定監聽與觸發的部分,針對 DOM 的因為實在不是屬於編輯器的範圍,所以容後再述。關於自定 Event 的部分其實方法都很簡單,大概有以下幾個項目:
  1. CKEDITOR.event.implementOn
    靜態方法,宣告一個 Object 元件,將 Object 元件的資料傳入所呼叫的 Event 中。
  2. fire
    觸發 Event。
  3. fireOnce
    觸發 Event,但是觸發完成之後會釋放所有的監聽。
  4. hasListeners
    檢查是否包含監聽函式。
  5. on
    設定監聽函式。
  6. removeListeners
    移除監聽函式。
另外還有一個類別,叫做 eventInfo,他是用來取得監聽所傳入的一些數值,而且還包含了兩個方法:
  1. data
    監聽函式傳入的任何資料。
  2. editor
    監聽函式傳入的編輯器。
  3. listenerData
    監聽函式傳入的資料(跟 data 不同,容後說明)。
  4. name
    監聽函式的名稱。
  5. sender
    監聽自身元件(Object)。
我們要建立一個自定的監聽,其實方法很簡單:
[code javascript]
var myObject = { message : 'Example' };
CKEDITOR.event.implementOn( myObject );
myObject.on( 'someEvent', function(event) {
  alert(event.message);
});
myObject.fire( 'someEvent' );
[/code]這樣就完成了一個監聽的建立與觸發。CKEDITOR.event.implementOn 這個靜態方法的用意,就是讓一個 Object 元件可以設立監聽與被觸發。我們再來看 on 與 fire 這兩個方法:

[code javascript]
var myeditor = CKEDITOR.replace( 'editor1' );
var myObject = { message : 'Example' };
CKEDITOR.event.implementOn( myObject );
myObject.on( 'someEvent',
  function(event) {
   alert(event.message);
    alert(event.listenerData.listener);
    alert(event.editor.name);
    alert(event.name);
    alert(event.data.all);
    alert(event.priority);
  },
  { message: 'Example2' },
  { listener: 'Example3' },
  100
);
myObject.fire( 'someEvent', { all: 'Example4' }, myeditor);
[/code]基本上 on 可以傳入的資料如下:
  1. 監聽的名稱。
  2. 監聽的函式。
  3. 監聽輸入的值(是個 Scope),會蓋掉 CKEDITOR.event.implementOn 宣告所建立的 Object。
  4. 監聽建立時附帶傳入的資料。
  5. 監聽優先值。
而 fire 則可傳入 data 與 editor 兩項數值,這樣可以分清楚 listenerData 與 data 的不同處嗎(其實我覺得有點畫蛇添足,但是也或許有甚麼地方會需要用到這樣的特性吧)。另外 event 的兩個方法 cancel() 與 stop() 個人是認為跟 fireOnce 有異曲同工之妙。官方的舉例如下:

[code javascript]
/* cancel() 範例 */
someObject.on( 'someEvent', function( event )
{
    event.cancel();
});
someObject.on( 'someEvent', function( event )
{
    // This one will not be called.
});
alert( someObject.fire( 'someEvent' ) );  // "true"

/* stop() 範例 */
someObject.on( 'someEvent', function( event )
{
    event.stop();
});
someObject.on( 'someEvent', function( event )
{
    // This one will not be called.
});
alert( someObject.fire( 'someEvent' ) );  // "false"
[/code]看完之後我只有一句話想說,既然不要讓人家觸發,就不要建立監聽就好啦(翻桌)

最後,CKEDITOR 的 DOM,說穿了根本是一個超級大地雷!他的工作就是用來取代 jQuery 爾等的 DOM 控制器,換句話說,他自己寫了一套像是 jQuery 這樣的 DOM 控制器,來給他的編輯器使用。偏偏,他的 API 並沒有提到如何去控制"編輯器內部"的 DOM 元件,所以,個人認為,DOM 那一塊根本是雞肋(或者說,我們都被 jQuery 寵壞了)。

到現在為止,你發現了嗎?
CKEDITOR 到目前為止都只是在作 jQuery 可以做的工作而已啊啊啊啊!

下一回我們來講地雷等級更高的 UI 跟 Dialog 的部分(這兩項才是編輯器該做的事情)。

Posted by hina

2009/09/28 17:22 2009/09/28 17:22

接續上一篇所提到的基本與完整模式,後來我在測試其他的程式碼的時後發現,所謂的基本(basic)模式,的確是很簡單的五個 JavaScript 檔案就可以做到,但是相對的,犧牲掉的部分卻比我想像中的還要多很多。而最令我震驚的部分是,在基本模式中,呼叫編輯器的 CKEDITOR 的其中一支 prototype: instances 會全數失效,所帶來的後果就是,編輯器本身會完全無法控制(所以才叫做 basic 嗎)?

所以,基本上想要客製化這個編輯器,在官方還沒有釋出(或者不打算釋出)各種 plugins 之間的相依性的話,還是得使用完整的版本(就是檔案大小高達 265 KBytes 的版本)。雖然檔案有點過大,但是以現在的網路速度來說,應該還不至於到能感覺到嚴重延遲的地步。

接著,我先來提一下 htmlParser 與 htmlWriter。這兩個函式其實主要是用來產生與寫入標準的 HTML 標記語言,不同的是,htmlParser 的作用其實是用於監聽貼入的內容,而 htmlWriter 則是單純的寫入。我們先來看 htmlParser 的方法有哪些:
  1. onCDATA, function( cdata )
    依序取出被貼入的 <sript>...</script> 內的資料。
  2. onComment, function( comment )
    依序取出被貼入的 <!-- ... --> 內的資料。
  3. onTagClose, function( tagName )
    依序取出被貼入的 </tag> 的標籤名稱。
  4. onTagOpen, function( tagName, attributes, isSelfclosing )
    依序取出被貼入的 <tag> 的標籤名稱、屬性值與是否為自我結尾標籤(self-closing tag)。
  5. onText
    依序取出被貼入的 <tag> ... </tag> 內的資料。
  6. parse
    貼入 HTML 資料。
扣除第六項之外,其他的方法都是監聽的方法,總共會針對貼入的內容的五種標籤做監聽動作。至於你問我為什麼要監聽這些動作,其實是為了能夠在寫入編輯器的內文或是取出內文時能做一些調整,當然啦,如果你是很單純的使用者打什麼你就存什麼的話,就不需要這麼費功夫了。

接著,htmlParser 還有幾個特別的類別:
  1. CKEDITOR.htmlParser.cdata
  2. CKEDITOR.htmlParser.comment
  3. CKEDITOR.htmlParser.element
  4. CKEDITOR.htmlParser.fragment
  5. CKEDITOR.htmlParser.text
以上的類別全部都是為了給 htmlWriter 用的,直接呼叫會返回 undefined,至於為什麼他要寫在 API 裡面我也不懂。而且 htmlWriter 幾乎包含了上述所有功能(除了 fragment 之外),這一塊我想官方還要做詳細一點的說明才行,不然無法直接呼叫使用是有點奇怪。

至於 htmlWriter(有雷) 又在做甚麼:
  1. openTag, function( tagName, attribute )
    開啟一個 tag,形同 <tag 的意思。但是那個 attribute 是個雷,因為這個函式完全沒有屬性設定的相關動作,所以,我在想這個地方應該是官方有所保留要做甚麼事情用的吧。
  2. openTagClose, function( tagName, isSelfclosing )
    關閉一個開啟的 tag,形同 <tag.... > 的意思。請不要跟 closeTag 搞混,這裡還有是否為自我結尾標籤(self-closing tag)的判定,他是用於關閉一個開啟中的標籤,與成對標籤的關閉不同。
  3. attribute, function( attName, attValue )
    在標籤中設定屬性,這裡的屬性設定才是有效的,而這個設定必須要在 openTagClose 之前完成。
  4. closeTag, function( tagName )
    關閉成對標籤,形同 <tag.... > ... </tag> 的意思。
  5. text, function( text )
    加入內文。
  6. comment, function( comment )
    加入注釋,形同 <!-- ... --> 的意思。
  7. lineBreak
    在原始碼加入斷行,形同 \n 的意思。
  8. indentation
    在原始碼加入縮排,形同 \t 的意思。
  9. setRules, function( tagName, rules )
    在標籤中加入標籤規則,這裡的標籤規則總共有五項:
    • indent,插入縮排(\t)。
    • breakBeforeOpen,標籤開啟前斷行(\n)。
    • breakAfterOpen,標籤開啟後斷行(\n)。
    • breakBeforeClose,標籤關閉前斷行(\n)。
    • breakAfterClose,標籤關閉後斷行(\n)。
    如果需要對某特定標籤設定標籤規則,在標籤開啟(openTag)之前需要先執行標籤規則(setRules)。
截至目前為止,這些都還是屬於 Core 的部分,還有很多可以寫,但是今天看太多 htmlWrite 的 plugin 部份,雷太多被炸得滿頭包(是的,htmlWrite 有分 core 跟 plugin 兩種版本,但是作用相同),所以下次就來講講其他的部分,大概是 editor 與 events 的部分吧。

Posted by hina

2009/09/27 16:35 2009/09/27 16:35

如果有聽過 FCKeditor 的人,那我想 CKeditor 就一定要換上來用。雖然是同一家公司出品的編輯器,但是用了這麼久的 FCKeditor,我對於他的 API 頁面少成這樣實在有點頭痛,講好聽一點有四頁(驚),講難聽一點只有一頁。自從 CKeditor 推出之後,我一看到他的 API 頁面,差點沒痛哭流涕!這才叫做 API 啊(淚)!

CKeditor 是 FCKeditor 的新作品,無論在速度或是 API 上都有很強大的進步。雖然我是在本機測試,但是幾乎瞬間啟動的編輯器畫面還是深得我心啊(大心)。同時官方也提供了完整的 source code 以供參考(做壞事),你可以在官方的 svn 裡面找到 CKeditor 的 nightbuild 版本,如果你心臟夠大顆可以試試看。

同時在 svn 裡面也有提供 CKPackager 給你下載,你可以用 pack 檔案來打包自己想要的 CKeditor 的功能。但是,因為各種原始檔案並沒有詳細說明個別的相依特性,所以自行打包的話就必須要承擔一點風險,不然編輯器有可能會跛腳。所以,除非必要,不然還是不建議自己打包,你可以使用輕量化的 ckeditor_basic 就好(完整版的 CKeditor 檔案高達 265KBytes)。

首先,我們直接跳過官方那個鳥鳥的 Develop's Guide 吧(喂喂)。先從源頭看起,當你下載回來解壓縮之後,你會看到有 _source 這個資料夾,他就是所有的原始檔案,數量龐大,不建議胡亂服用(沒人叫你吃),最重要的地方是 core 資料夾,裡面是整個 CKeditor 的核心,依照 ckeditor_basic 的設定,我們由五個 js 看起。
  1. ckeditor_base.js
  2. event.js
  3. editor_basic.js
  4. env.js
  5. ckeditor_basic.js
以上就可以架構一個檔案很小五臟俱全(7 KBytes)的編輯器。同時,這五個檔案也建立了 CKeditor 最基本的 Namespace/Classes 所需要的功能。
  1. Namespace: CKEDITOR
  2. Namespace: CKEDITOR.env
  3. Classes: CKEDITOR.editor
  4. Classes: CKEDITOR.event
至於這些東西能做些甚麼事情?
  1. event 提供:
    • 觸發(fire, fireOnce)
    • 監聽(on)
    • 監聽控制(hasListeners, removeListeners)
  2. editor 提供:
    • DOM 控制(element, elementMode)
    • 編輯區方法(暫不詳述,大致上跟 FCK 差不多,但是有一些新的,也有未完成的)
對於很基本的編輯器來說,這些控制與方法其實已經能符合八成以上的需求。

接著我來說幾個很有趣的東西(雖然是屬於 editor_base,但需要額外 plugin 支援):
  1. 虛擬(假)元件(需要 plugin 支援,並未包含在官方發佈的完整 ckeditor.js 中)
  2. 截取快照
首先,虛擬(假)元件雖然是包含在編輯器的 editor.js 中,但是他是需要你額外把 plugin 給打包進來的功能,個人猜測他還在測試中,所以並沒有真正把他包到完整的版本裡面。他的功能是,利用一張圖片來取代真正要顯示(插入)在編輯器裡的元件。這樣的動作跟 TextCube 的編輯器插入 Flash 的動作很像。他就只是插入一張替代的圖片,然後在儲存的時候再將圖片給換回真正要插入的 code

其實這個功能我已經在 FCKeditor 實作出來了,所以我很樂見他有內建這樣的功能。但是,稍微深入一點,他這個方法要呼叫還不是那麼容易,首先,你要先準備一組符合 CKEDITOR 標準的元件給他,換句話說,你必須要用 CKEDITOR API 先建立一個元件(就是你要插入的 code),然後再利用這個方法把元件換成假的。至於說,這樣作會不會多此一舉?我個人是覺得見仁見智,你要用正規表示式去洗其實也可以啦。

至於截取快照,他是另一種"儲存"的方式,不真的儲存,而是將已經輸入好的內容放到某個變數裡面(就是丟到記憶體裡面啦),然後你可以隨時取出來用。這樣可以解決遠端連線出問題的時候資料的保存,但是缺點是,萬一客戶端這邊當機,也一樣一去不復返啊(茶)。

就暫時先到這,接下來將會介紹 htmlParser/htmlWrite 這個核心命脈 plugin(笑)。

Posted by hina

2009/09/25 12:59 2009/09/25 12:59

[AS3 note.] 翻頁座標分析 part 2


r2 = b2 + (r-a)
2
r2 = b2 + r2 -2ar+r2
r =
b2/ 2a

求得第三點 D3 的座標值
(ex, r),上圖綠色的線代表書本上緣的邊界值,所以 r 值其實已經超出了邊界值(r > ey),所以必須要再次計算翻頁出來的真正 D3 的位置。由直角三角形比例線段求得點 D3 對於邊界值的距離 z

r/c = t/z
z = tc / r

所以新的 D3 座標值應該為 (ex - z, ey),其中 z 為 (r - ey)*c / r,求得新的 D3 座標點後,接下來要求的是 D4 的座標點。

(v - z)
2 + p2 = z2 -> p2 = z2 - (v - z)2
v2 + (t - p)2 = t2
v2+ t2- 2pt + p2= t2
v
2+ t2- 2pt + (z2- (v - z)2) + p2= t2
v = pt / z

p2 = z2 - (v - z)2
p2 = 2(pt/z)z - (pt/z)2
pz
2  = 2tz2 - pt2
p = 2tz
2/ (z2+ t2)
p = 2tz
2/ g2

v = (2tz
2/ g2)(t/z)
v = 2t
2 z / g2

已上求得點 p, v 對於上緣極限值的距離,所以可得點 D4 的真實座標值為 ( ex - v, ey - p )。倘若點 D3 沒有超出上緣極限值,則 D3 = D4 為共點。

基本上,若是以矩形對角線相交點為二元座標原點,則書本四個角落分別就是四個象限,公式都一樣,只有正負值的變化而以。
就降 幹得好

Posted by hina

2009/09/11 17:10 2009/09/11 17:10

[AS3 note.] 翻頁座標分析 part 1



r2 = (2c)2 + (b-r)2
r2 = 4c2 + b2 -2br+r2
r = 4c
2/2b + b/2
r = ((2a)
2 -b2 )/2b + b/2
r = 2a
2/ b

故在滑鼠任意點 (x,y) 與已知點 (fx,fy) 可求得底邊點 (r, fy)。則 r 點 x 座標值為 fx -
2a2/ b,其中:

a = 1/2 ( sqrt((fy-y)
2 + (fx-x)2 )
b =
fx-x


所以,就這樣 幹得好

Posted by hina

2009/09/10 18:03 2009/09/10 18:03
, , , , , , , , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/801

Flickr 瀏覽圖片的註解功能,花了點時間把它變成 Flash 版本了。不過因為 TC 不能放這種跨網域存取的 flash 檔案,所以只好放在其他地方了。在這邊附上截圖給大家參考:



這個東西並不難,滑鼠動態圈選的部分之前的文章已經有提到兩個很完整的範例了。至於動態圈選的時候,因為利用 Sprite 來畫框框,配合 MOUSE_MOVE 的監聽,會出現殘影,所以,其實可以利用 MovieClip 來做,只要加一塊半透明的 MovieClip 然後利用 MOUSE_MOVE 來控制 MovieClip 的 Scale 特性,就不會有殘影問題了。缺點是,如果這塊 MovieClip 裡面還放了東西,那就會變形囉。

就降~ 幹得好

Posted by hina

2009/08/26 16:04 2009/08/26 16:04

[灌水] 這是灌水文章...

上次的那個程式改成這樣了。


嗯,這篇是純灌水,等我想到再補 幹得好

Posted by hina

2009/08/21 23:27 2009/08/21 23:27
, , , , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/796

http://telepathy.freedesktop.org/wiki/Pymsn

update, 這個 Python 實在是大的要命,測試過後發現以下問題:
  1. notification.py 裡面少一個 event handler,會讓執行過程中跳出 WARNING。

  2. 如果對話視窗沒有被使用者關閉,當 pymsn 重新登入的時候,使用者的控制項會失效。原因在於 msnp 再重新登入之後,查到使用者的對話視窗,會送一次 RNG,而這個 RNG 會重新對 switchboard server 申請一個新的 session 來給 Client 端使用。然而,pymsn 在送回 RNG 之後,使用 ANS IRO 的 transaction_id 被重新歸零了,所以會導致同一個 Client 端有兩個 session,造成無法對應到使用者而失效。
解決的辦法是:
把它(event handler)加回去,打開 pymsn/msnp/notificatoin.py 檔案。
我在 --- Presence & Privacy --- 的底下加入:
[code]
def _handle_GCF(self, command):
   pass
[/code]另外一個 event 錯誤的問題目前尚無解答,這個 transaction_id 為什麼會被歸零,還是要查看原始碼才知道問題出在哪裡。不過,因為 Python 我實在是太不熟了,等我看懂我想這個問題應該就有人會解決了吧。
[code]
INFO:protocol:switchboard_manager:requesting new switchboard

/// 這裡的 SB 的 transaction_id 是 12

DEBUG:Transport:>>> XFR 12 SB
DEBUG:Transport:<<< XFR 12 SB 207.46.26.159:1863 CKI 355252581.3720.42179215 U messenger.msn.com 1
DEBUG:Transport:<-> Connecting to 207.46.26.159:1863
DEBUG:Transport:>>> USR 0 bot1@hinablue.me 355252581.3720.42179215
DEBUG:Transport:<<< USR 0 OK bot1@hinablue.me Python%20MSN%20Bot
INFO:protocol:switchboard_manager:New switchboard attached
DEBUG:Transport:>>> CAL 1 hinablue@msn.com
DEBUG:Transport:<<< CAL 1 RINGING 355252581
DEBUG:Transport:<<< JOI hinablue@msn.com *9mil%20Hina::Cain%20@%20NB%20猛當機...%20該不會是在討錢了吧%20orz 2789003308
DEBUG:Transport:<<< RNG 1983480976 207.46.26.47:1863 CKI 84735.18013320 hinablue@msn.com *9mil%20Hina::Cain%20@%20NB%20猛當機...%20該不會是在討錢了吧%20orz U messenger.msn.com 1
DEBUG:Transport:<-> Connecting to 207.46.26.47:1863

/// 這裡送出了 RNG 並且連線到 switchboard server

DEBUG:Transport:>>> ANS 0 bot1@hinablue.me 84735.18013320 1983480976

/// 拿回來的 ANS 的 transaction_id 是 0,但是這裡的 transaction_id 應該是接續 XFR 的 trid+1="13" 才是正確的

DEBUG:Transport:<<< IRO 0 1 1 hinablue@msn.com *9mil%20Hina::Cain%20@%20NB%20猛當機...%20該不會是在討錢了吧%20orz 2789003308

/// 既然 transcation_id 都不對了,IRO 自然也就不是原來的 Client 了。

DEBUG:Transport:<<< ANS 0 OK

/// 這裡 ANS OK 沒錯,但是 ANS 的對象已經不是原來的 Client 了。
[/code]以上就是他的錯誤,至於怎麼解,目前傷腦筋中。

在 FreeBSD 裡面測試了一下,需要安裝:
  1. python(廢話)。
  2. py-gobject,路徑在 /usr/ports/devel/py-gobject/。
  3. ElementTree,路徑在 /usr/ports/devel/py-elementtree/。
  4. pyOpenSSL,路徑在 /usr/ports/security/py-openssl/。
  5. pyCrypto,路徑在 /usr/ports/security/py-pycrypto。
  6. epydoc,路徑在 /usr/ports/devel/epydoc/,產生 api-doc 要用的。
然後就可以 python test.py 測試了。
[code]
test [/home/hinablue/pymsn-0.3.3] -hinablue-> python test.py
Account: (打入帳號)
Password: (打入密碼)
DEBUG:Transport:<-> Connecting to messenger.hotmail.com:1863
DEBUG:Transport:>>> VER 0 MSNP15 MSNP14 MSNP13 CVR0
DEBUG:Transport:<<< VER 0 MSNP15
...(後略)
[/code]按下 ctrl+C 就可以離開了。至於互動上,要參考 user-api-doc 去自己實作囉。

Posted by hina

2009/08/12 15:54 2009/08/12 15:54
, , , , , , , , , , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/794

強者請直接參考這兩篇:
  1. [AS 3] Actionscript Image Crop Project – Part I – Drawing Rectangle
  2. [AS 3] Actionscript Image Crop Project – Part II – Copying Bitmap Data
上述兩篇是教你怎麼在 Flash 裡面利用滑鼠的拖曳動作來做圈選框,並將圈選框內的圖形給複製出來。我呢,則是將圈選框小改了一下,因為利用 Shape 透過 graphics 的方式來做的話,會有很多影分身出現。所以我就換了個思考模式,一樣是利用滑鼠的拖曳動作,把他換成 MovieClip 來做的話,就不會有影分身問題,而且也可以將透明度給拉出來,讓圈選的部位可以半透明。

這是影片測試結果,我可以多次的圈選區塊:


線上輸出測試:

然後,輸出圈選區塊的座標值:


礙於這是私人需求的程式,所以原始碼就不在此野人獻曝了,基本上是參考我所列出來的那個網站修改而來的,有需要的人就煩請自己動動腦囉。

Posted by hina

2009/08/09 02:37 2009/08/09 02:37
, , , , , , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/793

筆記一下。

在 FreeBSD 7 以後掛載大容量硬碟,建議使用 gpart 來做規劃。核心需要重編一下,這是 man gpart 的建議。我只編我需要的兩個項目而已,其他的,若是有需要可以自行選用。

[code]
options    GEOM_PART_BSD
options    GEOM_PART_GPT
[/code]
然後,先確定你的機器有抓到設備,接著我們就開始來做新硬碟的切割。我這次是掛載了四顆 1TB 的硬碟,所以他們分別叫做 ad4、ad6、ad8、ad10,其實操作方法都一樣。

[code]
gpart create -s GPT ad4
gpart show
=>        34    1953525101    ad4    GPT    (932G)
              34    1953525101    -- free --

gpart add -b 34 -s 1953525101 -t freebsd-ufs ad4
gpart show
=>        34    1953525101    ad4    GPT    (932G)
             34    1953525101         1    freebsd-ufs    (932G)
[/code]由於我整顆硬碟不分割使用,所以在 gpart add 的時候,磁區自然就使用全部了。至於 34 之前不能用,是因為那是留給 PMBR (sector 0)、primary GPT header (sector 1) 和 primary GPT table (sector 2~33) 用的。

接下來只要格式化之後,再掛起來就可以用了。
[code]
newfs -b 16384 -f 2048 -U /dev/ad4p1
[/code]對了,用 gpart 新增的磁區,它的 index 從 1 開始依此類推,所以第一個就是 ad4p1 了。

ps. 核心若是沒編譯,系統會超級不穩。

Posted by hina

2009/07/27 17:49 2009/07/27 17:49
, , , , , , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/792

[CSS tech.] Blueprint CSS simple howto

我不知道該怎麼樣去解釋甚麼是 Blueprint CSS Framework,所以,底下我直接摘錄 Blueprint CSS Framework 的官方 README 中的說明(謎:你是要充版面吧),另外,他們也有 Google Code 可以參考:
Welcome to Blueprint! This is a CSS framework designed to cut down on your CSS development time. It gives you a solid foundation to build your own CSS on. Here are some of the features BP provides out-of-the-box:

* An easily customizable grid
* Sensible default typography
* A typographic baseline
* Perfected browser CSS reset
* A stylesheet for printing
* Powerful scripts for customization
* Absolutely no bloat!
簡單來說,他是一個蠻基本的 CSS 設定,但是除了基本設置之外,另外有分欄設定,也有針對列印等設置。太過詳細的這裡就不贅述,基本上我覺得他不算是一個真正的 framework,不過如果拿來當作是一個開始也算是不錯的選擇。畢竟,寫了太多 CSS 之後,其實有個統一的標準也是一件好事。從官方下載檔案之後,其中我們之需要 blueprint 資料夾就可以了。我們將這個資料夾放到我們放置 CSS 檔案的資料夾中,例如:styles/blueprint,這樣,我們就可以開始規劃我們的 Layout 了。
[code html]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Blueprint simple layout</title>
  <!-- Framework CSS -->
  <link rel="stylesheet" href="./styles/blueprint/screen.css" type="text/css" media="screen, projection">
  <link rel="stylesheet" href="./styles/blueprint/print.css" type="text/css" media="print">
  <!--[if lt IE 8]><link rel="stylesheet" href="./styles/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
</body>
</html>
[/code]事前準備工作結束,單純的把 Framework 給寫進來就好了,接著,我們得思考我們的 Layout 該長成甚麼樣子呢?假設我們要做個三欄式的模樣,那它大概會長成這個樣子:

然後呢?我們回到 HTML 裡,一開始,我們需要準備一個"容器"來裝我們所需要的東西。這個容器通常我們將它放置在最外層,使用 DIV 標籤,並給他一個 ID 命名為"container"。在使用 Blueprint CSS 時,我們必須要有這個容器,關於這一點請牢記。

我們再回過頭來看看 Blueprint CSS 到底做了甚麼事情。關於 screen.css 這支 CSS 檔案的原始檔案,都在 src 的資料夾底下,這個檔案總共包含了有:Reset、Grid、Forms、Typography 爾等的設定。另外 ie 則是使用 ie.css 做額外的設置。

首先,我們先來看 Reset,顧名思義,他就是將 CSS 的設置針對瀏覽器做初始化的動作。關於這方面的工具其實很多,包括 jQuery 也有這樣的 Plugin 可以使用,不過這裡不提 CSS 以外的事情,所以這是基本重新設定 CSS Styling 的方式。

接著,Typography,針對文字控制(印刷)的部分做一個標準化的動作,其實這個所指的印刷,不單純是所謂的列印這個動作,而是廣泛的針對顯示在螢幕上的樣式做設定。換句話說,他是統一畫面上文字控制的一個初始化設定。當然,Blueprint CSS 也是有針對列印的部分額外提出一個 print.css 來做控制。

再來,Forms,針對所有表單元件進行初始化。當然,這些設定是可以依照個人需求去修改的,不過,並不建議直接修改這個檔案,最好另外將其設定寫在額外的 CSS 檔案會比較好。

最後,Grid,針對容器的設定。這是這個 Framework 的靈魂所在,所有關於容器這個部份的設定,都在這個 CSS 裡面完成。使用這個 Framework 的限制是,容器的大小固定在 950px 以內,所以若是要使用更大的容器尺寸,這個 Grid 設定就不敷使用了。不過,其實還是有跡可循的,關於這方面在此暫時跳過。

所以,其實這一整個 Framework 在做的事情,只是幫你把粗略的架構給畫出來,而且你不需要去針對各項元件的設定而傷透腦筋。不過,要真的從中去學習或是修改這樣的 Framework,除非你有非常多的時間跟耐性,不然我並不建議真的需要去理解它(Framework)到底在做甚麼。

回到 Grid,我們其實對於容器,只需要知道我們所需要的大小即可。由 Blueprint CSS 的預設容器大小 950px 來看,我們倘若需要三欄設定,左與右皆為 190px,那麼依照 Blueprint CSS 的設定,左右的 Class 只要設定為 span-5 即可,那麼中間怎麼辦呢?依照簡單的數學公式:
950 - 190*2 - 10*2 = 550
所以,中間的寬度是 550px,那麼我就可以從 Grid 中得知,中間的 Class 我可以設置為 span-14。而,為了避免留白的推擠導致欄位被迫換行,我可以在左右的 Class 分別加上 first 與 last 的 Class 設定,來清除空白。所以,我們由 Grid 中所得知的設定,就是如此。
[code html]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Blueprint simple layout</title>
  <!-- Framework CSS -->
  <link rel="stylesheet" href="./styles/blueprint/screen.css" type="text/css" media="screen, projection">
  <link rel="stylesheet" href="./styles/blueprint/print.css" type="text/css" media="print">
  <!--[if lt IE 8]><link rel="stylesheet" href="./styles/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
  <div class="container">
 
    <div class="header">
      <h1>header</h1>
    </div>
    <hr>
   
    <div class="span-5 first">
      <h1>sidebar 1</h1>
    </div>
   
    <div class="span-14">
      <h1>content</h1>
    </div>
   
    <div class="span-5 last">
      <h1>sidebar 2</h1>
    </div>
   
    <hr>
    <div class="footer">
      <h1>Footer</h1>
    </div>
 
  </div>
</body>
</html>
[/code]這樣就完成了。沒有錯,不要懷疑,這樣就完成了三欄式的基本設定。

如果需要更複雜的設定呢?其實 Grid 也幫你準備好了,只是,這樣的類別設定是不是真的很容易理解?老實說我個人還是有相當的疑慮。對於特殊的定位或是相關位置控制,可以使用的類別有:append、prepend、pull、push 這四種,不過,前兩個並不帶有浮動(float)設置,後兩者則各自帶有浮動與相對定位(position: relative;)設置,在使用上並不相同。

我舉個例子,不過這種較為複雜的設定,希望是對於物件定位稍有了解的人再來看會比較容易理解。

實際上的操作是這樣的:
[code html]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Blueprint simple layout</title>
  <!-- Framework CSS -->
  <link rel="stylesheet" href="./styles/blueprint/screen.css" type="text/css" media="screen, projection">
  <link rel="stylesheet" href="./styles/blueprint/print.css" type="text/css" media="print">
  <!--[if lt IE 8]><link rel="stylesheet" href="./styles/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
  <style type="text/css" media="screen">
   
  </style>
</head>
<body>
  <div class="container">
 
    <div class="header">
      <h1>header</h1>
    </div>
    <hr>
   
    <div class="span-5 first">
      <div style="height: 3em;">
      </div>
      <h1>Sidebar 1</h1>
    </div>
   
    <div class="pull-5">
      <h1>Sidebar 0</h1>
    </div>

    <div class="span-14">
      <h1>Content</h1>
      <br><br><br><br><br><br><br>
    </div>
   
    <div class="span-5 last">
      <h1>Sidebar 4</h1>
    </div>

    <div class="span-14 push-5 last">
      <h1>Sidebar 3</h1>
    </div>
   
    <hr>
    <div class="footer">
      <h1>Footer</h1>
    </div>
 
  </div>
</body>
</html>
[/code]其實如果對定位的關係不甚理解的話,其實這個地方並不是那麼容易懂。如果換個方式來說的話,其實它就是浮動元件(float)中,利用推齊(clear)的屬性來做成的效果,只是,在這個地方他利用浮動與留白來做,好處是可以較為精準的控制元件的位置,缺點是,相對於使用推齊的方式來說,不容易理解。

大致上是這樣,對於做 Layout 來說,不失為一個好用的工具。另外推一個好站:DivtoDesign,關於他的 CSS Fframework 的文章可以參考這一篇:960 CSS Framework – Learn the Basics

最近心得:DIV 不是萬能,嚴謹易讀的 HTML 結構才是唯一正途!

Posted by hina

2009/07/13 15:06 2009/07/13 15:06

[IE Sucks] 我操你媽天殺的 IE6+7

WTF IE 6+7 Sucks!



WTF IE8, print always reload image source from remote.

我已經不知道要說甚麼了 怎麼辦

Posted by hina

2009/07/09 16:17 2009/07/09 16:17

連線,其實並不是一件簡單的事情。要怎麼樣讓一個程序,能夠持續的跟伺服器溝通,卻不會因為逾時等問題導致連線中斷,這就是即時通訊比較麻煩的地方。首先,我們首先需要做的,就是跟 MSN 的服務做溝通的動作,這個溝通動作有詳細的命令,請參閱:http://msnpiki.msnfanatic.com/index.php/Reference:Commands

注意,溝通的動作如果送太多次,會被 MSN 服務給擋掉喔!所以請勿輕易用固定 IP 的主機嘗試。

首先,跟 MSN 服務溝通的第一步,我們使用 php 的 fsockopen 來與服務器做溝通。並且告訴他我們所使用的通訊協定版本。
[code php]
<?php
    $id = 1;
    $fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
    stream_set_timeout($fp, 2);
    if (!$fp) {
        echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
        exit;
    }
    fwrite($fp, "VER $id MSNP15 CVR0\r\n");
    fclose($fp);
?>[/code]你沒有看錯,這就是連線的第一步,單純的告訴 MSN 服務器,我要使用 MSNP15 的 protocol。接下來呢,我們要告訴他關於我們(客戶端)的一些訊息,使用 CVR 這個指令來完成。
[code php]
<?php
   $id = 1;
   $fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
   stream_set_timeout($fp, 2);
   if (!$fp) {
       echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
       exit;
   }
   fwrite($fp, "VER $id MSNP15 CVR0\r\n");
    while (!feof($fp)) {
        $data = fgets($fp, 4096);

        $id++;
        $code = substr($data, 0, 3);
        switch ($code) {
            case 'VER':
                fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs bot1@hinablue.me\r\n");
            break;
            default:
                fwrite($fp, "OUT\r\n");
        }
    }
    fclose($fp);
?>[/code]嗯?就這樣,你也沒看錯,我用了 CVR 來告訴他我所使用的帳號以及我的相關資訊。關於 CVR 能夠傳送甚麼東西,請參考這裡的說明:http://msnpiki.msnfanatic.com/index.php/Reference:Commands。接下來,我要告訴他我是誰。這時候要先送個 USR 指令跟他說。
[code php]
<?php
   $id = 1;
   $fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
   stream_set_timeout($fp, 2);
   if (!$fp) {
       echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
       exit;
   }
   fwrite($fp, "VER $id MSNP15 CVR0\r\n");
    while (!feof($fp)) {
       $data = fgets($fp, 4096);
       $id++;
       $code = substr($data, 0, 3);
       switch ($code) {
           case 'VER':
               fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs bot1@hinablue.me\r\n");
           break;
            case 'CVR':
                fwrite($fp, "USR $id SSO I bot1@hinablue.me\r\n");
            break;
           default:
               fwrite($fp, "OUT\r\n");
       }
   }
    fclose($fp);
?>[/code]跟他說完我是誰之後,接下來的動作便是檢查是否已經登入了 Passport.NET 的網絡。登入的動作就是上一篇所說的 SSO 的動作(如果是使用 MSNP9 的話則是 TWN),如果已經登入了,那我們就需要取得 Passport.NET 的 token 來丟給 MSN 服務,讓他知道我已經登入,並且取得 ticket 了。另外,這裡需要一些特別的運算,我後面再提。
[code php]
<?php
   $id = 1;
   $fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
   stream_set_timeout($fp, 2);
   if (!$fp) {
       echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
       exit;
   }
   fwrite($fp, "VER $id MSNP15 CVR0\r\n");
   while (!feof($fp)) {
       $data = fgets($fp, 4096);
       $id++;
       $code = substr($data, 0, 3);
       switch ($code) {
           case 'VER':
               fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs bot1@hinablue.me\r\n");
           break;
           case 'CVR':
               fwrite($fp, "USR $id SSO I bot1@hinablue.me\r\n");
           break;
            case 'USR':
                // 這裡是做登入檢查的地方,現在暫時不做,讓他直接離開。
                fwrite($fp, "OUT\r\n");
            break;
           default:
               fwrite($fp, "OUT\r\n");
       }
   }
   fclose($fp);
?>[/code]我想取得 ticket 的地方並不麻煩,最主要的是要將 login_code 算出來,在 MSNP15:SSO 的地方其實有提到要如何去計算那些值。而在現成的類別裡面,也已經提供了運算的方式,所以我這邊就不再贅述,基本上這些數值運算是 MSN 服務器所規定的,所以你不算也不行。

在取得登入動作之前,還有兩個預設的動作,一個是 XFR,另一個是 GCF,所以我們個別處理。
[code php]
<?php
   $id = 1;
   $fp = fsockopen('messenger.hotmail.com', 1863, $errno, $errstr, 5);
   stream_set_timeout($fp, 2);
   if (!$fp) {
       echo "Can't connect to messenger.hotmail.com:1863, error => $errno, $errstr";
       exit;
   }
   fwrite($fp, "VER $id MSNP15 CVR0\r\n");
   while (!feof($fp)) {
       $data = fgets($fp, 4096);
       $id++;
       $code = substr($data, 0, 3);
       switch ($code) {
           case 'VER':
               fwrite($fp, "CVR $id 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs bot1@hinablue.me\r\n");
           break;
           case 'CVR':
               fwrite($fp, "USR $id SSO I bot1@hinablue.me\r\n");
           break;
           case 'USR':
               // 這裡是做登入檢查的地方,現在暫時不做,讓他直接離開。
               fwrite($fp, "OUT\r\n");
           break;
            case 'XFR':
                @list(/* XFR */, /* id */, /* NS */, $server, /* ... */) = @explode(' ', $data);
                @list($ip, $port) = @explode(':', $server);
                fclose($fp);
                $fp = @fsockopen($ip, $port, $errno, $errstr, 5);
                if (!$fp) {
                    return false;
                }
                stream_set_timeout($fp, 2);
                fwrite($fp, "VER $id MSNP15 CVR0\r\n");
                break;
            break;
            case 'GCF':
                // 這裡會取得非常大量的資料,暫時不做處理,直接讓他離開。
                fwrite($fp, "OUT\r\n");
            break;
           default:
               fwrite($fp, "OUT\r\n");
       }
   }
   fclose($fp);
?>[/code]這樣,就完成了"一次"與服務器完整的連線動作。我這邊提供完整記錄的 log 檔案給大家參考。
[code]
VER 1 MSNP15
CVR 2 1.0.0000 1.0.0000 1.0.0000 http://msgr.dlservice.microsoft.com http://download.live.com/?sku=messenger
XFR 3 NS 64.4.34.156:1863 U D
VER 4 MSNP15
CVR 5 1.0.0000 1.0.0000 1.0.0000 http://msgr.dlservice.microsoft.com http://download.live.com/?sku=messenger
GCF 0 7019
<Policies>
<Policy type="ABCH">
<policy>
<set id="push" service="ABCH" priority="200">
      <r id="pushstorage" threshold="180000" />
    </set>
<set id="delaysup" service="ABCH" priority="150">
  <r id="whatsnew" threshold="900000" />
  <r id="whatsnew_storage_ABCH_delay" timer="900000" />
</set>
</policy>
</Policy>
<Policy type="SHIELDS">
<config>
  <shield>
    <cli maj="7" min="0" minbld="0" maxbld="9999" deny="" />
  </shield>
  <block>
    <hashes>
    </hashes>
    <regexp>
      <imtext value="cGhvdG8yMzRcLnppcA==" />
      <imtext value="aW1nMDIxXC56aXA=" />
      <imtext value="dGFueWFiYWJlXC56aXA=" />
      <imtext value="c3R1ZmZcLnppcA==" />
      <imtext value="Zm90b3NcLnppcA==" />
      <imtext value="dHVmb3Rv" />
      <imtext value="Z2V0LW1lc3Nlbmdlcg==" />
      <imtext value="Mm5udmM3" />
      <imtext value="YmxvY2tpbnJpbw==" />
      <imtext value="bWVzc2FnaW5nLW5hbWVz" />
      <imtext value="cGljdHVyYTAwMg==" />
      <imtext value="bWVzc2VuZ2VyLXNjYW4=" />
      <imtext value="c3VtbWVyMjAwOA==" />
      <imtext value="bWVzc2VuZ2VyZGVsZXRlY2hlY2tlcg==" />
      <imtext value="cGhvdG9hbGJ1bTIwMDc=" />
      <imtext value="aW1hZ2UwMjlcLnppcA==" />
      <imtext value="Zm90b183ODFcLnppcA==" />
      <imtext value="Y2hpcnN0bWFzLTIwMDdcLnppcA==" />
      <imtext value="cGhvdG8yMDA3LTEyXC56aXA=" />
      <imtext value="aW1nNS0yMDA3XC56aXA=" />
      <imtext value="bXlwaWN0dXJlcy0wMTA4XC56aXA=" />
      <imtext value="d3d3XC5la2FzdGFtb251XC5pbmZv" />
      <imtext value="d3d3XC5tc24tZnJpZW5kXC5jb20=" />
      <imtext value="d3d3XC5zb250YXJpaFwuaW5mbw==" />
      <imtext value="cGhvdG9zMS0yMDA4XC56aXA=" />
      <imtext value="aGFwcHkyMDA4XC5leGU=" />
      <imtext value="aGFwcHlfMjAwOFwuZXhl" />
      <imtext value="aGFwcHktMjAwOFwuZXhl" />
      <imtext value="bmV3X3llYXJzX2xldHRlcl9mbGFzaFwuZXhl" />
      <imtext value="d3d3XC5tc25saXN0c3RhdHVzXC5jb20=" />
      <imtext value="d3d3XC5nb2xkd2luZG9zMjAwMFwuY29t" />
      <imtext value="d3d3XC5tc253ZWJpbWFnZXNcLmNvbQ==" />
      <imtext value="d3d3XC5tYWlubXNuXC5jb20=" />
      <imtext value="d3d3XC5idXJhc2lzZW5pbnllcmluXC5pbmZv" />
      <imtext value="bWFpbm1zblwuY29t" />
      <imtext value="d3d3XC5lbXJldFwuaW5mbw==" />
      <imtext value="d3d3XC5tc25zcHlcLmV1" />
      <imtext value="bWFpbm1zblwubmV0" />
      <imtext value="aW1hZ2VzXC5pZG9ob3N0XC5jb20=" />
      <imtext value="Zm1jb25zdWx0aW5n" />
      <imtext value="bXNuZ2FsbGVyeVwubXNcLmZ1bnBpY1wuZGU=" />
      <imtext value="ZS1hZnlvbmthcmFoaXNhclwuaW5mbw==" />
      <imtext value="YWNpc2FsYXZhbnNcLmluZm8=" />
      <imtext value="YWNpbGFzdGlyXC5pbmZv" />
      <imtext value="YW1hem9uaGFsa2lcLmluZm8=" />
      <imtext value="ZWthcnNcLmluZm8=" />
      <imtext value="YmlyZXljaVwuaW5mbw==" />
      <imtext value="cmV1dHlcLmluZm8=" />
      <imtext value="cG9ydGFrYWxsaWRhdmV0XC5pbmZv" />
      <imtext value="YW1hem9uZGFrYXlib2xkdW1cLmluZm8=" />
      <imtext value="ZGFuc2FkaW1pXC5pbmZv" />
      <imtext value="dXlzYWxsaWtcLmluZm8=" />
      <imtext value="YmV6Z2lcLmluZm8=" />
      <imtext value="c3VsYW5kaXJtYQ==" />
      <imtext value="dHVuYWJhbGlnaQ==" />
      <imtext value="eW9ydW5nZXNlbA==" />
      <imtext value="dHVoYWZraW1zZQ==" />
      <imtext value="YWNpc2FsY2FwXC5pbmZv" />
      <imtext value="Ym95YW1hZ3VjdQ==" />
      <imtext value="dHV0dXNrYW5saWs=" />
      <imtext value="YWNpc2FsY2Fw" />
      <imtext value="bXNucHJvZmlsZXNcLm1zXC5mdW5waWNcLmRl" />
      <imtext value="aW1hZ2VzXC5nZXRlbmpveW1lbnRcLm5ldA==" />
      <imtext value="bXltc25nYWxsZXJ5" />
      <imtext value="ZnVucGljXC5kZQ==" />
      <imtext value="aW1hZ2UwMzFcLnppcA==" />
      <imtext value="Zm90bzcyMmE2" />
      <imtext value="cGhvdG8yMVwuemlw" />
      <imtext value="cGljdHVyZTIyXC56aXA=" />
      <imtext value="cGljdHVyZXo5MjNcLnppcA==" />
      <imtext value="cGhvdG9fNjg4LWpwZ1wuemlw" />
      <imtext value="bXlwaG90bzk0XC56aXA=" />
      <imtext value="dmlkZW9cLmV4ZQ==" />
      <imtext value="a3V6ZW5cLmV4ZQ==" />
      <imtext value="bWlyY1wuZXhl" />
      <imtext value="YmFja2Rvb3I6aXJjL2Zsb29kXC5jYw==" />
      <imtext value="Zm90b18wMjdcLnppcA==" />
      <imtext value="Zm90bzcwOGs2XC56aXA=" />
      <imtext value="bXNuYmxvY2tsaXN0XC5jb20=" />
      <imtext value="bXNuLWZyaWVuZFwuY29t" />
      <imtext value="cXVpZW50ZWFkbWl0ZVwuY29t" />
      <imtext value="Y2hlY2ttZXNzZW5nZXJcLm5ldA==" />
      <imtext value="bXNuYmxvY2tlcmxpc3RcLmNvbQ==" />
      <imtext value="bXNubGlzdHN0YXR1c1wuY29t" />
      <imtext value="ZnJpZW5kbHktb2ZmZXJcLmNvbQ==" />
      <imtext value="c2plZ2F0XC5waWNzXC5za2FxXC5pbmZv" />
      <imtext value="c2V0dXBcLmV4ZQ==" />
      <imtext value="aG9zdGlsZWFwcGxldA==" />
      <imtext value="Z3NkYWdkZmdhc2RnczI0NTc0NTIyNDdcLmV4ZQ==" />
      <imtext value="Z3NkZGFzMjQ1ODcyMTRnc2RcLmV4ZQ==" />
      <imtext value="Zm90by0zMTFfanBlZw==" />
      <imtext value="aW1hZ2UyMDZcLmpwZy13d3dcLnBob3Rvc2hhcmVcWzFdXC5jb20=" />
      <imtext value="aW1hZ2UyMDZcLmpwZw==" />
      <imtext value="YnVzaFwuY29t" />
      <imtext value="Y2FtZGFcLmV4ZQ==" />
      <imtext value="eW91dHViZV9naXJpc19wcm9ncmFtaVwuZXhl" />
      <imtext value="Y2gzY2szclwuaW5mbw==" />
      <imtext value="cjU3OWRrYTkyalwuemlw" />
      <imtext value="MjAzXC4xNTVcLjc0XC45MS9lbGliL2Jsb2cvYmxvZ1wuaHRtbA==" />
      <imtext value="aWRcLmI0bmdcLmluZm8=" />
      <imtext value="bW9uY2xvY2hlclwuY29t" />
      <imtext value="aW1hZ2Vzd2l0Y2hcLmluZm8=" />
      <imtext value="c3BsaW50ZXJcLmhvc3RpbWdzXC5pbmZv" />
      <imtext value="dWx0aW1hdGUtc3R1ZmZcLmluZm8=" />
      <imtext value="bmV3ZXN0cGljdHVyZTAzXC5qcGc=" />
      <imtext value="d3d3XC5mcmVlcGxheWxpc3RcLmluZm8=" />
      <imtext value="bm90aWNpYXNkb2JyYXNpbFwuY29tXC5zYXBvXC5wdC9ub3RpY2lhdXJnZW50ZWJyYXNpbG51bWVybzk4MjFcLmNvbQ==" />
      <imtext value="d3d3XC5ob3JueW1hdGNoZXNcLmNvbQ==" />
      <imtext value="d2luZG93c215X3Bob3Rvc18xNTMwMVwuemlw" />
      <imtext value="cGhvdG9zXzE1MzAxXC56aXA=" />
      <imtext value="d2ViY2FtZXJhNHlvdVwubmV0" />
      <imtext value="dXN1YXJpb3NcLmx5Y29zXC5lcy9saXl0cmU=" />
      <imtext value="dGhld29tYW5pemVyXC5uZXQ=" />
      <imtext value="aW1wbGF5XC5jb20=" />
      <imtext value="aW1wbGF5XC5vcmc=" />
      <imtext value="Ym9ieXVwXC5jb20=" />
      <imtext value="YWlydW5pcXVlXC5ib2J5dXBcLmNvbQ==" />
      <imtext value="Ym9iYmxha1wuY29t" />
      <imtext value="cGljcy1hdC10aGUtcGFydHlcLmNvbQ==" />
      <imtext value="Ym9iem9wXC5jb20=" />
      <imtext value="bW9vcnNoXC5jb20=" />
      <imtext value="Ym9ieXVw" />
      <imtext value="d3d3XC5teXNwY1wubmV0" />
      <imtext value="d293YmFtXC5jb20=" />
      <imtext value="Zm90b1wuZXhl" />
      <imtext value="ZHNjMjAwOTAxMTdcLmpwZ1wuZXhl" />
      <imtext value="ZnJpZW5kaW1zXC5jb20=" />
      <imtext value="bm93cG91bmRzXC5jb20=" />
      <imtext value="Y2xpcGRlZXBzXC5jb20=" />
      <imtext value="YmVpbmctc2luZ2xlXC5jb20vaW52aXRhdGlvblwucGhw" />
      <imtext value="aGVhcnRtZWJhZFwuY29tL2RhdGVcLnBocA==" />
      <imtext value="ZGlwaWV0cm8xMFwubm9ucmVjZWl2ZWRtYWlsXC5jb20vY2xpY2tcLnBocA==" />
      <imtext value="bWFrZW1leW91cmxvdmVyXC5jb20vcHVzc3lcLnBocA==" />
    </regexp>
  </block>
</config>
</Policy>
</Policies>
USR 6 SSO S MBI_KEY_OLD n1DPXrGuiTp6hWDiJNCQkLI1weGA2TJl27DlzyApfPhq2TvV0frz0v+tfbpZFHQH
[/code]這就是跟 MSN 服務器初步溝通的完整過程,整個連線到這裡是完整的一次連線。連線之後要做甚麼呢?上述從 MSN 服務器拿回來的資料,其實就是跟帳號有關的資訊,不過需要解碼而已。

其實,無論你要做甚麼樣的動作,其實都得透過 Command 來達成,只有某些特定的資料取得,是需要透過 SOAP 來抓取,詳細的資料可以參考這裡:
  1. http://msnpiki.msnfanatic.com/index.php/MSNP13:Contact_List
  2. http://msnpiki.msnfanatic.com/index.php/MSNP13:Contact_Membership
  3. http://msnpiki.msnfanatic.com/index.php/MSNP13:Contact_Updates
  4. http://msnpiki.msnfanatic.com/index.php/MSNP13:Offline_IM
  5. http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
這些較為特殊的 SOAP 是需要比較特別的方式去處理,不然其他的動作其實都是透過 Command 來完成的。以上,就是 MSN 服務連線的粗淺介紹。

Posted by hina

2009/07/02 14:28 2009/07/02 14:28

網路上有現成的 class 可以使用,請參閱:phpmsnclass V1.8,這是他的 Google Code。現成的類別很方便使用,所以在這裡就不多說關於類別的事情,我這邊從 protocol 開始,也許有人會覺得繁瑣,不過,就當作是我自己筆記用的好了,關於 MSN protocol 可以參考這裡:http://msnpiki.msnfanatic.com/index.php/Main_Page

首先,我們先從 SSO(Single Sign-On) 說起。我們利用 php 的 curl 函式,將我們所要做的認證送給 MSN 伺服器。關於 SSO 所需要傳送的 XML 規格,可以參考這裡:http://msnpiki.msnfanatic.com/index.php/MSNP15:SSO 由 SSO 的狀況我們可以知道,總共有 RST0 ~ RST7 等八種狀態的 Ticket Token,分別是:
  1. RST0:Passport.net 認證。
  2. RST1:Messenger Live 認證。
  3. RST2:MSN Messenger 認證。
  4. RST3:MSN 聯絡人清單認證。
  5. RST4:Messenger Live OIM 離線訊息認證。
  6. RST5:Live Spaces 認證。
  7. RST6:Live 聯絡人清單認證。
  8. RST7:Live Storage 認證。
以上這個順序是依照 SSO 的清單所列出來的,這些順序資料跟現成的 Class 並不太相同,當然,順序是可以自己修改的,所以也不一定是這種序列,這裡只是提出來說明,關於 SSO 可以傳送並取得的 token 有這些。至於傳送的方式,我用第一個 Passport.net 認證舉例,他是這麼寫的:
[code php]<?php
    $userid = ''; // 你的帳號
    $password = ''; // 你的密碼

    $XML = '<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
          xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
          xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
          xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
          xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
          xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
          xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
          xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
<Header>
  <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
    <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
    <ps:BinaryVersion>4</ps:BinaryVersion>
    <ps:UIVersion>1</ps:UIVersion>
    <ps:Cookies></ps:Cookies>
    <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
  </ps:AuthInfo>
  <wsse:Security>
    <wsse:UsernameToken Id="user">
      <wsse:Username>'.$userid.'</wsse:Username>
      <wsse:Password>'.$password.'</wsse:Password>
    </wsse:UsernameToken>
  </wsse:Security>
</Header>
<Body>
  <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
    <wst:RequestSecurityToken Id="RST0">
      <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
      <wsp:AppliesTo>
        <wsa:EndpointReference>
          <wsa:Address>http://Passport.NET/tb</wsa:Address>
        </wsa:EndpointReference>
      </wsp:AppliesTo>
    </wst:RequestSecurityToken>
  </ps:RequestMultipleSecurityTokens>
</Body>
</Envelope>';
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'https://login.live.com/RST.srf');
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
    $data = curl_exec($curl);
    curl_close($curl);
    $fp = fopen('./test.log', 'a+');
    if ($fp) {
        fputs($fp, $data);
        fclose($fp);
    }
?>
[/code]直接運行之後,他會產生一個叫做 test.log 的檔案,那個檔案就是 MSN 伺服器所返回的資料。至於他會返回些甚麼呢?我這邊舉兩個例子,一個是返回認證失敗的內容,另一個則是返回認證成功的內容。
[code xml]
<?xml version="1.0" encoding="utf-8" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<S:Header>
<psf:pp xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<psf:serverVersion>1</psf:serverVersion>
<psf:authstate>0x80048800</psf:authstate>
<psf:reqstatus>0x80048820</psf:reqstatus>
<psf:serverInfo Path="Live1" RollingUpgradeState="ExclusiveNew" LocVersion="0" ServerTime="2009-07-02T02:28:07Z">BAYIDSLGN1K12 2009.05.08.01.28.02</psf:serverInfo>
<psf:cookies/>
<psf:response/>
</psf:pp>
</S:Header>
<S:Fault>
<faultcode>S:Client</faultcode>
<faultstring>Invalid Request</faultstring>
</S:Fault>
</S:Envelope>[/code]這是返回失敗的例子,注意到他有 <faultcode> 的返回項目,代表著你傳送的資料有錯誤。
[code xml]
<?xml version="1.0" encoding="utf-8" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<psf:pp xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<psf:serverVersion>1</psf:serverVersion>
<psf:PUID>0003BFFD93BB0005</psf:PUID>
<psf:configVersion>6.0.11409.0</psf:configVersion>
<psf:uiVersion>3.100.2179.0</psf:uiVersion>
<psf:authstate>0x48803</psf:authstate>
<psf:reqstatus>0x0</psf:reqstatus>
<psf:serverInfo Path="Live1" RollingUpgradeState="ExclusiveNew" LocVersion="0" ServerTime="2009-07-02T02:28:51Z">BAYIDSLGN1S18 2009.05.08.01.28.02</psf:serverInfo>
<psf:cookies/>
<psf:browserCookies>
<psf:browserCookie Name="MH" URL="http://www.msn.com">MH=MSFT; path=/; domain=.msn.com; expires=Wed, 30-Dec-2037 16:00:00 GMT</psf:browserCookie>
<psf:browserCookie Name="MHW" URL="http://www.msn.com">MHW=; path=/; domain=.msn.com; expires=Thu, 30-Oct-1980 16:00:00 GMT</psf:browserCookie>
<psf:browserCookie Name="MH" URL="http://www.live.com">MH=MSFT; path=/; domain=.live.com; expires=Wed, 30-Dec-2037 16:00:00 GMT</psf:browserCookie>
<psf:browserCookie Name="MHW" URL="http://www.live.com">MHW=; path=/; domain=.live.com; expires=Thu, 30-Oct-1980 16:00:00 GMT</psf:browserCookie>
</psf:browserCookies>
<psf:credProperties>
<psf:credProperty Name="MainBrandID">MSFT</psf:credProperty>
<psf:credProperty Name="BrandIDList">
</psf:credProperty>
<psf:credProperty Name="IsWinLiveUser">false</psf:credProperty>
<psf:credProperty Name="CID">e75f7fe32aea2838</psf:credProperty>
<psf:credProperty Name="AuthMembername">bot1@hinablue.me</psf:credProperty>
</psf:credProperties>
<psf:extProperties>
<psf:extProperty Name="ANON" Expiry="Mon, 18-Jan-2010 10:28:51 GMT" Domains="bing.com;atdmt.com" IgnoreRememberMe="false">A=AC09B2815155B5C33BA3E160FFFFFFFF&amp;E=88d&amp;W=1</psf:extProperty>
<psf:extProperty Name="NAP" Expiry="Sat, 10-Oct-2009 09:28:51 GMT" Domains="bing.com;atdmt.com" IgnoreRememberMe="false">V=1.9&amp;E=833&amp;C=iruWWhJxoXnSMAY9Xbla_KEevr9BKN2iBGAXwaBzpLkBfOohtiiAYg&amp;W=1</psf:extProperty>
<psf:extProperty Name="LastUsedCredType">1</psf:extProperty>
</psf:extProperties>
<psf:response/>
</psf:pp>
</S:Header>
<S:Body>
<wst:RequestSecurityTokenResponseCollection xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy" xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<wst:RequestSecurityTokenResponse>
<wst:TokenType>urn:passport:legacy</wst:TokenType>
<wsp:AppliesTo xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">
<wsa:EndpointReference>
<wsa:Address>http://Passport.NET/tb</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<wst:LifeTime>
<wsu:Created>2009-07-02T02:28:51Z</wsu:Created>
<wsu:Expires>2009-07-03T02:28:51Z</wsu:Expires>
</wst:LifeTime>
<wst:RequestedSecurityToken>
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#" Id="BinaryDAToken0" Type="http://www.w3.org/2001/04/xmlenc#Element">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc">
</EncryptionMethod>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyName>http://Passport.NET/STS</ds:KeyName>
</ds:KeyInfo>
<CipherData>
<CipherValue>ATcwZCPTlRTD4abiB7sCiS/9oQTlN+Zd3Z1X/Umfr42gJ6PPSiDP7CBqvDMIB+rfUeENFM2ksp03CojxKV2HH1UrLpqhA1D5rgnEFhNZWQxRpoWlFUo21FMzP2j01w12aFPrvvIJrex7E+BKdWoKEymOeGctKYS0xf6bd6JzCDvtBtEeUd5GQatYCRXrhRU7214/udW0yw+y+Mx6VnRdAYoPAm+jap8HsMbTEQB7u3X6Odcz2wuZB6/8GByvbqLaOwR0jcYNyOiMvUeXZYa6A3b19xFHfQGsNYARVuYkSp748NVwMloBsnWElRW3ynvujzWMTeBPRsCxW+oYCfrCrslR+t9d/Di8vmW5AbAnmLQni0reLW/qmPZwY8u5FuvWmIedIUA7Hs7Yf3Z6dNOHjypPBl5Ml7AJxjCqDbZraUqEaUMiemjw+JQ/W/4JpMsZ3pPhYcQDJRv9sjDaZFcFwQbZYmnoCKebpmiZetkJthAmaTahOezHBl13NHsz</CipherValue>
</CipherData>
</EncryptedData>
</wst:RequestedSecurityToken>
<wst:RequestedTokenReference>
<wsse:KeyIdentifier ValueType="urn:passport">
</wsse:KeyIdentifier>
<wsse:Reference URI="#BinaryDAToken0">
</wsse:Reference>
</wst:RequestedTokenReference>
<wst:RequestedProofToken>
<wst:BinarySecret>mnJ+1QLiTC4SLDgLZI5bz+J6qZbwTBlr</wst:BinarySecret>
</wst:RequestedProofToken>
</wst:RequestSecurityTokenResponse>
</wst:RequestSecurityTokenResponseCollection>
</S:Body>
</S:Envelope>[/code]這是返回成功的例子,留意,它返回了 <wst:BinarySecret> 的部分,就是我們所需要的 Token 資料。另外,並不是每一種返回都會是 <wst:BinarySecret>,而是 Passport 比較特別,其餘的 protocol 都是返回 <wsse\:BinarySecurityToken ...> 這種形式,所以在取用上會略有不同。

所以當我們依照上述的順序來傳送所有的資料,並取回 token 的時候,利用正規表示式將這些 Token 分別取出來,那麼他們的順序如下:
  1. Passport.net / MSN Messanger 認證。
  2. Passport.net / MSN Messanger 認證(Binary Secret)。
  3. MSN Messanger 認證。
  4. Messanger 聯絡人認證。
  5. MSN Messanger OIM 離線訊息認證。
  6. Live Spaces 認證。
  7. Live 聯絡人清單認證。
  8. Live Storage 認證。
以上,是對於 SSO 的簡單介紹。下一篇我將簡單介紹一下如何與 MSN 伺服器做連線溝通。

附註:還有更強大的 .net 套件,請拜:http://code.google.com/p/msnp-sharp/

Posted by hina

2009/07/02 11:27 2009/07/02 11:27

[jQuery tech.] paging flip with jQuery

update, fixed IE 8 Sucks!

問題點:jQuery 1.3.2 的核心 1061 行,關於 attribute 的設定。

在 DOM 中,倘若有一個很正常的 Tag,若是這個 Tag 所擁有的 attribute 不存在,或是 CSS 設定的屬性質不存在,或是返回 NaN,則在 IE 中會出現"引數不正確"的錯誤訊息,特別是在 CSS 的設定中,並不容易察覺。
[code css]
div.classA {
margin: 0 auto;
padding: 2px 2px;
}
[/code]這是一個 classA 的 CSS 設定,倘若我使用 parseInt($(".classA").css("margin-left")); 的話,在 IE 瀏覽器中就會返回 NaN 這樣的訊息,而 Firefox 瀏覽器則返回 0 這個數字,而當返回 NaN 之後,往後有使用到這個數值的程序便會出錯。

看來,jQuery 在存取 CSS 上面,還是存在著不同瀏覽器返回不同結果的問題啊。

更新,本來想寫成 prototype 但是想想還是算了。使用方法就是這樣而已:
[code javascript]
$(document).ready( function() {
    pagingFlipInit({width: 400, height: 300});
});
[/code]可以指定要翻頁的寬度跟高度。至於 CSS 設定除了 paging-flip-box 與 masker 相關的設定之外,大多都可以修改,不過改壞了我一概不負責。基本上你可以修改 paging-flip-box 的 width 設定,這項設定是可以依據你的整個 container 的寬度來做修改,或是跟 container 一樣(通常會小個 10px)。除了 paging-flip-box 的 width 設定之外,在 CSS 檔案中只有 paging-flip-box 這行以上可以修改,其他的倘若改壞了我一概不負責。

不要問我為什麼!有興趣的人可以自己研究。畫面大概是這樣:


檔案在這裡:
[##_1C|4631416616.zip|style="width: 90px; height: 30px; border: 2px outset #796; background-color: #efd; background-repeat: no-repeat; background-position: center center; background-image: url('/image/extension/unknown.gif')"|_##]

Posted by hina

2009/06/25 22:27 2009/06/25 22:27
, , , , , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/776

開始之前,底下是參考文件:
如果你看完了,覺得可以理解,那麼這一篇文章你就可以跳過了 幹得好

CSS 3 的 Template Layout Module 特異功能!

雖然說 CSS3 跟 HTML5 一樣還沒正式發表,發表時間也是遙遙無期,不過,當我看完 Template Layout Module 之後,我深深的覺得,所謂的網頁設計這個工作,真的在 CSS3 與 HTML5 發表之後,會產生甚麼樣的變化呢?我想起之前工作時,跟大陸的工程師聊過的一些事情。在大陸做網路這一塊,分了好多種專門的職位,當然,對台灣的老闆市場來說,這是不合成本的做法。不過,所謂術業有專攻,我還是覺得,我比較羨慕大陸那樣各司其職的做法就是。

如果有看過上面的 CSS Template layout module demo,那麼我們繼續看下去。

CSS3 在這個模組裡,重新定義了 display 這個屬性,並且新增了 ::slot 這個擬似元件(pseudo-elements),另外增加了一個專用的單位"gr"。暫且先不提這兩個新增的屬性值跟單位,我們先來看看被改寫的 display 有了甚麼樣的特異功能。
  1. 可定義行、列、內文區域位置。
  2. 可定義每一行、列寬度,最大、最小寬度(高度)。
  3. 可用萬用字元(*)自動取得寬度(高度)。
  4. 可用點(.)插入一個區隔行、列的空白區塊。
另外,新的擬似類別 ::slot 與新的單位 gr 能做什麼呢?
  1. 可用 ::slot 直接指定每一區塊的各別的 Styling 設定。
  2. 在已經定義的 display 中,gr 代表了行與列的 grid line 位置,元件可以利用 n x m 的 grid-line 來定義新的位置,這個單位在目前的 jQuery plugin 並沒有被實作出來。
我們繼續回到 display 這個屬性,我這裡直接使用範例來說明。
[code css]
body {
    margin: 0;
    padding: 0;   
    display:    ".hhh." /10em
                ".nnn." /5em
                ".ocp."
                ".fff."
    * 100px minmax(500px,800px) 12em *
    ;
    width: 100%;
    background: #DDF;  
}
[/code]以上就是 CSS3 的寫法,他做了甚麼事情呢,我們繼續看下去。
  • ".hhh." /10em
    "空白 名為 h 的區塊 名為 h 的區塊 名為 h 的區塊 空白" /高度限定 10em
  • ".nnn." /5em
    "空白 名為 n 的區塊 名為 n 的區塊 名為 n 的區塊 空白" /高度限定 5em
  • ".ocp."
    "空白 名為 o 的區塊 名為 c 的區塊 名為 p 的區塊 空白"
  • ".fff."
    "空白 名為 f 的區塊 名為 f 的區塊 名為 f 的區塊 空白"
眼尖的人或許發現了,這就跟畫表格是一樣的道理。我用表格畫出了一個田字型的區域,然後把需要的內容擺進去,這就是這個新定義的 display 屬性在做的事情。那你或許會有疑問:
  1. 為什麼 ".ocp." 跟 ".fff." 沒有限制高度?
    因為這兩個區塊是依照內容變動長度,所以一旦限定高度,內容並不會被截斷,而是直接破格。
  2. 高度設定可不可以跳過 ".ocp.",直接指定給 ".fff."?
    可以,但是目前這個 jQuery plugin 會識別錯誤,不過可以在最後加入 "." 不設定高度避開。
  3. 已經定義好的區塊可否改變位置?
    可以,使用擬似元素 ::slot 來做其他 CSS Styling 設定。
  4. 區塊是否可以跨行?
    不行,CSS 會無法得知正確位置而產生顯示錯誤(但邏輯上不會有錯)。
指定好了區塊之後,接著我們要將 HTML 文件中的元素"放到"我們的位置上。
[code css]
#header { position: h; text-align: center; }

#content {
    position: c;
    margin: .5em;
}

#nav1 {
    position: n;
    background: #AAD;
    margin: 0;
    padding: .5em;
}

#nav2 { position: o }

#nav3 {
    position: p;
    text-align: center;
}

#footer {
    position: f;
    text-align: center;
    font-weight: bold;
}
[/code]利用新規範的 position 屬性,我們可以將元件的位置給"指定"到 display 的設定中。

那如果我要改某個區塊的設定的時候怎麼辦?這時候就使用新的擬似元件 ::slot 來指定。
[code css]
body::slot(o) {
    background: #FDD;
}
body::slot(p) {
    background: #DFD;
    vertical-align: middle;
}
[/code]如果要改變位置?做法也是一樣的,利用 ::slot 來對指定的區塊做 position 的設定即可。

如此一來,我們的 HTML 檔案完全不需要做複雜的排版,只要輸出時確認確切的位置,其他的全部都可以交給 CSS3 的 Template Layout Module 來做。當然,好的視覺設計不可少,對於這樣來做 layout 的方式,設計輸出可能就必須要更精準且更能夠掌控自己所切出來的區塊與元素,否則,這樣的工具再怎麼強大也是白搭。



關於 CSS3 的部分,畢竟還在起草階段,就不說太多了。這個 Template Layout module 剛好有人寫了 jQuery plugin,所以算是可以小小應用的部分,至於好不好用就端看個人了。我覺得拿來做一些惡趣味應用應該是不錯的(啥)!

接著,來說說 CSS Multiple, Layed backgrounds with jQuery 這個小東西,有去過網站的應該知道他在做甚麼了。這個 jQuery plugin 的目的,就是把一大堆 background 元件給拼湊起來。就有點類似我之前講過的這個部分:

現在我要說,上面這個做法,還有另一個神兵:jQuery

這個 jQuery plugin 可以利用 CSS 的設定完美的解決這個問題。當然,CSS3 也有 multiple Background 的新鼠性設定,不過目前能夠正常支援的瀏覽器時在少得可憐,也許利用這個 jQuery plugin 也不失為一個好辦法。不然,這樣的元件切割,最少要使用 9 個 DIV 來切割,複雜一點的恐怕就更麻煩,也徒增 HTML code 的麻煩與困擾(而且困擾的都是工程師)。這個 plugin 應該可以包成比較方便使用的模組(應該說對自己比較方便的模組吧),改天在上來分享(如果我真的有去改的話)。

就降,散會!

Posted by hina

2009/06/11 15:40 2009/06/11 15:40

[PHP tech.] PHP MSN Bot 筆記

首先,需要用到的類別可以在這裡找到:
我的作業系統是 FreeBSD,PHP 版本是 5,Apache 是 2.x。安裝的方式很簡單,就如同他的 readme 說的一樣。不過,他預設將檔案放在 /var/spool/msnbot 底下,如果你要換地方放的話,可能要自己修改一下 msnbot.php 這支檔案,否則跑起來可能會有點問題。另外,他所寫的 msnbot.sh 在 BSD 底下好像有點問題,不過我是自己做了一支簡單的 shell-script 來去啟動,所以沒差。
有一個要注意的地方是,它的 php Extensions 最好有安裝下列:
  • curl
  • pcre
  • pcntl
  • mhash(支援 MSNP15)
  • mcrypt(支援 MSNP15)
  • bcmath(支援 MSNP15)
在我的測試中,我發現支援 MSNP15 會比較穩定一點,所以如果有需要的話,那些 extensions 還是裝起來,讓它單純的跑在 MSNP15 底下會比較穩定(預設是自動決定要跑 MSNP9 或是 MSNP15)。當然,這些 php 全部都要在命令提示視窗下執行,所以 Apache 記得要裝 CLI 的模組,這個服務需要用到 1863 port 別忘記打開(如果你防火牆有擋住的話)。另外,MSN 的認證需要用到 443 port 也別忘記開(一般都會開吧)。
下載回來都設定好之後,我們需要一組 MSN 的帳號來做登入動作。通常,我們不是申請 XXX@hotmail.com 不然就是 OOO@msn.com,這樣感覺挺不蘇胡的(啥?)。所以,我們可以透過 MSN Live 的 .net Passport regisration 來註冊一個帳號。這個帳號是用來通過 .net passport 的認證的,我們申請完之後,可以改掉它。
  1. 首先,先填好申請資料。
  2. 確認 email 地址,就把左邊的 email 地址再打一次就好。
  3. 用剛申請好的 passport email 與密碼登入,選擇"Credentials"。
  4. 選擇修改 email address。
  5. 填寫你要改的 email address,其他資料一併填妥。
  6. 送出後它會將你登出,之後你就可以用你修改過的 email address>$2 跟原本的密碼>$2登入。
這樣,你已經準備好 MSNBot 的帳號了,接著下來先測試一下。你下載的檔案中,有一個檔案叫做 sample.php,打開並編輯。
[code php]
#!/usr/bin/php -Cq
<?php

error_reporting(E_ALL);
include_once('msn.class.php');

// force to use MSNP9, without debug information
// $msn = new MSN('MSNP9');

// force to use MSNP9, with debug information
// $msn = new MSN('MSNP9', true);

// force to use MSNP15, without debug information
// $msn = new MSN('MSNP15');

// force to use MSNP15, with debug information
// $msn = new MSN('MSNP15', true);

// auto detect MSN protocol, without debug information
// $msn = new MSN;

// auto detect MSN protocol, with debug information
// 我們這裡先不強制使用 MSNP15,先做測試
$msn = new MSN('', true);

if (!$msn->connect('填入你剛剛修改好的 email 帳號', '填入你的密碼')) {
    echo "Error for connect to MSN network\n";
    echo "$msn->error\n";
    exit;
}

$msn->sendMessage('Now: '.strftime('%D %T')."\nTesting\nSecond Line\n\n\n\nand Empty Line",
                  array(
                    '填入你自己的 MSN 帳號'
                       )
                 );
echo "Done!\n";
exit;

?>[/code]檔案修改好之後存檔,接著直接測試。
[code]
>php ./sample.php
[/code]如果執行有錯誤,請到 log/ 底下去查看 debug.log。
在 MSN 收到的畫面如下:
這個類別所產生出來的機器人,基本上你跟它說什麼,它會還給你一模一樣的訊息。關於這一點,可能就得自己去修改了,至於跟資料庫串接的部分,壓縮檔中有很簡易的範例可以參考。當然,我個人覺得是直接修改掉 msnbot.php 這支檔案,來做到符合自己需求的機器人會比較快一點。不過大抵上,這已經算可用度頗高的工具了,剩下的部分除了程式上,就只缺創意了。

Posted by hina

2009/06/08 14:10 2009/06/08 14:10

[聽說] 今天是工程師節

感謝 Neo 提供圖片!


所以,我要靠北紀念一下!

Posted by hina

2009/06/06 18:04 2009/06/06 18:04
, , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://hina.ushiisland.net/blog/rss/response/766

« Previous : 1 : 2 : 3 : 4 : 5 : ... 9 : Next »

블로그 이미지

- DearJohn

Tag Cloud

Archives

Authors

Recent Posts

Recent Comments

Recent Trackbacks

Calendar

«   2010/03   »
  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      

Site Stats

Total hits:
529304
Today:
0
Yesterday:
0