2012年5月30日星期三

免費的ASP.NET MVC網頁空間

對上一次介紹ASP.NET的Free Hosting已經是幾年前 - (2009年1月11日 - 免費的ASP.NET網頁空間),幾年前那一番說話:( 其實Free ASP.NET Hosting真是少之有少,你在Google找遍全世界都可能找不到超過10個。 )
到今天,依然有效。

今天再度整理一下現在的免費空間,只著眼於比較主要的項目,排名不分先後 :

2012年5月29日星期二

7-Zip 換新裝 - 7-Zip Theme Manager

幾日前轉貼 - 電腦軟件《ZIP》背後原來包含一個鮮為人知的悲劇,90年代未期,eDonkey,Pub FTP興起,大多數Warez,Moviez都是Rar...r01..r02的分割檔案發放,造就WinRAR取代WinZIP,但時至今日,其實WinRAR都不好過,有很多開源/免費軟件已經慢慢侵占佔有率。

7-Zip大家當然聽過亦用過,但無奈介面實在非常嘔心,表面好像是Windows 98時代產物,而作者似乎多年來從未有想過搞一搞介面的Icon,所以有一段時間我轉用了PeaZip,但PeaZip亦過於複雜,起動時間慢,資料夾內的檔案雜亂無章,最後我還是用回7-Zip,而其中一個原因就是7-Zip終於可以靠第三方軟件去修改介面圖示或Windows檔案圖示了。

7-Zip Theme Manager
http://www.7ztm.de/

這個軟件其實是一個Patch檔,修改7-Zip的exe去替換圖示。
我已經用了幾個月,沒有什麼問題。
經修改後的7-Zip顯然有返一點時代感。
有多款圖示給你選擇,其實任何一款都一定比原版的好看,所以用7-Zip的人可以試用下。


2012年5月22日星期二

WordPress to Blogger 搬家全攻略 Part.2

繼續上文 - WordPress to Blogger 搬家全攻略 Part.1
這一次就輕鬆簡單得多了,文字雖然一樣多,但其實只需要三步曲,而且沒有Part .1般需要太多人手。

WordPress to Blogger 搬家全攻略 Part.1

用了Blogger都差不多快一個多月,而Host WordPress網存那邊今晚就是到期日,如無意外幾日後應該看不到我的舊Blog。
之前說過會公開我搬家過程和工具,趁現在記憶還清晰,寫一下,內容比較長。

2012年5月20日星期日

電腦軟件《ZIP》背後原來包含一個鮮為人知的悲劇

ZIP 背後是一個沒落天才的故事,Phil Katz 不願意為一個壓縮軟件付錢,因此索性自己編寫一個更好的算法,然後免費公開,但他於 2000 年 4 月 14 日被發現死於一家汽車旅館,年僅 37 歲,死時手中握著一個空酒瓶……

一堆免費的BBCode Web Editor

作為高登巴打一份子,經常都會轉載網上的文字,純文字的話還好,如果連帶多張圖片的話,不可能一張一張去補上BBCode的[IMG] Tag。

我之前的習慣就會利用HotEditor - HTML網頁轉換至Forum BBCode工具,但HotEditor的官網Demo有時候很慢,所以上網看看,有沒有其他選擇,最後找到4個不錯的。

很有名的TinyMCE w/ BBCode Plugin
http://www.tinymce.com/tryit/bbcode.php


跟上者同樣出名的CKEditor w/ BBCode Plugin
http://nightly.ckeditor.com/latest/ckeditor/_samples/bbcode.html


再來兩個小品:
SCEditor – A WYSIWYG HTML & BBCode editor
http://www.samclarke.com/2011/07/sceditor/
Demo


WYSIWYG BBCode Editor
http://wysiwygbbcode.codeplex.com/

2012年5月15日星期二

Blogger Recent Posts by jQuery / Ajax / JSONP

之前已經貼過的Recent Comments,當然不會少得Recent Posts。

Demo : http://webapp.heliohost.org/html/blogger_recent_posts_json.htm

使用方法都是一樣很簡單 :
如果你的Template並未加入jQuery,請去設計 / 修改 HTML / 修改範本中,把以下程式碼放在<head>與</head>之間。
<script 
type="text/javascript" 
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
</script>

然後在Blogger新增一個HTML/Javascript小工具,然後把下面的程式碼貼上後,再儲存就完成。


