2014-02-21 17:43

[T4] url, base64, sprite 三種格式的 icons.css 產生器

之前有寫過 [PHP] url, base64, sprite 三種格式的 icons.css 產生器,這次換用 C# T4 Text Templates 來製作這個功能:

  1. int spriteGap = 30;   
  2. var scanTypes = new string[]{".jpg", ".gif", ".png"}; 
  3.  
  4. string workDirectory =  
  5.    Path.GetDirectoryName(this.Host.TemplateFile); 
  6.  
  7.  
  8. /* 取得圖檔資訊 */   
  9. List<ImageInfo> imageList = Directory 
  10.    .EnumerateFiles(Path.Combine(workDirectory, "icons")) 
  11.    .Where(path => scanTypes.Contains(Path.GetExtension(path))) 
  12.    .Select(path =>  
  13.    { 
  14.        var image = Image.FromFile(path); 
  15.  
  16.        return new ImageInfo{ 
  17.            FilePath = path, 
  18.            FileName = Path.GetFileName(path), 
  19.            IconImage = image, 
  20.            IconName = Path.GetFileNameWithoutExtension(path), 
  21.            TopOffset = 0, 
  22.            Width = image.Width, 
  23.            Height = image.Height, 
  24.            IsAnimated = ImageAnimator.CanAnimate(image), 
  25.        }; 
  26.    }) 
  27.    .OrderBy(info => info.IsAnimated) 
  28.    .ToList(); 
  29.  
  30.  
  31.  
  32. /*檢查重複的圖檔名稱*/ 
  33. List<string> repeatList = imageList.GroupBy(info => info.IconName) 
  34.    .Where(g => g.Count() > 1) 
  35.    .SelectMany(g => g.Select(x => x.FileName)) 
  36.    .ToList(); 
  37.  
  38. if(repeatList.Count > 0){ 
  39.    throw new Exception( 
  40.        "出現重複的圖檔名稱[" + String.Join(", ", repeatList) + "]" 
  41.    ); 
  42. }    
  43.  
  44.  
  45.  
  46. /* 製作 CSS Sprite */ 
  47. int sumHeight = imageList.Sum( info => info.Height); 
  48. int spriteWidth = imageList.Max( info => info.Width); 
  49. int spriteHeight = sumHeight + (imageList.Count - 1) * spriteGap; 
  50.  
  51. var bitmap = new Bitmap(spriteWidth, spriteHeight); 
  52.  
  53. using (Graphics graphics = Graphics.FromImage(bitmap)) 
  54. {            
  55.    int nextTop = 0; 
  56.    for(int i=0; i< imageList.Count; i++){ 
  57.        /*忽略具有動畫的 GIF 圖片*/  
  58.        if(imageList[i].IsAnimated){ continue; }  
  59.  
  60.        imageList[i].TopOffset = nextTop; 
  61.  
  62.        graphics.DrawImage(imageList[i].IconImage, 0, nextTop); 
  63.  
  64.        nextTop = nextTop + imageList[i].Height + spriteGap; 
  65.    } 
  66.  
  67.    graphics.Save(); 
  68. } 
  69.  
  70. SaveOutput("icons.sprite.png");  
  71. bitmap.Save( 
  72.    Path.Combine(workDirectory, "icons.sprite.png"),  
  73.    ImageFormat.Png 
  74. ); 
  75. bitmap.Dispose(); 

下載完整程式: t4_make_icons_css.zip
2014-02-18 22:46

[jQuery] $.log 與 $.fn.dump

記錄一下,偷懶的 console.log
  1. if ($ && $.fn) { 
  2.    $.log = (window['console'] && typeof(console.log)=='function') ? 
  3.        function () { console.log.apply(console, arguments); } : 
  4.        $.noop; 
  5.  
  6.    $.fn.dump = function (tag) { 
  7.        var args = Array.prototype.slice.call(arguments); 
  8.        args.push(this); 
  9.  
  10.        $.log.apply($, args); 
  11.        return this; 
  12.    } 
  13. } 
  14.  
  15.  
  16. // use 
  17. $.log('ok'); 
  18.  
  19. $('img').dump(); 
  20.  
  21. $('div').dump('my tag'); 
2014-02-18 22:26

