<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    love fish大鵬一曰同風起,扶搖直上九萬里

    常用鏈接

    統計

    積分與排名

    friends

    link

    最新評論

    Google Web Toolkit(翻譯)

    原文出處:XML.comhttp://www.xml.com/pub/a/2006/07/12/google-web-toolkit-ajax-java-ant-xml.html
    翻譯:劉嘉晗 http://www.tkk7.com/johnlau

    Google Web Toolkit

    By Bruce Perry
    2006年7月12日

    如果你是一名Java軟件或Ajax開發者,可能Google Web Toolkit(GWT)已經引起了你的注意。

    2006年5月,Google發布了這一免費的工具箱,遵循Apache風格的許可證。GWT被設計用于以Java語言編寫Ajax應用。Google已經提供了Windows和Linux下的beta版本,并承諾以后增加Mac OS X版本。

    本文描述了在Max OS X上一個簡單的Ajax應用的開發,使用GWT和常用的Java工具,如Apache Ant,Tomcat 5.0 Servlet容器,以及IntelliJ IDEA集成開發環境(后者是一款商業IDE)。本文假設讀者具有Java和Ant的基本知識。

    結合使用Ant與GWT


    我下載了GWT Linux beta版本,用Java編寫了我的應用程序,然后用一個Ant構建文件編譯和在一個Tomcat 5.0實例上部署了此程序。Ant文件執行GWT Java-to-JavaScript編譯器。此“編譯器”是一個命令行腳本,它執行一個GWT Java類,而后者為應用程序輸出JavaScript腳本。

    使用GWT beta包括兩種開發模式:宿主(host)模式和web模式。

    宿主模式是一個開發中的中間步驟,它使用內嵌的GWT瀏覽器;在這種模式下,已編譯代碼繼續在Java虛擬機(JVM)中運行。然而,宿主模式對于我們這些借用Linux版本的Mac OS X用戶還不可用。Mac OS X下的宿主模式要等到Max OS X版本發布了。

    一種不同的web開發方式


    本文探討了GWT開發者在為遠程過程調用(RPC)創建服務時可能面對的一些典型的web開發相關的任務。RPC是用于使用面向服務架構(SOA)的應用的軟件模型的一部分。這些開發任務包括:

    • 使用構建文件自動化開發和開發步驟(構建過程運行GWT編譯器,然后將編譯器的輸出和服務器端Java類文件部署到一個servlet容器,如Tomcat,Jetty或Resin)。
    • 使用Firefox的DOM Inspector查看GWT應用程序生成的HTML。
    • 重新設計頁面控件而不需改動底層HTML(因為你在使用GWT的Java API)。
    • 確定HTML已合法地標記,例如,基于你的組織所要求的特定格式的XHTML文檔。

    為您服務


    首先,我會簡要描述該應用程序創建的服務。它被設計用來展示GWT采用的模式。

    該應用程序在瀏覽器中顯示一個表單,要求用戶輸入名字,年齡,以及國家。當用戶通過點擊按鈕提交表單時,程序在一個文本區內顯示一條服務器回應而不需刷新頁面。圖1是程序在Safari瀏覽器中的顯示。

    圖1: GWT生成的一個簡單視圖

    例如,當用戶在一個文本框為空時點擊OK,Submit按鈕時,結果如圖2所示。

    圖2: 應用程序以紅色顯示一條錯誤信息

    巧妙的服務機制


    在Ajax應用中使用RPC消除了顯式地處理XMLHttpRequest和相關聯的服務器返回值的需要,因為GWT對象會為你處理這些通訊工作。

    程序定義的每個服務需要兩個Java接口和一個Java類。為了編譯這些類,需要確定gwt-user.jar庫在classpath中(Ant文件中增加一個條目就可以解決)。以下代碼示范了定義我們的服務的Java接口。


    package?com.parkerriver.gwt.testapp.client;

    import?com.google.gwt.user.client.rpc.RemoteService;

    public?interface?ShowRespService?extends?RemoteService{
    ????String?displayResponse(String?req);
    }


    服務接口必須擴展GWT接口RemoteService。它只定義了一個方法 displayResponse()。

    你還需要定義一個接口,客戶端或最終被下載的JavaScript代碼將會用它調用服務方法。GWT使用了一種回調設計模式,當我給出客戶端代碼的時候會再來描述它(請看MyForm.java)。

    package?com.parkerriver.gwt.testapp.client;

    import?com.google.gwt.user.client.rpc.AsyncCallback;

    public?interface?ShowRespServiceAsync?{
    ????
    public?void?displayResponse(String?s,
    ????????????????????????????????AsyncCallback?callback);
    }


    要使一個服務在GWT中可用必須遵守命名約定;在服務接口名(ShowRespService)后增加Async后綴。AsyncCallback對象是GWT API的一部分,它的目的是為客戶端處理服務響應。不管怎樣,等你看了這段代碼應用的地方后會對這一行為有更清晰的了解。這些對象定義都位于用于生成客戶端JavaScript的Java代碼中。

    一個改頭換面的servlet


    最后,你需要定義一個Java類來實現遠程服務接口。這個類將會存在于你的Ajax應用程序的服務器端。

    package?com.parkerriver.gwt.testapp.server;

    import?com.parkerriver.gwt.testapp.client.ShowRespService;
    import?com.google.gwt.user.server.rpc.RemoteServiceServlet;
    import?java.util.Date;

    public?class?ShowRespServiceImpl?extends?RemoteServiceServlet
    ????????
    implements?ShowRespService??{
    ????
    public?String?displayResponse(String?req)?{
    ????????
    if(req.length()?<?1)?{
    ????????????
    throw?new?IllegalArgumentException(
    ????????????????????
    "Blank?submissions?from?the?client?are?invalid.");
    ????????}
    ????????StringBuffer?buf?
    =?new?StringBuffer("Your?submission:?");
    ????????Date?date?
    =?new?Date();
    ????????String?serverInfo?
    =?this.getServletContext().getServerInfo();
    ????????buf.append(req);
    ????????buf.append(
    "\n");
    ????????buf.append(
    "Server?response:?");
    ????????buf.append(date.toString());
    ????????buf.append(
    "\n");
    ????????buf.append(serverInfo);
    ????????
    return?buf.toString();
    ????}
    }


    該類必須擴展RemoteServiceServlet,這是一個GWT API對象,它本身擴展了javax.servlet.http.HttpServlet。也就是說,該類和它實現的接口需要被部署到你的servlet容器。

    步驟


    現在我們已經定義了服務,讓我們退回幾分鐘來看一看程序的目錄結構。Google Web Toolkit包括了一個名為applicationCreator的命令行腳本,它將會為你生成項目目錄結構。解壓縮GWT下載包后,你可以在頂層目錄中找到applicationCreator。我使用下面的命令行來開始:

    applicationCreator?-out?/Users/bruceperry/1gwt/secondapp/?com.parkerriver.gwt.testapp.client.MyForm

    圖3顯示了目錄結構

    圖3: 一個GWT和IntelliJ項目目錄

    applicationCreator生成./src目錄和MyForm-compile以及MyForm-shell腳本。我的Ant文件執行MyForm-compile;另一個腳本運行宿主模式。./src目錄包括與最初的包命名相符的嵌套目錄結構,如圖4所示。

    圖4:一個GWT應用程序的包和模塊

    文件MyForm.gwt.xml是生成的配置文件,GWT稱之為“模塊(module)”。它為你的程序定義了表示“入口點(entry point)”的Java類,類似于包含main()方法的Java類。

    <module>
    ????
    <!--Inherit?the?core?Web?Toolkit?stuff.??????????????????-->
    ????
    <inherits?name="com.google.gwt.user.User"/>
    ????
    <!--Specify?the?app?entry?point?class.???????????????????-->
    ????
    <entry-point?class="com.parkerriver.gwt.testapp.client.MyForm"/>
    </module>


    其它的文件或目錄是IntelliJ Web application project的產物,包括./classes,./WEB-INF,和./gwtproj.ipr,所以不需要特別留意它們。

    另外,./www目錄之前并不會出現(除非你自己創建它),直到運行GWT編譯器生成程序代碼。我的項目使用Ant文件getproj.xml,而項目屬性在gwtproj.properties中定義。在我展示Ant構建文件之前,我們先看一下代表程序入口點的MyForm.java類。

    入口點


    MyForm.jaca類實現了GWT API接口EntryPoint。所以,該類必須實現onModuleLoad()方法,當瀏覽器載入你的Ajax程序時,瀏覽器的JavaScript引擎將會調用它。

    換句話說,GWT編譯器將這個類編譯為JavaScript代碼。MyForm.java類為瀏覽器視圖建立表單控件。該類同時決定了用戶點擊OK,Submit按鈕時的響應。代碼中的注釋詳細描述了到底發生了什么,所以我在正文中不再贅述。

    package?com.parkerriver.gwt.testapp.client;

    import?com.google.gwt.core.client.EntryPoint;
    import?com.google.gwt.core.client.GWT;
    import?com.google.gwt.user.client.DOM;
    import?com.google.gwt.user.client.Window;
    import?com.google.gwt.user.client.Element;
    import?com.google.gwt.user.client.rpc.ServiceDefTarget;
    import?com.google.gwt.user.client.rpc.AsyncCallback;
    import?com.google.gwt.user.client.ui.*;

    import?java.util.Iterator;

    public?class?MyForm?implements?EntryPoint?{
    ????
    //提供狀態信息的HTML div元素的id
    ??? private?String?statusId?=?"status";
    ????
    //一個Grid對象;實際上,是一個HTML table
    ????private?Grid?grid?=?new?Grid(5,?2);
    ????
    //其它用戶界面對象
    ????private?Label??nmLab?=?new?Label();
    ????
    private?Label??ageLab?=?new?Label();
    ????
    private?Label??homeLab?=?new?Label();
    ????
    private?TextBox?nmTxt?=?new?TextBox();
    ????
    private?TextBox?ageTxt?=?new?TextBox();
    ????
    private?TextBox?homeTxt?=?new?TextBox();
    ????
    private?Button?okBut?=?new?Button();
    ????
    private?TextArea?tarea?=?new?TextArea();
    ????
    /* 當瀏覽器載入應用程序時本方法被調用。
    ??????? 本方法設置3個標簽和文本框,以及一個
    ??????? 按鈕和用來顯示服務器相應的文本區
    */
    ?????
    public?void?onModuleLoad()?{

    ????????
    //設置標簽和文本域
    ????????nmLab.setText("Full?Name:");
    ????????nmTxt.setMaxLength(
    25);
    ????????ageLab.setText(
    "Age:");
    ????????ageTxt.setVisibleLength(
    3);
    ????????ageTxt.setMaxLength(
    3);
    ????????homeLab.setText(
    "Home?country:");
    ????????homeTxt.setMaxLength(
    25);
    ????????
    //將這些控件放入Grid中
    ????????grid.setWidget(0,0,nmLab);
    ????????grid.setWidget(
    0,1,nmTxt);
    ????????grid.setWidget(
    1,0,ageLab);
    ????????grid.setWidget(
    1,1,ageTxt);
    ????????grid.setWidget(
    2,0,homeLab);
    ????????grid.setWidget(
    2,1,homeTxt);

    ????????
    //設置按鈕和文本區
    ????????tarea.setCharacterWidth(40);
    ????????tarea.setVisibleLines(
    25);
    ????????okBut.setText(
    "OK,?Submit");
    ????????
    //通過增加一個listener對象為按鈕設置行為
    ????????
    //詳細地講,一個帶有onClick()事件處理器的
    ????????
    //ClickListener對象。
    ????????okBut.addClickListener(new?ClickListener()?{
    ????????????
    public?void?onClick(Widget?sender)?{
    ????????????????
    //提示用戶遠程過程調用的狀態;
    ????????????????
    //見下面的方法
    ????????????????showRpcStatus(true);
    ????????????????
    //為服務器端服務創建一個客戶端存根的實例
    ??????????????? ShowRespServiceAsync?respService?=
    ????????????????????????(ShowRespServiceAsync)?GWT
    ????????????????????????????????.create(ShowRespService.
    class);
    ????????????????ServiceDefTarget?endpoint?
    =?(ServiceDefTarget)?respService;
    ????????????????
    //我們的服務的實現是一個RemoteServiceServlet的實例,
    ????????????????
    //所以提供到該servlet的服務器路徑
    ????????????????
    //該路徑為web.xml中設置的值
    ????????????????endpoint.setServiceEntryPoint("/parkerriver/s/showresp");
    ????????????????
    //該接口處理服務器響應。
    ????????????????
    //它會在一個文本區中顯示服務器的響應。
    ????????????????
    //如果回復消息表示一個錯誤
    ????????????????
    //則將用紅色字體顯示
    ????????????????AsyncCallback?callback?=?new?AsyncCallback()?{
    ????????????????????
    public?void?onSuccess(Object?result)?{
    ????????????????????????
    //如有,則移除與錯誤消息外觀相關的‘warning’CSS樣式
    ????????????????????????
    //
    ????????????????????????if(tarea.getStyleName().
    ????????????????????????????????equalsIgnoreCase(
    "warning")){
    ????????????????????????????tarea.removeStyleName(
    "warning");
    ????????????????????????}
    ????????????????????????
    //文本區顯示服務器的返回值
    ????????????????????????tarea.setText((String)result);
    ????????????????????}
    ????????????????????
    public?void?onFailure(Throwable?caught)?{
    ????????????????????????
    //文本區顯示任何異常消息
    ????????????????????????tarea.setStyleName("warning");
    ????????????????????????tarea.setText(
    ????????????????????????????
    "Server?request?raised?an?error;?Java?exception?:?"+
    ????????????????????????????caught?
    ==?null???"An?unknown?exception"?:
    ????????????????????????????????????????caught.getMessage());
    ????????????????????}
    ????????????????};
    ????????????????
    //調用服務方法。
    ????????????????
    //首先驗證表單值。
    ????????????????try{
    ????????????????????respService.displayResponse(
    ????????????????????????????getPanelTextContent(grid,
    true),
    ????????????????????????????callback);
    ????????????????}?
    catch?(Exception?e)?{
    ????????????????????tarea.setStyleName(
    "warning");
    ????????????????????tarea.setText(
    "Server?request?raised?an?error:?"+
    ????????????????????????????e.getMessage());
    ????????????????}??
    finally?{
    ????????????????????
    //當我們完成RPC調用時
    ????????????????????
    //移除狀態信息
    ????????????????????showRpcStatus(false);
    ????????????????}
    ????????????}
    ????????});

    ????????
    //現在將這些控件加到Grid上
    ????????grid.setWidget(3,0,okBut);
    ????????grid.setWidget(
    3,1,tarea);
    ????????
    //為OK按鈕的單元設定垂直對齊屬性
    ????????grid.getCellFormatter().setVerticalAlignment(3,0,
    ????????????????HasVerticalAlignment.ALIGN_TOP);
    ????????
    //為文本框設置垂直對齊屬性,
    ????????
    //以使它們合適地排列
    ????????grid.getCellFormatter().setVerticalAlignment(0,1,
    ????????????????HasVerticalAlignment.ALIGN_BOTTOM);
    ????????grid.getCellFormatter().setVerticalAlignment(
    1,1,
    ????????????????HasVerticalAlignment.ALIGN_BOTTOM);
    ????????grid.getCellFormatter().setVerticalAlignment(
    2,1,
    ????????????????HasVerticalAlignment.ALIGN_BOTTOM);


    ????????
    //將grid,實際上是一個HTML table,加到
    ????????
    //瀏覽器HTML中id值為"gridholder"的div元素中。
    ????????RootPanel.get("gridholder").add(grid);
    ????}
    ????
    /*簡單地測試是否有為空的域,然后以單一字符串返回
    ??? ?? 提交的值

    ??? ?? HasWidgets是Grid和其它panel類型的對象實現的一個接口。
    ??? ?? 因此,我們可以將grid傳入該方法;遍歷它包含的文本框,
    ??? ?? 并且驗證文本框的內容。
    ????
    */
    ????
    private?String?getPanelTextContent(HasWidgets?panelType,
    ???????????????????????????????????????
    boolean?validateContent)?{
    ????????StringBuffer?buf?
    =?new?StringBuffer("");
    ????????String?tmp?
    =?null;
    ????????
    if(panelType?!=?null)?{
    ???????????
    //為了簡介,省略
    ????????}
    ????????
    //返回以空格分隔的文本框的內容
    ???????
    return?buf.toString();
    ????}
    ????
    ????
    /* 過于簡化的驗證!?*/
    ????
    private?boolean?validateText(String?_content){
    ????????
    return?_content.length()?>?0;

    ????}

    ????
    private?int?getTextboxCount(HasWidgets?pType){
    ????????
    //未顯示: 返回panel中TextBox控件的數量
    ???????

    ????}

    ????
    /* 如果響應很長時間才到達
    ???? 則顯示給用戶一個狀態信息。
    */
    ????
    private?void?showRpcStatus(boolean?_on){
    ????????
    //利用GWT DOM API進行JavaScript DOM編程
    ??????

    ????????Element?el?=?DOM.getElementById(statusId);
    ????????
    if(el?!=?null)??{
    ????????????
    if(_on)?{
    ????????????????DOM.setStyleAttribute(el,
    "font-size","1.2em");
    ????????????????DOM.setStyleAttribute(el,
    "color","green");
    ????????????????DOM.setInnerHTML(el,?
    "Fetching?server?info");
    ????????????}??
    else{
    ????????????????DOM.setInnerHTML(el,?
    "");
    ????????????}
    ????????}

    ????}
    }


    代碼中大多部分處理GWT API。值得一提的是如果你需要實現JavaScript DOM編程,如showRpcStatus()方法中那樣,你可以通過使用com.google.gwt.user.client.DOM類實現這個任務。

    構建文件


    以下是Ant構建文件的重點;它:
    1. 編譯Java文件,結果寫入項目目錄中的./classes目錄。
    2. 執行GWT編譯腳本(本例中名為MyForm-compile)。
    3. 將./www目錄中生成的結果代碼移到一個已部署到Tomcat上的更大的web應用。
    4. 復制編譯后的Java servlet和相關接口(ShowRespService)到同一個web應用。

    負責編譯Java類和執行到JavaScript的轉換的兩個Ant任務定義為如果產生任何錯誤,則使整個構建失敗。

    Ant XML


    這里是gwtproj.properties文件包含的部分內容:

    web.deploy.location=/users/bruceperry/parkerriver/gwt
    web.classes.location
    =/users/bruceperry/parkerriver/WEB-INF/classes


    下面的XML表示了上述Ant文件的主要內容;完整文件的鏈接在本文的資源一節中。

    <?xml?version="1.0"?encoding="UTF-8"?>
    <project?name="gwtproj"?default="all">
    ????
    <property?file="gwtproj.properties"/>
    ????
    ?????
    <!--?The?top-level?directory?for?the?project?and
    ????where?the?ant?file?resides?
    -->
    ????
    <dirname?property="module.gwtproj.basedir"?file="${ant.file}"/>
    ????
    ?????
    <!--?The?./classes?directory?inside?the?top-level?directory?-->
    ?????
    <property?name="gwtproj.output.dir"?value=
    ????????????"${module.gwtproj.basedir}/classes"
    />
    ????????????
    ????
    <!--?This?target?calls?MyForm-compile?to?create
    ????all?the?content?in?the?./www?directory?
    -->
    ????
    <target?name="gwt-compile"?depends=
    ????????????"compile.production.classes"

    ????????????description
    ="use?gwt's?compiler">
    ????????
    <delete>
    ????????????
    <fileset?dir="${web.deploy.location}"?includes="**/*"/>
    ????????
    </delete>
    ????????
    <exec?executable=
    ????????????????"${module.gwtproj.basedir}/MyForm-compile"

    ??????????????failonerror
    ="true"/>
    ????????
    <copy?todir="${web.deploy.location}">
    ????????????
    <fileset?dir=
    ????????????????????"${module.gwtproj.basedir}/www"
    >
    ????????????
    </fileset>
    ????????
    </copy>
    ????
    </target>

    ????
    <target?name="compile.production.classes"?description=
    ????????????"Compile?the?gwtproj?production?classes"
    >
    ????????
    <mkdir?dir="${gwtproj.output.dir}"/>
    ????????
    <javac?destdir="${gwtproj.output.dir}"?debug=
    ????????????????"on"
    ?failonerror="true"?nowarn=
    ????????????????"off"
    ?memoryMaximumSize="128m"?fork=
    ????????????????"true"
    ?executable="${module.jdk.home.gwtproj}/bin/javac">
    ????????????
    <classpath?refid="gwtproj.module.classpath"/>
    ????????????
    <src?refid="gwtproj.module.sourcepath"/>
    ????????
    </javac>
    ????
    </target>
    ????
    ??
    <!--?copy?the?Java?servlet?classes?to?the?web?application?-->
    ????
    <target?name="deploy.classes"??depends="gwt-compile"
    ????????????description
    ="copy?classes?to?web?directory">
    ????????
    <copy?todir="${web.classes.location}">
    ????????????
    <fileset?dir="${gwtproj.output.dir}">
    ????????????
    </fileset>
    ????????
    </copy>
    ????
    </target>
    ????
    ????
    <target?name="all"?depends="deploy.classes"
    ????????????description
    ="build?all"/>
    </project>


    你可以在IDE中運行該Ant文件(比如在IntelliJ中)或者在包含構建文件的目錄中運行下面的命令行:

    ant?-buildfile?gwtproj.xml


    大多數情況下,在更改應用程序并運行Ant后,你可以通過在瀏覽器中刷新頁面看到所作的改變。

    最后設置


    在最后的設置上,你可能需要了解的是添加gwt-user.jar庫到你的web應用的/WEB-INF/lib目錄。

    我創建了我自己的JAR文件,去掉了其中的javax包,命名為gwt-user-deploy.jar,并添加到/WEB-INF/lib。這是因為Tomcat將不會載入web應用程序中包含servlet API類的庫。

    web開發者的吹毛求疵


    applicationCreator同時創建了你的Ajax應用程序的HTML前端,本例中名為MyForm.html。

    如果你的應用程序的HTML需要遵從如XHTML transitional或Strict時怎么辦呢?對于XHTML transitional的情況,我首先在MyForm.html頂端增加了所需的DOCTYPE,以及html標簽的相關屬性:

    <!DOCTYPE?html?PUBLIC?"-//W3C//DTD?XHTML?1.0?Transitional//EN"
    ????????"http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd"
    >
    <html?xmlns="http://www.w3.org/1999/xhtml"?xml:lang="en"?lang="en">


    然后我上傳MyForm.html到http://validator.w3.org/的World Wide Web協會的HTML驗證器。

    運行驗證器后,我對HTML做了一些簡單的修改,如合適地關閉元標簽以及增加type="text/javascript"到script標簽。

    Strict:嘖嘖


    然而,如果你要遵從XHTML Strict標準,則可能需要做一些更復雜的更改。例如,W3C的驗證器將iframe標簽標為“undefined element”,iframe是GWT的歷史支持所需要的(提供如瀏覽器的后退按鈕同樣的功能)。XHTML已經移除了iframe元素。

    這對你可能不是一個問題(可能會同其它明顯問題一起在GWT的未來版本中解決);但是,你可以自己實現替代策略,如擴展GWT的類和創建你自己的兼容的控件。

    使控件各就各位


    視覺外觀設計是web開發中總是出現的問題。項目設計者希望頁面看起來和他們在Adobe Illustrator中創建的一摸一樣,是嗎?

    盡管在復雜的Ajax項目中你可能無法實現這一使程序養眼的理想,至少你可以使用Firefox的DOM Inspector來檢視你的Java類最終生成的HTML。那么從這里開始。

    打開Firefox的Tools=>DOM Inspector菜單項(如圖5)

    圖5: 用DOM Inspector查看HTML

    可見Java代碼中的com.google.gwt.user.client.ui.Grid對象實現為一個HTML table標簽。table中包含OK,Submit按鈕的TD標簽與一個樣式屬性“verticle-align:top”關聯。

    這使按鈕與文本區頂端對齊。下面是MyForm.java類中設置對齊方式的代碼:

    //set?the?vertical?alignment?for?the?OK?button's?cell
    ????????grid.getCellFormatter().setVerticalAlignment(3,0,
    ????????????????HasVerticalAlignment.ALIGN_TOP);

    如果代碼中沒有此調用,按鈕就會很業余地掛在文本區的中間區域。

    現在要做的只是使按鈕與它上面的文本標簽左對齊。

    資源

    ?? ?Google Web Toolkit: http://code.google.com/webtoolkit/
    ??? 本文代碼: gwtarticle_jul06.zip

    XML.com Copyright?? 1998-2006 O'Reilly Media, Inc.

    posted on 2006-07-21 01:31 liaojiyong 閱讀(570) 評論(1)  編輯  收藏 所屬分類: Ajax

    評論

    # re: Google Web Toolkit(翻譯) 2013-01-28 14:04 3

    3  回復  更多評論   

    主站蜘蛛池模板: 免费阿v网站在线观看g| 午夜免费1000部| 国产免费131美女视频| 亚洲午夜国产精品| 亚洲视频免费观看| 亚洲成人黄色在线| 91成人免费观看网站| 国产亚洲蜜芽精品久久| 成人毛片免费观看视频大全| 中文字幕乱码亚洲无线三区| 日韩精品无码区免费专区| 亚洲人成电影网站久久| 高清国语自产拍免费视频国产 | 亚洲精品国产suv一区88| 四虎成人免费网站在线| 亚洲av日韩综合一区久热| 国产精品冒白浆免费视频| 色九月亚洲综合网| 亚洲国产精品一区二区第一页免| 免费一级特黄特色大片| 亚洲色成人中文字幕网站 | 中文字幕无码播放免费| 午夜在线a亚洲v天堂网2019| 国产美女精品久久久久久久免费| 污视频网站免费观看| 亚洲国产精品无码久久一线| 91香蕉国产线观看免费全集| 亚洲狠狠成人综合网| 全黄性性激高免费视频| 免费视频一区二区| 亚洲另类春色校园小说| 免费大黄网站在线观| 黄色网址在线免费| 在线观看亚洲AV日韩A∨| 亚洲人成色77777在线观看大| av永久免费网站在线观看| 日韩亚洲产在线观看| 亚洲人成网站观看在线播放| 无码成A毛片免费| 亚洲AV无码精品国产成人| 久久亚洲AV无码精品色午夜麻|