大家使用前,需要去調整一下設定 :
maxcount (line 18) - 最多文章顯示數目。
maxlength (line 19) - 長度多於這個值時,用"..."代替。
feedURL (line 48) - 換上你blogger的ID。

樣式方面,大家可以自行去修改。
  <script type="text/javascript">
   var BloggerFunctions = {
    getPosts : function(feedURL) {
     var paras = {
      alt : 'json-in-script'
     };
     $.ajax({
      url : feedURL,
      type : 'GET',
      dataType : "jsonp",
      success : BloggerFunctions.onGotPostData,
      data : paras
     });
    },

    //Parse the JSON post data returned by the Google Blogger API
    onGotPostData : function(data) {
     var maxcount = 10;
     var maxlength = 20;

     var feed = data.feed;
     var entries = feed.entry || [];
     var title = "";
     var content = "";
     var url = "";
     var published = "";
     var cnt = 0;
     var html = "";

     for(var i = 0; i < entries.length; ++i) {
      var entry = entries[i];
      var title = entry.title.$t;
      //var content = entry.content.$t;
      var url = entry.link[4].href;
      var published = entry.published.$t.substring(0,10);

      html += "<a href='" + url +"'><span style='font-family:Arial;color:#333;font-size:11px;font-weight:bold;'>" + "[" + published + "] " + (title.length > maxlength ? title.substring(0, maxlength) + '...' : title)  + " </span></a><br />";
      cnt++;
      if(cnt == maxcount)
       break;
     }
     var dom = $(html);
     $('#recent_posts').append(dom);
    }
   }

   $(document).ready(function() {
    var feedURL = "http://tatmingstudio.blogspot.com/feeds/posts/default";
    BloggerFunctions.getPosts(feedURL);
   });
  </script>
  <div id="recent_posts"></div>

2012年5月14日星期一

Aptana Studio 3 安裝 PHP xDebug

自從Aptana Studio 3重新加入PHP開發功能後,我幾乎已經完全用它取代了Eclipse PDT。
Aptana Studio 3很多地方都要比PDT或另一免費的PHP IDE - Netbeans強,但到目前為止Aptana唯一失色的地方就是沒有PHP Debugger。

幸好Aptana團隊自3.0版本開始十分積極,差不多一個月有一次更新。預計v3.2.0版本就會支援Zend Debugger 和 xDebug了。
而PHP Debugger亦隨著nightly build公開下載,貼一下安裝攻略。

首先第一樣要做,當然是更新至Nightly Build的Aptana Studio 3,或者有人擔心用Nightly會不穩定,但我親身試用了5個月Nightly Build,沒有任何問題,只是提示更新比較頻密而已。

安裝

  1. 前往Preferences,打開Install/Updates,選擇Available Software Sites
  2. Add,Name位置輸入Aptana Studio Nightly Updates,Location輸入http://preview.appcelerator.com/aptana/studio3/standalone/update/nightly/,緊記要選取CheckBox才算是完成,之後按OK退出Preferences
  3. 前往Help,按Check for updates,此時應該會見到3.2.0.xxxx版本了,之後安裝完成,重新開啟Aptana(很重要!)。
  4. 然後前往Aptana的IssueTracker Sitehttps://jira.appcelerator.org/browse/APSTUD-769,下載Attachments - com.aptana.php.debug.site.zip,把它放到Aptana安裝目錄的plugins資料夾之中。
  5. 再次前往Preferences,打開Install/Updates,選擇Available Software Sites,按Add,Name位置輸入PHP Debugger,Location按Archive按鈕,選取剛下載回來的com.aptana.php.debug.site.zip,跟之前一樣,要選取CheckBox,然後按OK退出,安裝完成,重新開啟Aptana
  6. 重新啟動後,可以打開Preferences/Aptana Studio/Editors/PHP,看看有沒有Debug選項,有的話就大功告成。

我自己是用xDebug的,其實設定上比Netbeans或其他商業IDE更要下點功夫,但都跟PDT大同小異。
至於怎樣設置xDebug,可以參考下文 : Configure Eclipse PDT / XDebug for PHP debugging

唯一要注意的是在Debug Configurations中,是否需要使用URL/Auto Generate? 又是否需要Path Mapping?
好像我的情況就要取消Auto Generate URL和自行設定Path Mapping了。

送上一些圖給大家參考一下吧。

