BloggerAds

2011年6月25日 星期六

Google Map JavaScript API v3 實作滑鼠右鍵(Right Click)選單

要實作Google Map的右鍵選單,可參考下列做法,在實作之前,先做好Google Map API的一些基本動作,不熟的人可以參考之前的文章1. Google Map  初始化這段。接著就可以參考下面的動作了。


首先可以先設定一個CSS的Class,如下:
.contextMenu {
       position: absolute;
       min-width: 100px;
       z-index: 1000;  /* 必要 */
       background: #fff;
       border-top: solid 1px #CCC;
       border-left: solid 1px #CCC;
       border-bottom: solid 1px #676767;
       border-right: solid 1px #676767;
       padding: 0px;
       margin: 0px;
       display:none;    /* 預設為不顯示 */
       font-size:smaller;
}
其中有個很重要的一點,就是z-index這個屬性一定要給,如果沒給的話,不管你怎麼按右鍵、改程式,選單都不會出來。原因的話,我還沒查到...= = (這是心路歷程)。

再來可以寫一個DIV,就是要在滑鼠右鍵按下( Right Click)時顯示的選單,把class設剛剛寫好的CSS Class:
<div id="rightMenu" class="contextMenu">
    <a>設為起點</a>    
    <a>設為終點</a>   
</div>
在接下來的JavaScript部份,因為有使用jQuery語法,所以必須要include jQuery,請在<head>中加入這一行程式:
<script type="text/javascript" src="jquery-1.6.1.min.js"></script>
接下來就是JavaScript部份了:
<script language='javascript'>
    var contextMenu = $("#rightMenu");    //宣告物件
    $(map.getDiv()).append(contextMenu);          
  
    //加入事件
    google.maps.event.addListener(map, "rightclick", function(e) {     
        //先隱藏div
        contextMenu.hide();

        var mapDiv = $(map.getDiv());
        var x = e.pixel.x;
        var y = e.pixel.y;              

        //當選單太靠近邊界時,調整選單位置
        if (x > mapDiv.width() - contextMenu.width()) {
            x -= contextMenu.width();
        }
        if (y > mapDiv.height() - contextMenu.height()) {
            y -= contextMenu.height();
        }
        //設定座標,並加入動畫
        contextMenu.css({ top: y, left: x }).fadeIn(100);                    
    });

    //當下列事件觸發時,隱藏選單
    $.each('click dragstart zoom_changed maptypeid_changed'.split(' '),  
        function(i, name) {
            google.maps.event.addListener(map, name, function() {
                contextMenu.hide()
            });
    });
</script>
參考資料:
http://www.pinsoftstudios.com/tips/google-maps-api-v3-right-click-context-menu

2011年6月22日 星期三

ASP.NET 動態新增Control、UserControl在PostBack後的處理

當有控制項(Control)或使用者控制項(UserControl)在頁面中是屬於動態新增的,若在頁面上不會觸發PostBack事件,其實是很好處理的,在使用上不會有什麼大問題。問題在於觸發PostBack的情況是很容易發生的,當PostBack發生後,你會發現所有動態產生的控制項通通不見了,這個問題實在是很惱人,所以在動態新增控制項時,要做一些特別的處理,程式才會乖乖的照著你的意思跑。

動態增加控制項的方法如下(以下以使用者控制項為例):
//載入User Control
UserControl1 ucObj=(UserControl1)LoadControl("~/UserControl1.ascx");

//若為一般控制項(Control),只要new一個新物件即可
//以TextBox為例,TextBox txtObj = new TextBox();