[jQuery] 製作 $.fn.delayAll

jQuery 有提供 delay 這個方法,可惜只能用在動畫操作上,想要做到下面的事情就只能用 setTimeout 了。
  1. // not working 
  2. $('div').css('color','red').delay(200).css('color','blue'); 
  3.  
  4.  
  5. // use setTimeout 
  6. $('div').css('color','red'); 
  7.  
  8. setTimeout(function(){ 
  9.    $('div').css('color','blue'); 
  10. }, 200); 


稍微研究了一下,發現 jQuery 本身有製作 queue 的功能,這個用來存放 delay 後要執行動作的紀錄器,最後只要對 jQuery Object 作一個代理器的包裝,就可以達到想要的目的了。
  1. var queueName = 'delayAll'; 
  2.  
  3. /*定義 jQuery Delay 代理類別*/ 
  4. function jQueryDelay(jqObject, duration){ 
  5.    /*將缺少的成員屬性補上*/ 
  6.    for(var member in jqObject){ 
  7.        if(!$.isFunction(jqObject[member]) || !this[member]){ 
  8.            this[member] = jqObject[member]; 
  9.        } 
  10.    } 
  11.  
  12.    /*新增 delay 時間並啟動 queue*/ 
  13.    jqObject 
  14.        .delay(duration, queueName) 
  15.        .dequeue(queueName); 
  16.  
  17.    /*紀錄 jQuery 物件*/ 
  18.    this._jqObject = jqObject; 
  19. }; 
  20.  
  21.  
  22. /*為所有的 jQuery 方法製作 proxy*/ 
  23. for(var member in $.fn){ 
  24.    if(!$.isFunction($.fn[member])){ continue; } 
  25.  
  26.    jQueryDelay.prototype[member] = function(){ 
  27.        var jqObject = this._jqObject; 
  28.        var args = Array.prototype.slice.call(arguments); 
  29.        var mothed = arguments.callee.methodName; 
  30.  
  31.        /*將需要執行動作加入 queue*/ 
  32.        jqObject.queue(queueName, function(next) { 
  33.            jqObject[mothed].apply(jqObject, args); 
  34.            next(); 
  35.        }); 
  36.  
  37.        return this; 
  38.    }; 
  39.  
  40.    /*紀錄方法名稱,在 proxy 時會需要參考到*/ 
  41.    jQueryDelay.prototype[member].methodName = member; 
  42. } 
  43.  
  44.  
  45. /*允許多次串接的可能*/ 
  46. jQueryDelay.prototype.delayAll = function(duration){ 
  47.    this._jqObject.delay(duration, queueName); 
  48.    return this; 
  49. }; 
  50.  
  51.  
  52. /*用 jQueryDelay 將原本的 jQuery Object 包裝起來*/ 
  53. $.fn.delayAll = function(duration){ 
  54.    return new jQueryDelay(this ,duration); 
  55. }; 


使用範例:
  1. $('div').css('color','#f00') 
  2.    .delayAll(2000).css('color','#0f0') 
  3.    .delayAll(2000).css('color','#00f'); 


檔案下載:jquery.delayAll.js
2014-02-15 14:48