2012年5月8日星期二

為何.NET叫.NET?


若你在Google搜尋的話,便發現很多人都有同一個疑問 - Why was .NET called .NET?

.NET這個名稱,既不Search Engine family,作為Logo/Domain中的一員,又因為那一個Dot - "."太細小或字眼問題,要轉成Dot或放大那一點。
雖然時至今日大家都叫慣叫熟,但我都覺得應該可以有更好的命名。

剛剛又看到一名國外Blogger寫有關的文章 - Why was .NET called .NET?

最多人認同的答案在StackOverFlow - Why was .NET called .NET?
.NET enabled Microsoft's marketing people to emphasise the "Network"-ing aspect of its technologies, and was also a reaction to the marketing blitz by Sun Microsystems in the late 1990s whose theme was "The network is the computer". The term "Dot-Com" was synonymous with the Internet that time, and "Dot-NET" was a play on that term.
或多或少我都同認的,90年代正值Internet發展年代,叫".NET",有點意識流,令人感覺.NET Framework不單是Offline本機的技術,而是關聯到Internet。

2012年5月5日星期六

Build Blogger Recent Comments by jQuery / Ajax / JSONP

用上Blogger後,試用一下Google提供的小工具。
使用最近留言(Recent Comments)和最近文章(Recent Posts)幾乎是Blog站的指定部件。

Google提供的小工具是由第三方 - Blogger Buster 製作的。
但可惜廢得可憐,因為是透過iframe呈現,又不能自定樣式,而且最不能接受的是連Trackback都包含在內,又不能把我自己的回應去掉。

Google搜尋,有很多都隨著Google API的更新不能用,或者是我不能理解的做法。

最後當然又是自己動手做,但出師不利,前幾天就遇到問題 - jQuery Cross-Domain Ajax w/XML = NO WAY!

所以唯有轉用JSONP方式,參考了以下文件。
幾經修改過後,製作完成了。
現在大家在右手邊的Sidebar看到的最近留言就是了。
Demo : http://webapp.heliohost.org/html/blogger_recent_comments_json.htm

使用方法很簡單,
如果你的Template並未加入jQuery,請去設計 / 修改 HTML / 修改範本中,把以下程式碼放在<head>與</head>之間。
<script 
type="text/javascript" 
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
</script>

然後在Blogger新增一個HTML/Javascript小工具,然後把下面的程式碼貼上後,再儲存就完成。


大家使用前,需要去調整一下設定 :
maxcount (line 29) - 最多留言顯示數目。
minlength (line 30) - 長度多於這個值才顯示。
maxlength (line 31) - 長度多於這個值時,用"..."代替。
exclude_name (line 32) - 過濾字。
feedURL (line 69) - 換上你blogger的ID。

樣式方面,大家可以自行去修改。
<script type="text/javascript">
   function contains(arr, obj) {
    for(var i = 0; i < arr.length; i++) {
     if(arr[i].toLowerCase() == obj.toLowerCase() && arr[i].length == obj.length)
      return true;
    }
   }
   if( typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function(str) {
     return this.indexOf(str) == 0;
    };
   }
   var BloggerFunctions = {
    getComments : function(feedURL) {
     var paras = {
      alt : 'json-in-script'
     };
     $.ajax({
      url : feedURL,
      type : 'GET',
      dataType : "jsonp",
      success : BloggerFunctions.onGotCommentData,
      data : paras
     });
    },

    //Parse the JSON comment data returned by the Google Blogger API
    onGotCommentData : function(data) {
     var maxcount = 10;
     var minlength = 10;
     var maxlength = 20;
     var exclude_name = new Array("達MiNG");

     var feed = data.feed;
     var entries = feed.entry || [];
     var title = "";
     var content = "";
     var url = "";
     var authorname = "";
     var published = "";
     var cnt = 0;
     var html = "";

     for(var i = 0; i < entries.length; ++i) {
      var entry = entries[i];
      var title = entry.title.$t;
      var content = entry.content.$t;
      var url = entry.link[2].href;
      var authorname = entry.author[0].name.$t;
      var published = entry.published.$t.substring(0,10);
      
      if(!contains(exclude_name, authorname) && !content.startsWith('[...]') && title.length >= 10) {
       html += "<span style='font-family:Arial;color:#333;font-size:11px;font-weight:bold;'>" + "["+ published + "] " 
         +authorname + " 說 : </span><br />" 
         + "<span style='font-family:Arial;color:#666;font-size:11px;'>" 
         + "<a href='" + url + "'>" + (content.length > maxlength ? content.substring(0, maxlength) + '...' : content) + "</a></span>"
         + "<br /><br />";
       cnt++;
      }
      if(cnt == maxcount)
       break;
     }
     var dom = $(html);
     $('#recent_comments').append(dom);
    }
   }

   $(document).ready(function() {
    var feedURL = "http://tatmingstudio.blogspot.com/feeds/comments/default";
    BloggerFunctions.getComments(feedURL);
   });
  </script>
  <div id="recent_comments"></div>