panel1.Controls.Add(ucObj); //加在Panel中
若上述程式放在按鈕的Click事件中,當User每按一次按鈕時,控制項照理說會一直增加,但實際上卻一直只有一個UserControl出現在畫面上。出了什麼問題? 原因就是沒有ViewState,所以PostBack後,原先增加的控制項就消失了,只有最後新增的一個控制項存在。因為是動態新增的,所以在Page_Init時.NET沒辦法將控制項重建,而解決方式就是用ViewState或Session將新增的控制項總數記下來,在Page_Load時將動態新增的控制項加回去,在新增控制項時,要多記錄控制項數目,所以上面的程式要改成如下:
//載入User Control
UserControl1 ucObj=(UserControl1)LoadControl("~/UserControl1.ascx");

panel1.Controls.Add(ucObj); //加在Panel中

if (ViewState["UcCount"] != null) {
    //記錄控制項數目
    ViewState["UcCount"]=           (int.Parse(ViewState["UcCount"].ToString())+1).ToString();
}else{
    ViewState.Add("UcCount", "1");
}
另外,在Page_Load事件中,要加入下列程式碼:
if (ViewState["UcCount"] != null) {
    for (int i = 1; i <= int.Parse(ViewState["UcCount"].ToString()); i++) {                              panel1.Controls.Add((UserControl1)LoadControl("~/UserControl1.ascx"));
    }
}
另外要注意的是,新增控制項時不可以設定ID(ex. ucObj.ID = "xxx"),否則每次PostBack時,都會視為一個新的物件,而讓PostBack前輸入的值消失。

參考資料:
http://social.msdn.microsoft.com/forums/zh-TW/236/thread/c18da009-fbcd-437b-9f9c-f758ade1e477/

2011年6月20日 星期一

ASP.NET錯誤:具有潛在危險 Request.Form 的值已從用戶端 偵測到 對應方式

在ASP.NET中,預設會將XSS (Cross Site Script)攻擊擋掉,若發現XSS攻擊,ASP.NET就會出現下列這種錯誤訊息:
具有潛在危險 Request.Form 的值已從用戶端 (XXXX) 偵測到
對於網頁開發者來說,自動擋掉錯誤是一件好事,但系統卻會出現錯給你的User看,這是個很不好的,為了避免這種狀況,
有一種解決方式是將 validateRequest 設成false,如果是針對個別網頁的話,修改的地方如下:
<%@ Page Language="C#" AutoEventWireup="false" CodeFile="Test.aspx.cs" Inherits="Test" validateRequest="False" %>
如果要針對整個網站的話,則是透過修改 web.config來達到整個網站關閉的目的
<system.web>
         <pages validateRequest="False" />
</system.web>

但若依上述方式修改,等於是讓系統開了個漏洞,所以若以上述方式修改的話,還必須要為每個控制項的值加入HtmlEncode,以防止XSS攻擊,ex.
txtBox1.Text = HttpUtility.HtmlEncode(txtBox1.Text)
總而言之,上面的方式,我個人覺得不太好,所以接下來還有另一種方式:透過Error事件,來做處理。這也有兩種方式可以實作
第一種方式,是用Page_Error來做處理,在程式中加入如下程式:
    protected void Page_Error(object sender, EventArgs e) {              
        if (Server.GetLastError().GetType().ToString() == "System.Web.HttpRequestValidationException") {
            Response.Redirect([要導入的錯誤畫面]);
        }

        Server.ClearError(); //清掉錯誤,讓ASP.NET的錯誤不出現
    }
若嫌要在每個網頁加上這段程式很麻煩,可以建立一個 BasePage 類別,再讓每個網頁繼承即可。

另外還有一種更方便的方式:在 Global.asax新增application_error事件,程式如下:
 void Application_Error(Object sender, EventArgs e)
    {
       if (Server.GetLastError().GetType().ToString() == "System.Web.HttpRequestValidationException") {
            Response.Redirect([要導入的錯誤畫面]);
        }
     
        Server.ClearError();
    }
這樣就不會出現ASP.NET錯誤了