[C#] 取得 Domain 的 IP 列表

  1. //using System.Net; 
  2. IPAddress[] ipList = Dns.GetHostAddresses("www.google.com"); 


來源:
Dns.GetHostAddresses 方法 (System.Net)

2014-02-15 14:45

[C#] delegate 到 Lambda Expressions 語法演進

一開始要看懂 Lambda Expressions 有點困難,下面會以演進方式來介紹如何做到語法省略。

首先定義一個單參數的 delegate
  1. delegate int Del(int x); 

以傳統 delegate 的語法來建構 delegate
  1. Del a = delegate(int x) { return x + 2; }; 

去掉 delegate 改成 Lambda 表示式
  1. Del a = (int x) => { return x + 2; }; 

由於大括號裡只有一句陳述式,而且是一個 return 的陳述式,所以可以省略大括號跟 return
  1. Del a = (int x) => x + 2; 

在 delegate 已經有定義輸入參數的型別,所以在小括號裡的型別可以省略
  1. Del a = (x) => x + 2; 

由於小括號裡面只有一個輸入參數,所以可以再進一步省略小括號
  1. Del a = x => x + 2; 

參考來源:
Lambda 運算式 (C# 程式設計手冊)
2014-02-13 23:54

HTTP GET 與 POST 的比較及使用時機

  GET POST
瀏覽器歷史紀錄 參數都會紀錄,因為都是URL的一部分 參數都不會紀錄
加入書籤 參數都會紀錄,因為都是URL的一部分 參數都不會紀錄
回上一頁/
重新載入
GET請求是重新執行,但被存儲在瀏覽器的快取,則不被重新提交到服務器 數據將被重新提交(瀏覽器通常會警告使用者該數據將需要重新提交)
編碼類型 application/x-www-form-urlencoded multipart/form-data 或 application/x-www-form-urlencoded,使用多編碼的二進制數據
參數大小限制 受限於 QueryString 長度限制(不超過 2KB 是最保險的,有些瀏覽器可以允許多達 64KB) 允許大量傳輸,包括上傳文件到服務器
參數傳輸方式 QueryString POST Data(message-body)
安全性 容易破解,因為參數是網址的一部分,所以它被紀錄在瀏覽器歷史記錄和明文服務器日誌 比較難破解
使用性 不應該被使用在發送密碼或其他敏感信息上 使用在發送密碼或其他敏感信息上
能見度 GET方法是對所有人可見(它會顯示在瀏覽器的地址欄) POST方法變量不顯示在URL中
執行速度 快,GET 比 POST 快 1.5 倍 慢,POST 多出需要發送數據的步驟
快取 瀏覽器會依據網址來快取資料,不同的網址有不同的快取 瀏覽器不會快取
自動重送 會,在回應過長時會重發請求,直到重試結束 不會,一個請求發出後會一直等待回應
適用行為 檢視(Read) 新增(Create)、修改(Update)、刪除(Delete)
情況包括 透過 <link href="">、<img src="">、<script src="">、<iframe src=""> 額外載入的 JavaScritp、CSS、圖片 透過 <form method="post"> 以及 Ajax post 發送的請求


GET 適合用在「檢視(Read)」的操作行為,由於檢視資料的操作會遠比資料異動來的更頻繁,所以需要更快的回應,而且有快取可以加快二次檢視,參數在網址上可以使每一個網址都代表一個網頁,在加入書籤的連結能夠返回對應的網頁,再者所需要的參數很少(例如:id=122&type=1),對於資料異動後快取沒更新的問題,可以在 QueryString 加上資料最後修改的時間戳記(例如:id=122&type=1&t=1392183597718)。


POST 適合用在「新增(Create)修改(Update)刪除(Delete)」的操作行為,資料異動所需要傳送的參數很可能超過 QueryString 的限制,不適合用 GET 來處理資料異動的傳送,GET 在等待過久會重新發送請求,這會造成重複的請求,如果在新增儲存就多新增一筆資料,而 POST 在一個請求發出後會一直等待回應,這可以保障在傳送的過程中請求是唯一的,新增資料的請求如果被記錄在書籤或歷史紀錄中,使用者點擊連結網址就新增一筆資料,這真是一件恐怖的事,所以在資料異動上的請求必須使用 POST 來傳送。


參考來源:
GET vs POST - Difference and Comparison | Diffen
HTTP Methods: GET vs. POST
寻根究底:Ajax请求的GET与POST方式比较
2014-02-10 23:43

[C#] 對 FirstOrDefault 新的認識

FirstOrDefault 會依據最後的型別去決定 Default 時回傳的值,例如下面的範例:
  1. int? a = (new List<int>{2}).Select(x => x).FirstOrDefault(); 
  2. // 2 
  3.  
  4. int? b = (new List<int>{}).Select(x => x).FirstOrDefault(); 
  5. // 0 
  6.  
  7.  
  8. int? c = (new List<int>{2}).Select(x => (int?)x).FirstOrDefault(); 
  9. // 2 
  10.  
  11. int? d = (new List<int>{}).Select(x => (int?)x).FirstOrDefault(); 
  12. // null 
2014-02-10 23:38

[C#] 用 LINQ 將字串切割成整數陣列

  1. int id; 
  2. int[] idArray = "1,2,3,4,5".Split(',') 
  3.    .Where(idStr => Int32.TryParse(idStr, out id)) 
  4.    .Select(Int32.Parse) 
  5.    .ToArray(); 

這裡用到將 Int32.Parse 這個一般 method 指向給 delegate 的技巧。
2014-02-05 20:53

[C#] HttpUtility.ParseQueryString 的隱藏密技

在使用 Request.QueryString 發現 ToString 會產生 URL 的 query 字串,嘗試用 NameValueCollection 的 ToString 卻不是產生 URL 的 query 字串,這一整個就很奇怪,明明都是 NameValueCollection 確有不一樣的結果,透過 Reflector 發現 Request.QueryString 的 instance 型別是一個 HttpValueCollection,想說可以直接 new HttpValueCollection 出來使用,但 HttpValueCollection 卻是 System.Web 的內部 Class,外部是無法直接 new 出來使用,還好在又發現 HttpUtility.ParseQueryString 回傳的 NameValueCollection 的 instance 是 HttpValueCollection 這個型別,所以可以透過 HttpUtility.ParseQueryString 來建立 HttpValueCollection。

  1. // using System.Web; 
  2.  
  3. var qs1 = HttpUtility.ParseQueryString("id=5&type=1"); 
  4. qs1.ToString(); // "id=5&type=1" 
  5.  
  6. var qs2 = HttpUtility.ParseQueryString(String.Empty); 
  7. qs2["id"] = "11"; 
  8. qs2["name"] = "Tom";  
  9.  
  10. qs2.ToString(); // "id=11&name=Tom" 


HttpValueCollection 的簽名
  1. [Serializable]      
  2. internal class HttpValueCollection : NameValueCollection      
  3. { 
  4. } 


ParseQueryString 的簽名
  1. public static NameValueCollection ParseQueryString(string query, Encoding encoding) 
  2. { 
  3.    if (query == null) 
  4.    { 
  5.        throw new ArgumentNullException("query"); 
  6.    } 
  7.    if (encoding == null) 
  8.    { 
  9.        throw new ArgumentNullException("encoding"); 
  10.    } 
  11.    if ((query.Length > 0) && (query[0] == '?')) 
  12.    { 
  13.        query = query.Substring(1); 
  14.    } 
  15.    return new HttpValueCollection(query, false, true, encoding); 
  16. } 
2014-02-05 20:25

[CSS] float 與 clear

float

一開始是用來定義文繞圖的呈現,後來其浮動特性很適合用來排版佈局,所以大部分的網頁都用這種方式排版。

特性:
  • 不佔用父元素的空間
  • 寬高會內縮至子元素所呈現的大小
  • 會排擠其他相鄰的 inline 元素
  • 在所定位的空間不足時會自動換行

  1. <div style="border:1px solid #f00; float:left;">div 1</div> 
  2. <div style="background:#0f0;">div 2</div> 
div1
div2

透過上面的範例可以看出因為 div1 不佔用空間而讓 div2 的位子上移了,然而呈現與 div1 重疊的效果,以及可以看到 float 排擠文字的特性,而讓 div2 的文字被擠到 div1 之後。



clear

清除在元素相鄰邊上的 float 元素

屬性:
  • none 不做任何 clear 動作
  • left 將元素向下換行,來排除具有 float:left 的相鄰元素
  • right 將元素向下換行,來排除具有 float:right 的相鄰元素
  • both 將元素向下換行,來排除具有 float:left 或 float:right 的相鄰元素

  1. <div style="border:1px solid #f00; float:left;">div1</div> 
  2. <div style="background:#0f0; clear:left;">div2</div> 
div1
div2

這個範例在 div2 加上 clear:left 的屬性,讓 div2 根據前一個具有 float:left 元素之後向下換行,來排除具有 float:left 的相鄰元素。


  1. <div style="border:1px solid #f00; float:left;">div1</div> 
  2. <div style="background:#0f0; clear:right;">div2</div> 
div1
div2

這個範例則是將 div2 換成 clear:right,可以看到 div1 與 div2 仍舊重疊在一起,這是因為 div2 前一個具有 float:right 不存在,而 div1 的 float:left 不是 div2 clear:right 排除的對象。