2012年5月3日星期四

jQuery Cross-Domain Ajax w/XML = NO WAY!

今天製作Blogger小工具時,原以為會很順利的。
Google Blogger API Reference Guide看到可以用GET方式得到Comments的XML。

當然二話不說,打開Aptana Studio立即工作。
基於jQuery Ajax,不到一小時已經搞定,在本機試行,沒有問題。
沾沾自喜地心想: (那會難得到我?)


那就把程式碼Copy & Paste至Blogger的HTML/JavaScript小工具,儲存,看一看。
OMFG~竟然什麼都沒有,打開IE9的F12 Developer Tool看看出現大概是這樣的Error。
XMLHttpRequest cannot load XXX.
Origin XXX is not allowed by Access-Control-Allow-Origin.
上Google搜尋一下,原來是Google防止Cross-Domain Request帶出的問題。

根據一些討論區答案,解決方法是轉用JSONP,因為Google API 伺服器不支援CORS (Cross-origin resource sharing)

其實Google API 既有提供JSON,jQuery亦可以解讀,當然問題其實老早已解決。

但我就是有點不服氣,深入理解一下CORS。

按照網上的高手建議,
可以在Http Headers (PHP/ASP.NET)或jQuery Ajax中加入:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization

之後又有人建議對jQuery設定
$.support.cors = true;
crossDomain: true;

當然統統試過都沒有用,因為主導權是在Server那邊,要是好像Google般不容許Cross-Domain Request的話,無論如何在Headers出什麼古惑都沒有用。

以下就是我的Blogger Recent Comments的XML版本。
http://webapp.heliohost.org/html/blogger_recent_comments.htm
Save As另存新檔後,在本機用Browser打開,是沒有問題的。

但在線上看,就會出現我說的問題。
當然現在問題已經解決,我已經轉用JSONP方式去實現。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>blogger_recent_comments</title>
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
  <script type="text/javascript">
   function contains(arr, obj) {
    for(var i = 0; i < arr.length; i++) {
     if(arr[i].toLowerCase() == obj.toLowerCase() && arr[i].length == obj.length)
      return true;
    }
   }

   if( typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function(str) {
     return this.indexOf(str) == 0;
    };
   }

   $(document).ready(function() {
    var feedurl = "http://www.blogger.com/feeds/880240131395666753/comments/default";
    var maxcount = 10;
    var minlength = 10;
    var exclude_name = new Array("&#36948;MiNG");

    $.support.cors = true;
    $.ajax({
     type : "GET",
     url : feedurl,
     dataType : "xml",
     crossDomain : true,
     headers : {
      "GData-Version" : "2"
     },
     success : function(responseData, textStatus, jqXHR) {
      var cnt = 0;
      var html = "";
      $(responseData).find('entry').each(function() {
       var title = $(this).find("title").text();
       var content = $(this).find('content').text();
       var url = $(this).find("link[rel='alternate']").attr('href');
       var authorname = $(this).find("author").find('name').text();

       if(!contains(exclude_name, authorname) && !content.startsWith('[...]') && title.length >= 10) {
        html += "<span style='font-family:Arial;color:#333;font-size:11px;font-weight:bold;'>" + authorname + " &#35498; : </span><br />" + "<span style='font-family:Arial;color:#666;font-size:11px;'>" + title + "</span><br /><br />";
        cnt++;
       }
       if(cnt == maxcount)
        return false;
      });
      var dom = $(html);
      $('#recent_comments').append(dom);
     },
     error : function(responseData, textStatus, errorThrown) {
      console.log(textStatus);
     }
    });
   });
  </script>
 </head>
 <body>
  <div id="recent_comments"></div>
 </body>
</html>