參考資料:
http://tgw1029.blogspot.com/2010/02/requestform.html
http://social.msdn.microsoft.com/forums/zh-TW/236/thread/a4ecbacc-ffb9-49b9-bf52-93749a3e699a
http://www.dotblogs.com.tw/kirkchen/archive/2009/12/07/12314.aspx

Codeigniter 使用POST方式實作jQuery UI中的AutoComplete(自動完成)

要在Codeigniter中利用jQuery來實作AutoComplete,目前比較受歡迎的,大概是這個 jQuery Plugins jQuery UI。jQuery Plugins透過POST來實作的方式Google一下就有很多,反而jQuery UI的討論比較少。

在jQuery UI的官方網站中的範例中,是使用 $.getJSON方式來傳輸入的值給Server端,程式如下:

$.getJSON( "search.php", {term: extractLast( request.term )}, response );
$.getJSON是使用GET方式,與Codeigniter預設是不開放GET有衝突到,所以要解決這個問題有兩種做法:
一、將Codeigniter中application/config/config.php中的 $config['enable_query_strings'] 設為 true
二、不要用 $.getJSON,改用$.ajax來抓server端的資料

下面就來說說第二種改用$.ajax來抓資料的方式。

Server端的程式如下:

$aryPara = array(
   $this->input->post('term') //抓term(使用者所輸入的值)
);

$sql = "SELECT col1 FROM table1 WHERE col1 LIKE CONCAT(?, '%');";
$result = $this->db->query($sql, $aryPara);        
if($result->num_rows() > 0){       
   $data= array(); //一定要轉成有label及value的array
    
   foreach($result->result() as $row){
      //設定資料, col1替換成你的欄位名稱
      $data[] = array('label'=> $row->col1, 'value'=> $row->col1);
   }
}
echo json_encode($data);
   
$result->free_result();
$this->db->close();  

Client端的程式如下:

$("#txtKeyword").bind("keydown", function( event ) {
    if ( event.keyCode === $.ui.keyCode.TAB &&
           $(this).data("autocomplete").menu.active ) {
       event.preventDefault();     
    }
})
.autocomplete({
    source: function(request, response) {
       $.ajax({url: "search.php",
           //注意:這裡是設定post的變數及值,Server端就是以這個變數名稱取值
           data: {term: extractLast( request.term )},  
           dataType: "json",
           type: "POST",
           success: function(data){
                   response(data);
           }
       });
    },     
    search: function() {
      var term = extractLast(this.value);
      if (term.length < 2 ) { //當字數小於兩個字時,不抓資料
          return false;
      }
    },
    focus: function() {return false;},
    select: function(event, ui) {
          var terms = split( this.value );
          terms.pop();
          terms.push( ui.item.value );
          terms.push( "" );
          this.value = terms.join( ", " );
          return false;
    }
});

function split( val ) {
    return val.split( /,\s*/ );
}
function extractLast(term) {
    return split( term ).pop();
}
這樣就大功告成了。

參考:
http://forum.jquery.com/topic/jquery-autocomplete-new-parameter-implemented-method-get-post
http://api.jquery.com/jQuery.getJSON/
http://jqueryui.com/demos/autocomplete/#multiple-remote

2011年6月13日 星期一

Google Map JavaScript API v3 的一些常用語法

下列是使用Google Map API時的一些常用的語法

1. Google Map  初始化:
先 include google map api
<script type="text/javascript"src="http://maps.google.com/maps/api/js?sensor=false" ></script>
接著在Body中加入下面這個Div,這個Div是要用來顯示Google Map的物件
<div id="map_canvas" style="width:300px;height:300px;"></div>
接著加入下面的JavaScript程式碼,可以 寫在body的onload事件 或 jQuery的 $(document).ready 中
var latlng = new google.maps.LatLng(23.69781, 120.96051499999999);  //台灣座標
var myOptions = {
    zoom: 15,    //地圖縮放比例,數字越大,路越大
    center: latlng,  //地圖的中心座標
    mapTypeId: google.maps.MapTypeId.ROADMAP  //地圖型態,可參考 這裡
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
2. 新增Marker:
var myLatlng = new google.maps.LatLng(23.69781, 120.96051499999999);
var marker = new google.maps.Marker({
    position: myLatlng,    //Marker的座標
    map: map,    //map物件,以這篇文章的例子,就是上面那個map物件
    title: 'title'    //當滑鼠移至Marker上時,要顯示的Title文字
});
3. 在Marker click時,顯示InfoWindow:
google.maps.event.addListener(marker, 'click', function() {
    infowindow.open(map, marker);
});
4. 新增InfoWindow:
var infowindow = new google.maps.InfoWindow();    //初始一個物件
infowindow.setContent('這是內容');    //InfoWindow的內容,可用Html語法
infowindow.setPosition(myLatlng);    //座標
5. 在地圖Click時新增一個Marker:
google.maps.event.addListener(map, "click", function(event) {
    marker_Click = new google.maps.Marker({
                    map: map,
                    position: event.latLng
                });
            });
6. 用地址查座標:
function getLatLngByAddr() {
    var geocoder = new google.maps.Geocoder();  //定義一個Geocoder物件
    geocoder.geocode(
        { address: '[地址]' },    //設定地址的字串
        function(results, status) {    //callback function
            if (status == google.maps.GeocoderStatus.OK) {    //判斷狀態
                alert(results[0].geometry.location);             //取得座標                                
            } else {
                alert('Error');
            }
      }
 );
}

參考:
http://itunes.apple.com/us/app/wikihow-how-to-diy-survival/id309209200?mt=8
Google Map Reference
Google Map API Tutorial

2011年6月6日 星期一

Codeigniter上實現Ajax (使用jQuery)

下面這個在Codeigniter上實現Ajax的方式,是透過jQuery的post方法來實現,首先寫好一個controller,controller的程式範例如下:
<?php
class testAjax extends CI_Controller {
function index() {
}
function testPost() {
echo "result=".$this->input->post('para');
}
 function testPara($strPara) {
echo "result=".$strPara;
}
}
?>
上面有兩個function,分別是以post (testPost)、傳入參數 (testPara)兩種方式來呈現,其實只要選擇一種來實作即可,可以自已選擇自已喜歡的方式。View的程式範例如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="jquery-1.6.1.min.js"></script>
<title></title>
</head>
<body>
    <script language='javascript'>
    //<![CDATA[
    function testAjaxPost() {     
      var options = {'para' : '[在這輸入para的值]'};
   
    $.post('/index.php/testAjax/testPost', options, function(data) {
           alert(data);
    });
    }

    function testAjaxPara() {            
       $.post('/index.php/testAjax/testPara/[在這輸入要傳入$strPara的值]', null, function(data) {
         alert(data); 
     });
    } 
    //]]>
    </script>
 
<form id='frm1' method='post'>
<table border="0">
<tr>
<td>
<input type='button' id='btnTestPost' value='Post' onclick='testAjaxPost();' />
<input type='button' id='btnTestPara' value='Para' onclick='testAjaxPara();' />
</td>
</tr>
</table>
</form>    
</body>
</html>
這樣就可以執行了,jQuery的post說明如下:
$.post(URL [,POST_Para] [,Success] [,Fail]);

URL:Server端的URL。
POST_Para:要以Post方式傳入的陣列變數(不需使用時,可不傳入)。
Success:成功時要執行的程式碼(不需使用時,可不傳入)。
Fail:失敗時要執行的程式碼(不需使用時,可不傳入)。
其中傳入參數(testAjaxPara)方式,可以直接在網址輸入參數的值
而Post (testAjaxPost)方式則是透過javascript的陣列來傳入參數的值。

若要做比較進階的Ajax運用,可以在controller將值輸出成json或xml格式,就可以做到更多的功能

參考:http://stackoverflow.com/questions/308054/learning-how-to-use-ajax-with-codeigniter

2011年6月1日 星期三

Facebook login-button的一些研究

若想使用Facebook的login-button元件,可參考Facebook Developers Docs,下列這段就是從範例中節錄下來的,可直接參考使用:
<html>
    <head>
      <title>My Facebook Login Page</title>
    </head>
    <body>
      <div id="fb-root"></div>
      <script src="http://connect.facebook.net/en_US/all.js"></script>
      <script>
         FB.init({ 
            appId:'YOUR_APP_ID', cookie:true, 
            status:true, xfbml:true 
         });
      </script>
      <fb:login-button perms="email,user_checkins">Login with Facebook</fb:login-button>
</body> 
</html>
繁體中文語系的人,可以把include的網址改成
http://connect.facebook.net/zh_TW/all.js
其中login-button有下列 官方公佈的屬性 可以使用:

  • show-faces - 是否顯示Facebook的圖像(照片)
  • width - 設定元件的寬度 (注意!不是按鈕的寬度),預設為200 
  • max-rows - 設定圖像(照片)顯示最大的列數,預設為1 (不知道可以做什麼用? 試了沒效果)
  • perms - 要求Facebook使用者授權的項目,詳細屬性可至 這裡 查看
除了上面這些屬性,其實還有一些屬性是可以用的:

  • autologoutlink - 是否顯示 登出 按鈕,true: 若使用者登入,會顯示登出;false: 若使用者登入,還是顯示登入。
  • size - 按鈕大小,可使用:small、medium、large、xlarge。
  • length - 按鈕內預設文字的長度,ex. short:登入、long:用Facebook登入。若是自訂文字的話,這個屬性是沒作用的。
  • background - 指定按鈕的背景色,可使用white、light、dark。預設為light
如果要更改按鈕上的字,可以照下面這樣改,這樣就可以修改按鈕中的文字了。
<fb:login-button>[在這輸入文字]</fb:login-button>
但若有將上面的autologoutlink設為true,且login-button該變為「登出」時,login-button中的文字卻會一樣是自已輸入的登入文字,這點還蠻奇怪的,我還沒找到方法解決這個問題。

若要在login-button登入後執行一些動作,可以在FB.init後加上:

FB.Event.subscribe('auth.login', function(response) {
            //打上你的程式碼
          });

這樣就可以在login之後做您想做的事,同理,若要在登出後做某些事,只要把 auth.login 改成 auth.logout 即可

 --------------------------------- 接下來要說自訂按鈕的用法 ----------------------------------------

上面的用法,是直接使用Facebook內定的按鈕,只要放上FBML Tag即可使用。但若如果要自訂登入按鈕的話,可以直接寫一個function,如:
function fbLogin() {  
      FB.login(function(response) {
       if (response.session) {
         if (response.perms) {
          //使用者已登入Facebook成功,且已授權你的應用程式存取
         } else {
           //使用者已登入Facebook成功,但未授權你的應用程式存取
         }
       } else {
         //使用者未登入成功
       }
     }, {perms:'publish_stream,offline_access'}); //設定需要授權的項目
  }
接著再放入一個Html Tag,如button,在onclick中呼叫function即可:
<input type='button' value='登入' onclick="fbLogin();"/>
要注意的是,若你放的是input tag,千萬要切記啊,type不可以使用submit,否則當你按下按鈕,跳出Facebook登入視窗,並輸入帳號密碼登入後,你只會看到一個白色的畫面,標題上面寫著「XD Proxy」,然後你的function連動都不會動,但卻已成功登入進Facebook。

PS.今天卡了一個下午就是在查這個問題,google查的都沒有效 (facebook login-button popup blank),就在要放棄時,想說該不會是submit的問題,結果就好了,我也不知道是什麼原因造成這結果.....

參考:
http://developers.facebook.com/docs/
http://forum.developers.facebook.net/viewtopic.php?id=72137