low level programmer

星期二 七月 07, 2009

YUI 做 Yahoo suggest.

description

遇到一個需求, 就是要像 yahoo 搜尋那樣有橘色的字.

reference

Yahoo! UI Library: AutoComplete

codes

TestAutoCompleteServlet.java: 網頁會呼叫這個 servlet, 每次都回傳一樣的東西
public class TestAutoCompleteServlet extends HttpServlet {
   
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println("誰誰誰");
            out.println("神神神");
            out.println("一一一");
            out.println("二二二");
            out.println("aaa");
            out.println("bbb");
            out.println("ccc");
            out.println("ddd");
            out.println("eee");
            out.println("fff");
            out.println("ggg");
            out.println("hhh");
        } finally { 
            out.close();
        }
    } 

    // doGet, doPost call processRequest
}
TestAutoComplete.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">

<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/fonts/fonts-min.css" />
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/autocomplete/assets/skins/sam/autocomplete.css" />
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>

<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/connection/connection-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/animation/animation-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/datasource/datasource-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/autocomplete/autocomplete-min.js"></script>


<style type="text/css">
    #myAutoComplete {
        width:25em; /* set width here or else widget will expand to fit its container */
        padding-bottom:2em;
    }
</style>

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body class="yui-skin-sam">
        <div id="myAutoComplete">
            <input id="myInput" type="text">
            <div id="myContainer"></div>
        </div>


        <script type="text/javascript">
            var test = function() {
                // Use an XHRDataSource
                var oDS = new YAHOO.util.XHRDataSource("${pageContext.request.contextPath}/TestAutoCompleteServlet");
                // Set the responseType
                oDS.responseType = YAHOO.util.XHRDataSource.TYPE_TEXT;
                // Define the schema of the delimited results
                oDS.responseSchema = {
                    recordDelim: "\n",
                    fieldDelim: "\t"
                };
                // Enable caching
                oDS.maxCacheEntries = 5;

                // Instantiate the AutoComplete
                var oAC = new YAHOO.widget.AutoComplete("myInput", "myContainer", oDS);
                oAC.formatResult = function(oResultData, sQuery, sResultMatch) {
                    var resultDataString = new String(oResultData);
                    var queryString = new String(sQuery);
                    var minusString = resultDataString.substr(queryString.length, resultDataString.length);
                    var hasMatch = resultDataString.indexOf(queryString) > -1;
                    if ( hasMatch ) {
                        return '<font color=orange>' + queryString + '</font>' + minusString;
                    } else {
                        return resultDataString;
                    }
                };
                return {
                    oDS: oDS,
                    oAC: oAC
                };
            }();
            
        </script>



    </body>
</html>

星期一 三月 30, 2009

JavaScript - addEventListener 做拖曳 div 的效果

JavaScript - addEventListener 做拖曳 div 的效果

Reference

JavaScript大全 - CH17

Description

DOM Level 2 API 有 addEventListener function 來註冊事件處理器, 不過 ie6 好像沒支援, 所以只用 firefox 測. 反正重點在知道有 addEventListener 這個東西
<style>
    #article {
        position:absolute;
        width:700px;
        top:200px;
        left:200px;
    }
</style>
<script type="text/javascript">
    window.onload = function() {
        function clearChildren(target) {
            if (!target) { return; }
            if ( target.firstChild ) {
                target.removeChild(target.firstChild);
                clearChildren(target);
            }
        }
        function showText(text, positionShower) {
            clearChildren( positionShower );
            positionShower.appendChild( document.createTextNode(text) );
        }
        function makeMovable(target, targetInitX, targetInitY) {
            if ( !target ) { return; }
            if ( !targetInitX ) { targetInitX = 0; }
            if ( !targetInitY ) { targetInitY = 0; }
            var startX = 0;
            var startY = 0;
            var selectedElmt = null;
            target.addEventListener('mousedown', function(e) {
                selectedElmt = e.target;
                startX = e.clientX;
                startY = e.clientY;
            } , false);
            target.addEventListener('mouseup', function(e) {
            if ( selectedElmt != null ) {
                    targetInitX = targetInitX + (e.clientX - startX);
                    targetInitY = targetInitY + (e.clientY - startY);
                    selectedElmt = null;
                    startX = 0;
                    startY = 0;
                }
            }, false);
            target.addEventListener('mousemove', function(e) {
            if ( selectedElmt != null ) {
                    var positionShower = document.getElementById('positionShower');
                    showText('(' + e.clientX + ',' + e.clientY + ')', positionShower);
                    target.style.top = targetInitY + (e.clientY - startY) + 'px';
                    target.style.left = targetInitX + (e.clientX - startX) + 'px';
                }
            }, false);
        }
        makeMovable(document.getElementById('article'), 200, 200);
    }
</script>
<body>
    <span id="positionShower">(0,0)</span>
    <div id="article">
ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE
ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE
ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE
ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE ARTICLE
    </div>
</body>
        

星期三 三月 04, 2009

JavaScript - Java 呼叫 JavaScript

JavaScript - Java執行JavaScript

Java執行JavaScript

Reference

JavaScript大全 - CH12

Description

雖然 Java 呼叫 JavaScript 是 JDK6 內建的應該可以大量使用, 可是因為 JavaScript 和 Java 在型別處理上不同, 所以我比較傾向暫時大概知道怎麼用就好, 其他兩邊型態如何轉換則等真正用到再查.
否則以前用過的 BeanShell 因為 script 部份都是 Java 語法, 我反而比較喜歡..

Focal points

  1. 最簡單的使用 Java 呼叫 JavaScript (如果要重複執行 JavaScript, 先把 JavaScript 編譯好後再重複使用比較好)
    package test;
    
    import javax.script.Invocable;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
    
    public class TestJavaScript {
        public static void main(String[] args) {
            try {
                TestJavaScript test = new TestJavaScript();
                test.test();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        private void test() throws ScriptException, NoSuchMethodException {
            ScriptEngineManager scriptManager = new ScriptEngineManager();
            ScriptEngine js = scriptManager.getEngineByExtension("js");
            js.eval("function test(x) { return x; }");
            Invocable invocable = (Invocable) js;
            for ( int i = 0; i < 5; i++ ) {
                Object result = invocable.invokeFunction("test", String.valueOf(i));
                System.out.println(result);
            }
        }
    }
                    
  2. 簡單的用 JavaScript 實做 Java 的 interface 與客製的 class.
    還可使用 public variable 與 method.
    還可傳參數進去.
    還可以是客製的 class.
    還可以設定變數進去
    package test;
    
    import java.awt.event.ActionListener;
    import javax.script.Bindings;
    import javax.script.Invocable;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.WindowConstants;
    
    public class TestJavaScript {
    
        public final String HELLO_FINAL = "hello final";
        public static String HELLO_STATIC = "hello static";
    
        public static void main(String[] args) {
            try {
                TestJavaScript test = new TestJavaScript();
                test.test();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        private void test() throws ScriptException, NoSuchMethodException {
            ScriptEngineManager scriptManager = new ScriptEngineManager();
            ScriptEngine js = scriptManager.getEngineByExtension("js");
            js.eval("var x = '';" +
                    "function setX(x) { this.x = x; }" +
                    "function actionPerformed(e) { " +
                        "var testJavaScript = new Packages.test.TestJavaScript();" +
                        "println( '1:' + testJavaScript );" +
                        "println( '2:' + testJavaScript.testShowFromJS() );" +
                        "println( '3:' + testJavaScript.HELLO_FINAL );" +
                        "println( '4:' + Packages.test.TestJavaScript.HELLO_STATIC );" +
                        "println( '5:' + testJavaScript.showTestJavaScript( testJavaScript )); " +
                        "println( '6:' + x );" +
                    "}");
            Invocable invocable = (Invocable) js;
            invocable.invokeFunction("setX", "XXXXXX");
            JFrame f = new JFrame();
            JButton b = new JButton("test");
            b.addActionListener( (ActionListener) invocable.getInterface(ActionListener.class) );
            f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            f.getContentPane().add(b);
            f.pack();
            f.setVisible(true);
    
        }
    
        public String showTestJavaScript(TestJavaScript tester) {
            return "show it. " + tester;
        }
    
        @Override
        public String toString() {
            return super.toString() + "this is TestJavaScript.class";
        }
    
        public String testShowFromJS() {
            System.out.println("here!");
            return "You call me.";
        }
    
    }
                    

星期一 二月 16, 2009

JavaScript - class, constructor, prototype

JavaScript - class, constructor, prototype

class, constructor, prototype

  • reference
  • JavaScript 大全 - CH9
    1. 建構式
      function Person(firstName, lastName) {
          this.firstName = firstName;
          this.lastName = lastName;
      }
      window.onload = function() {
          var p = new Person('John', 'Smith');
          alert(p.firstName);
          alert(p.lastName);
      }
                      
    2. prototype : 所有物件共用一個 method 使用一般屬性效率會不佳, 所以可以用 prototype. 當一個物件被 new 起來之後會起始 prototype 並讓所有的物件擁有該 prototype. 由於物件由建構子建購之後會給予同一個 prototype, 所以使用 prototype 可以減少記憶體的使用.
      注意使用 prototype 的屬性時, 別不小心用物件的屬性蓋掉 prototype 的.
      另外 prototype 也可用在內建型態, 例如 String.prototype... , 可是這樣就變成在改寫核心 JavaScript API, 所以不建議這樣做.
      function Person(firstName, lastName) {
          this.firstName = firstName;
          this.lastName = lastName;
          Person.prototype.show = function() {
              alert("Hi, " + this.firstName + ' ' + this.lastName);
          }
          Person.prototype.age = 5;
      }
      window.onload = function() {
          var p1 = new Person('A1', 'A2');
          var p2 = new Person('B1', 'B2');
          p1.show(); // Hi, A1 A2
          p2.show(); // Hi, B1 B2
          alert(p1.age); // 5
          alert(p2.age); // 5
          Person.prototype.age = 6;
          alert(p1.age); // 6
          alert(p2.age); // 6
          p1.age = 99; // 這時候已經在 p1 物件裡建立一個 age 屬性, 而非改寫 Person 的 age 屬性
          alert(p1.age); // 99
          alert(p2.age); // 6
      }
                      
    3. Java 的 method 要回傳屬性值的時候只需要 return name; 但是 JavaScript 必須指定 this, 就是 return this.name; 或是用 with(this)
      function Person(name) {
          this.name = name;
          Person.prototype.hi = function() {
              with(this) {
                  alert('Hi, ' + name);
              }
          }
          Person.prototype.hi2 = function() {
              alert('Hi, ' + name);
          }
      }
      window.onload = function() {
          var p1 = new Person('Smith');
          var p2 = new Person("John");
          p1.hi();  // Hi, Smith
          p1.hi2(); // Hi, John
          p2.hi();  // Hi, John
          p2.hi2(); // Hi, John
      }
                      
    4. 類別屬性與類別 method. 其實就是一般的廣域屬性與廣域 method, 只是 assign 到 class. 邏輯意義較重要.
    5. 比較
      1. == 用來比較 reference
      2. <, <=, >, >= 會先呼叫物件的 valueOf 再比較
      3. 排序: Array.sort 接受一個 function, 比較 a b 兩個物件的值. a 大於 b 回傳值大於 0, a 小於 b 回傳值小於 0, a 等於 b 回傳值為 0.
        function Person(age) {
            this.age = age;
            Person.prototype.toString = function() {
                return '[Person age=' + this.age + "]";
            }
        }
        window.onload = function() {
            var persons = [new Person(5),new Person(3),new Person(4),new Person(1),new Person(2)];
            persons.sort(function(personA,personB) {
                if ( !personA || !personB || !('age' in personA) || !('age' in personA) ) {
                    throw new Error("Not Person. personA=" + personA + ",personB=" + personB );
                }
                return personA.age - personB.age;
            });
            alert(persons); // [Person age=1],[Person age=2],[Person age=3],[Person age=4],[Person age=5]
        }
                                
    6. prototype 從 Object.prototype 繼承屬性, 其他的物件再從 prototype 繼承屬性, 最後在使用屬性時, 會先找物件的屬性, 再找物件的 prototype, 沒有的話再找 Object.prototype. 因此如果物件有定義了屬性, 就不會看到物件的 prototype 設定的同名屬性, 物件 prototype 若設定了屬性, 就看不到 Object.prototype 設定的該屬性
      Object.prototype.testname = 'objectname';
      Object.prototype.testname2 = 'objectname2';
      Object.prototype.testname3 = 'objectname3';
      function Person() {
          this.testname = 'Persontestname';
      }
      Person.prototype.testname = 'Personprototypetestname';
      Person.prototype.testname2 = 'Personprototypetestname2';
      window.onload = function() {
          var p = new Person();
          alert(p.testname); // Persontestname
          alert(p.testname2); // Personprototypetestname2
          alert(p.testname3); // objectname3
      }
                      
    7. 繼承自訂的物件:
      1. 呼叫 superclass.call
      2. 指定 prototype 到 new superclass
      3. 指定 constructor 到 subclass
      function Animal(name) {
          this.name = name;
      }
      Animal.prototype.breath = function() {
          return this.name + ' can breath.';
      }
      function Mammal(name, isAquatic) {
          Animal.call(this, name); // 初始化 super class
          this.isAquatic = isAquatic;
      }
      Mammal.prototype = new Animal('default'); // 這樣會把 Mammal 的 prototype 指向 Animal 的 prototype
      Mammal.constructor = Mammal; // 指定 prototype 後 Mammal 的建構子會和 Animal 的一樣.
                                   // 但我們不想要建構子是同一個, 所以要把建構子指回 Mammal
      Mammal.prototype.nurse = function() {
          var aquaticStr = this.isAquatic ? ' is aquatic. ' : ' is not aquatic. ';
          return this.name + ' which ' + aquaticStr + ' can nurse.';
      }
      Mammal.prototype.breath = function() {
          var aquaticStr = this.isAquatic ? ' is aquatic. ' : ' is not aquatic. ';
          return this.name + ' which ' + aquaticStr + Animal.prototype.breath();
      }
      window.onload = function() {
          alert(Mammal.constructor == Animal.constructor);
          alert(new Animal('People').breath());
          alert(new Mammal('whale', true).nurse());
          alert(new Mammal('whale', true).breath());
          alert(new Mammal('whale').breath());
          alert(new Mammal('whale').nurse());
          var m = new Mammal('people', false);
          alert(m instanceof Object); // true
          alert(m instanceof Animal); // true
          alert(m instanceof Mammal); // true
      }
                      

    星期四 一月 29, 2009

    JavaScript - function

    TODO supply a title

    reference

    JavaScript 大全 - CH8

    Function

    1. return : 只有 return; 和沒有 return 一樣都回傳 undefined
    2. 參數 : 如果傳入的參數大於 function 定的參數, 多的會被忽略; 如果傳的參數少於 function 定的參數, 則少的部份會變成 undefined
    3. function literal
      var f = function ff() {
          alert('ff=' + ff);
      }
      window.onload = function() {
          alert('f=' + f);
          f();
          alert('ff=' + ff); // undefined error
      }
                      
      上面程式宣告的 f 是 function literal, ff 則只能用在 f function 主體裡面. 如果在外部使用 ff 就會出現 undefined 的錯誤.
      function literal 可看出三種用法.
      1. 宣告在變數中
      var f = function() {
          alert('f');
      }
      window.onload = function() {
          f();
      }
                      
      2. 直接傳入成為參數
      var callAnyWay = function(f) {
          f();
      }
      window.onload = function() {
          callAnyWay( function() {
              alert( 'Pass to callAnyWay' );
          } );
      }
                      
      3. 宣告 function 直接呼叫
      window.onload = function() {
          var callAnyWay = function(f) {
              f();
          }( function() {
              alert('Pass to callAnyWay');
          } );
      }
                      
    4. parameters
      function testParams(a, b, c) {
          a = a || [];
          b = b || 'bb';
          alert('a=' + a);
          alert('b=' + b);
          alert('c=' + c);
      }
      window.onload = function() {
          testParams(); // alert a= b=bb c=undefined
          testParams('aa', null); // alert a=aa b=bb c=undefined (null 被當成 false)
          testParams('aa', 'bb22'); // alert a=aa b=bb22 c=undefined
      }
                      
    5. arguments : 可取得所有參數像操作陣列一樣操作參數. 不過要注意 arguments 不是陣列, 而是一個和陣列有編號和長度屬性的物件. 比方說 Array 的 length 真的可以拿來指定長度, 但是 arguments 的 length 在 ECMAScript 卻沒有說可以指定. 而且改變 arguments 的值也會改變具名參數的值, arguments 和具名參數只是指向具名參數的兩種方式.
      function writeAllArgs(arg0) {
          for ( var i = 0; i < arguments.length; i++ ) {
              alert(arguments[i]);
          }
          alert(arg0); // 1
          arguments[0] = 123456;
          alert(arg0); // 123456
      }
      window.onload = function() {
          writeAllArgs(1,2,3,4);
      }
                      
    6. arguments.callee : 這個屬性指向 arguments 物件所屬的 function
      function testCallee() {
          alert(arguments.callee);
      }
      window.onload = function() {
          testCallee(1,2,3);
      }
                      
    7. function 也是一種資料, 可以在參數間傳遞, 要呼叫的時候用 () 就可以了.
      function callMe(me) {
          me();
      }
      function showMsg(msg) {
          alert(msg);
      }
      window.onload = function() {
          showMsg('a'); // a
          var a = showMsg;
          a('show from a'); // show from a
          var b = new Object();
          b.showMsgFromB = showMsg;
          b.showMsgFromB('show msg from BB'); // show msg from BB
          callMe( a('show in callMe') );
      }
                      
    8. length屬性 : function 的 arguments 物件的 length 可得知總共傳了幾個參數, 而 function 的 length 屬性可以知道 function 定義了幾個參數
              function showLength1() {
                  alert(arguments.length);  // 5
                  alert(arguments.callee.length); // 0
              }
              function showLength2(args) {
                  alert(arguments.length); // 6
                  alert(arguments.callee.length); // 1
              }
              window.onload = function() {
                  showLength1(1,2,3,4,5);
                  showLength2(1,2,3,4,5,6);
              }
                      
    9. function 的 call method. 可直接把 function 指定給物件呼叫
      function showThis() {
          var s = '';
          for ( var i = 0; i < arguments.length; i++ ) {
              s += arguments[i];
          }
          return this + s;
      }
      window.onload = function() {
          var o = new Object();
          alert(showThis(1,2,3)); // [object Window]123
          alert(showThis.call(o,1,2,3,4)); // [object Object]1234
      }
                      
    10. function 的 apply method. 可直接把 function 指定給物件呼叫. 和 call 的差別在 apply 使用陣列的方式傳遞參數
      function showThis() {
          var s = '';
          for ( var i = 0; i < arguments.length; i++ ) {
              s += arguments[i];
          }
          return this + s;
      }
      window.onload = function() {
          var o = new Object();
          alert(showThis(1,2,3)); // [object Window]123
          alert(showThis.apply(o,[1,2,3,4])); // [object Object]1234
      }
                      
    11. closure : 就是一種匿名 function, 不過注意 javascript 的 function 很特別, 例如可以當成資料傳遞, 加個() 就可以呼叫. 還有因為 function 靈活的關係, 作用域也要注意. (closure 有正式的解釋, 請參考書上所寫的.)
    12. function 被定義之後, 會加入一個 call 物件到最前面, 指到 arguments 物件, 用 var 宣告的變數也都定義在 call 物件內. 而且所有的區域變數, 參數等也都在 call 物件內. 這樣的結果就是可以把參數或任何的定義都藏在 call 物件中.
      所以假如我要加一個 javascript 檔案到 html 中的時候, 如果我把所有的 function 都寫成廣域 function, 就很容易因為命名相同造成衝突. 解決的辦法可能是把 function 通通寫在統一的 function 裡面.
      比方說.
      function myF() {
          methodA();
          function methodA() {
              alert("inner MA");
          }
      }
      function methodA() {
          alert("outter MA");
      }
      window.onload = function() {
          myF(); // inner MA
      }
                      
      這樣就算 methodA 重覆到了也沒關係, 因為都包在自己定�����的 myF 裡面了. 但還需要一個 myF 的名稱. 如果需要的話可以更簡單
      (function() {
          methodA();
          function methodA() {
              alert("inner MA");
          }
      })();
                      
      這樣也是呼叫 methodA 又不會佔用到任何廣域名稱. 還有, 可以把動態的屬性藏在匿名 function 中, 再回傳匿名 function 夾帶著動態屬性, 這樣每次呼叫一個 function 都會得到不同的結果.
      var test1 = function() {
          var id = 0;
          return function() {
              return id++;
          }
      }
      var test2 = (function() {
          var id = 0;
          return function() {
              return id++;
          }
      })();
      window.onload = function() {
          var t1 = test1();
          alert(t1()); // 0
          alert(t1()); // 1
          alert(t1()); // 2
          alert(test2()); // 0
          alert(test2()); // 1
          alert(test2()); // 2
      }
                      
      從上面的 function 中, test1 是直接回傳一個 id 為 0 且回傳 id++ 的 function, 每次呼叫 test1() 都會初始 id 為 0. test2 則是先起始 id 為 0 傳到 test2 變數中, 則每次呼叫 test2() 都是執行回傳 id++ 的 function.

    星期五 十二月 12, 2008

    JavaScript - 動態新增欄位

    description

    這個做法 ... 只適用 firefox. 雖然做的東西不能只在 firefox 有用, 可是要刪掉也可惜. 記錄一下..

    reference

    IE cannot set 'name' attribute on runtime elements created by createElement
    就是在 ie 可用 document.createElement("<input name='myname' class='myclass'>"); 但這在 firefox 不管用, 所以要寫兩種.

    codes

    TestWindow.html
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <script type="text/javascript">
            function addReceiver() {
                var nameSpan = createSpan('Name : ');
                var mailSpan = createSpan('Email : ');
                var nameInput = createInput('receiverName');
                var mailInput = createInput('receiverMail');
                var contentTdL = document.createElement('td');
                contentTdL.appendChild(nameSpan);
                contentTdL.appendChild(nameInput);
                var contentTdR = document.createElement('td');
                contentTdR.appendChild(mailSpan);
                contentTdR.appendChild(mailInput);
                
                var delBtn = document.createElement('input');
                delBtn.setAttribute('type', 'button');
                delBtn.setAttribute('value', 'Del');
                var btnTd = document.createElement('td');
                btnTd.appendChild( delBtn );
    
                var contentTr = document.createElement('tr');
                contentTr.appendChild( contentTdL );
                contentTr.appendChild( contentTdR );
                contentTr.appendChild( btnTd );
    
                document.getElementById('receivers').appendChild( contentTr );
    
                delBtn.onclick = function() {
                    document.getElementById('receivers').removeChild( contentTr );
                }
            }
    
            function createInput(elmtName) {
                elmtName = elmtName ? elmtName : '';
                var inputElmt = document.createElement('input');
                inputElmt.setAttribute('type', 'text');
                inputElmt.setAttribute('name', elmtName);
                inputElmt.setAttribute('class', 'column');
                return inputElmt;
            }
    
            function createSpan(textVal) {
                textVal = textVal ? textVal : '';
                var spanElmt = document.createElement('span');
                spanElmt.setAttribute('class', 'desc');
                spanElmt.appendChild(document.createTextNode(textVal));
                return spanElmt;
            }
    
            function showInnerHTML() {
                var target = document.getElementById('receivers');
                var shower = document.getElementById('innerHTMLShower');
                clearChildren( shower );
                shower.appendChild( document.createTextNode(target.innerHTML) );
            }
    
            function clearChildren( target ) {
                while ( target.firstChild ) {
                    target.removeChild( target.firstChild );
                }
            }
        </script>
        <style>
            .column {
                color:red;
                background:aqua;
            }
            .desc {
                font-size: small;
                color:blue;
            }
        </style>
      </head>
      <body>
          <input type="button" value="show innerHTML" onclick="showInnerHTML();" />
        <table>
            <tbody id="receivers">
                <tr>
                    <td>
                        <span class="desc">Name :</span>
                        <span class="column">
                            <input name="receiverName" class="column" />
                        </span>
                    </td>
                    <td>
                        <span class="desc">Email :</span>
                        <span class="column">
                            <input name="receiverEmail" class="column" />
                        </span>
                    </td>
                    <td>
                        <input type="button" value="Add" onclick="addReceiver()" />
                        <input type="button" value="Add From Dialog" onclick="window.showModalDialog('ReceiverSelectorDialog.html', 'Selector', 'width=500,height=300')" />
                    </td>
                </tr>
            </tbody>
        </table>
        <textarea id="innerHTMLShower" cols="100" rows="20"></textarea>
      </body>
    </html>
    
    ReceiverSelectorDialog.html : 如果能做到 check 更直接的對 opener 操作, 例如取消選取就把 parent 增加的物件刪掉更好. 可是沒時間做那些.
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
        <head>
            <title></title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <script>
                function createInput(elmtName, val) {
                    elmtName = elmtName ? elmtName : '';
                    var inputElmt = document.createElement('input');
                    inputElmt.setAttribute('type', 'text');
                    inputElmt.setAttribute('name', elmtName);
                    inputElmt.setAttribute('class', 'column');
                    inputElmt.setAttribute('value', val);
                    return inputElmt;
                }
    
                function createSpan(textVal) {
                    textVal = textVal ? textVal : '';
                    var spanElmt = document.createElement('span');
                    spanElmt.setAttribute('class', 'desc');
                    spanElmt.appendChild(document.createTextNode(textVal));
                    return spanElmt;
                }
    
                function addReceiverIntoParent(receivers, nameVal, mailVal) {
                    var nameSpan = createSpan('Name : ');
                    var mailSpan = createSpan('Email : ');
                    var nameInput = createInput('receiverName', nameVal);
                    var mailInput = createInput('receiverMail', mailVal);
                    var contentTdL = document.createElement('td');
                    contentTdL.appendChild(nameSpan);
                    contentTdL.appendChild(nameInput);
                    var contentTdR = document.createElement('td');
                    contentTdR.appendChild(mailSpan);
                    contentTdR.appendChild(mailInput);
    
                    var delBtn = document.createElement('input');
                    delBtn.setAttribute('type', 'button');
                    delBtn.setAttribute('value', 'Del');
                    var btnTd = document.createElement('td');
                    btnTd.appendChild( delBtn );
    
                    var contentTr = document.createElement('tr');
                    contentTr.appendChild( contentTdL );
                    contentTr.appendChild( contentTdR );
                    contentTr.appendChild( btnTd );
    
                    receivers.appendChild( contentTr );
    
                    delBtn.onclick = function() {
                        receivers.removeChild( contentTr );
                    }
                    return contentTr;
                }
    
                function doAddReceivers() {
                    var receivers = window.opener.document.getElementById('receivers');
                    if ( document.getElementById('check_1').checked ) {
                        var nameVal1 = document.getElementById('nameVal_1').value;
                        var mailVal1 = document.getElementById('mailVal_1').value;
                        addReceiverIntoParent(receivers, nameVal1, mailVal1);
                    }
                    if ( document.getElementById('check_2').checked ) {
                        var nameVal2 = document.getElementById('nameVal_2').value;
                        var mailVal2 = document.getElementById('mailVal_2').value;
                        addReceiverIntoParent(receivers, nameVal2, mailVal2);
                    }
                    if ( document.getElementById('check_3').checked ) {
                        var nameVal3 = document.getElementById('nameVal_3').value;
                        var mailVal3 = document.getElementById('mailVal_3').value;
                        addReceiverIntoParent(receivers, nameVal3, mailVal3);
                    }
                    if ( document.getElementById('check_4').checked ) {
                        var nameVal4 = document.getElementById('nameVal_4').value;
                        var mailVal4 = document.getElementById('mailVal_4').value;
                        addReceiverIntoParent(receivers, nameVal4, mailVal4);
                    }
                    if ( document.getElementById('check_5').checked ) {
                        var nameVal5 = document.getElementById('nameVal_5').value;
                        var mailVal5 = document.getElementById('mailVal_5').value;
                        addReceiverIntoParent(receivers, nameVal5, mailVal5);
                    }
                    window.close();
                }
            </script>
        </head>
        <body>
            <input type="button" value="OK" onclick="doAddReceivers();" />
            <table>
                <tr>
                    <td>
                        <input id="check_1" type="checkbox" />
                    </td>
                    <td>
                        <input id="nameVal_1" type="text" value="A1" />
                    </td>
                    <td>
                        <input id="mailVal_1" type="text" value="A1" />
                    </td>
                </tr>
                <tr>
                    <td>
                        <input id="check_2" type="checkbox" />
                    </td>
                    <td>
                        <input id="nameVal_2" type="text" value="A2" />
                    </td>
                    <td>
                        <input id="mailVal_2" type="text" value="A2" />
                    </td>
                </tr>
                <tr>
                    <td>
                        <input id="check_3" type="checkbox" />
                    </td>
                    <td>
                        <input id="nameVal_3" type="text" value="A3" />
                    </td>
                    <td>
                        <input id="mailVal_3" type="text" value="A3" />
                    </td>
                </tr>
                <tr>
                    <td>
                        <input id="check_4" type="checkbox" />
                    </td>
                    <td>
                        <input id="nameVal_4" type="text" value="A4" />
                    </td>
                    <td>
                        <input id="mailVal_4" type="text" value="A4" />
                    </td>
                </tr>
                <tr>
                    <td>
                        <input id="check_5" type="checkbox" />
                    </td>
                    <td>
                        <input id="nameVal_5" type="text" value="A5" />
                    </td>
                    <td>
                        <input id="mailVal_5" type="text" value="A5" />
                    </td>
                </tr>
            </table>
        </body>
    </html>
    

    星期六 十一月 22, 2008

    JavaScript - Object and Array

    Learning JavaScript

    reference

    JavaScript 大全 - CH7

    focal points

    1. 動態變數 : 因為除了 物件.變數名稱 外還可用 物件['變數名稱'] 來取得變數, 所以變數名稱可以使動態的.
      function TestObj() {
          TestObj.prototype.toString = function() {
              var result = 'props=';
              for ( var propName in this ) {
                  result += ',' + this[propName];
              }
              return result;
          }    
      }
      function test() {
          var o = new TestObj();
          o.a0 = 'a0';
          for ( var i = 1; i < 10; i++ ) {
              o['a' + i] = 'a' + i;
              alert( 'setup o value=' + o['a' + i] );
          }
          alert( o );
      }
                      
    2. 通用的屬性與 method
      1. constructor : 會放物件的建構子
      2. valueOf : 要把物件轉基本型態的時候會自動呼叫 valueOf
      3. hasOwnProperty : 符合以下條件回傳 true
        1. 非繼承的屬性
        2. 屬性名稱由單一字串參數指定
        var o = { x:1, y:2 };
        alert( 'constructor' in o ); // true
        alert( o.hasOwnProperty('constructor') ); // false
        alert( o.hasOwnProperty('z') ); // false
        alert( o.hasOwnProperty('x') ); // true
                                
      4. propertyIsEnumerable : 幾乎與 hasOwnProperty 回傳一樣的值, 不過多了一個條件就是要能用 for/in 列出來..即使如此我還是找不到有什麼情況下這兩個值回傳不同的結果..
        1. 非繼承的屬性
        2. 屬性名稱由單一字串參數指定
        3. 該屬性能用 for/in 列舉出來
        function test() {
            var o = { x:1, y:2, abc:4 };
            alert( o.propertyIsEnumerable('constructor') ); // false
            alert( o.propertyIsEnumerable('z') ); // false
            alert( o.propertyIsEnumerable('x') ); // true
            alert( o.propertyIsEnumerable('abc') ); // true
        }
                                
      5. isPrototypeOf : 判斷呼叫物件是參數的原形物件. 這個的意思是說同一個 class 嗎? 這段要看深入一點才知道, 否則測試的時候有時候好像只能測出同個 class 會回傳 true, 但無法確認是不是這樣
        function TestObj() {
            this.x = 1;
            this.y = 2;
            this.abc = 4;
            this.test = function() {
                return 'test';
            }
            TestObj.prototype.testPrototype = function() {
                return 'this is prototype function';
            }
        }
        function test() {
            var o = new TestObj();
            alert( TestObj.prototype.isPrototypeOf(o) ); // true
            alert( TestObj == o.constructor ); // true
            alert( o.testPrototype == TestObj.prototype.testPrototype); // true
            alert( o.testPrototype.isPrototypeOf( TestObj.prototype.testPrototype ) ); // false
            alert( TestObj.prototype.testPrototype.isPrototypeOf( o.testPrototype ) ); // false
        }
                                
    3. Array
      1. 陣列只是個有索引號功能的物件, 所以在宣告的時候也可以字串為索引名稱. 此外陣列的索引不可不是 大於零小於二的三十二減一次方 的整數, 只要超過這個範圍都會被當成字串索引.
        var a = new Array(10,9); // index 0 : 10, index 1 : 9
        a['ab'] = 5; // key ab : 5
        for ( var propName in a ) {
            alert( propName ); // show 0, 1 and ab
        }
                                
      2. 陣列的索引值不一定是要連續的數字, JavaScript 只會為陣列中有值的部份使用到記憶體. 另外如果用 '0' 當索引值, 則會把此 0 當成字串所引
      3. 如果設定陣列的 length 屬性值, 縮小就會把超過新陣列範圍的值丟掉, 放大就會增加新的陣列欄位, 然後欄位沒值
        function test() {
            var a = [1,2,3];
            alert( a ); // 1,2,3
            a.length = 5;
            alert( a ); // 1,2,3,,
            a[50] = 50;
            alert( a.length ); // 51
            a.length = 2;
            alert( a ); // 1,2
        }
                                
      4. Array.join 可以把陣列轉成字串接起來
        function test() {
            var a = [1,2,3];
            alert( a.join('') ); // 123
            alert( a.join(',') ); // 1,2,3
            alert( a.join('abc') ); // 1abc2abc3
        }
                                
      5. Array.reverse 會直接將陣列中的值反轉.
        function test() {
            var a = [1,2,3];
            alert( a.join(',') ); // 1,2,3
            a.reverse();
            alert( a.join(',') ); // 3,2,1
        }
                                
      6. Array.sort 可以傳回一個排序過的陣列, 排序的方式可以是預設的或是提供一個排序的 function. 這個 function 會比較陣列任兩個值 a 與 b, 如果 a 應排前面就回傳正數, a 應排後面就回傳負數, 順序沒關係就回傳 0.
        function test() {
            var a = [22,111,3];
            alert( a.sort().join(',') ); // 111,22,3
            var comparator = function(a, b) {
                return a.toString().length - b.toString().length;
            };
            alert( a.sort( comparator ).join(',') ); // 3,22,111
        }
                                
      7. Array.concat 會回傳一個新的陣列, 該陣列把參數的值加入原始陣列, 不過要注意 concat 只會把第一層陣列解開, 如果陣列裡還有陣列, 裡面那層陣列不會被解開
        function test() {
            var a = [1,2];
            a = a.concat(3);
            alert( a ); // 1,2,3
            a = a.concat(4,5);
            alert( a ); // 1,2,3,4,5
            a = a.concat([6,7]);
            alert( a ); // 1,2,3,4,5,6,7
            a = a.concat([8,9],[10,11]);
            alert( a ); // 1,2,3,4,5,6,7,8,9,10,11
            a = a.concat([12,13,[14,15]],16);
            alert( a ); // 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
            alert( a.length ); // 15 因為 [14,15] 只算一個
        }
                                
      8. Array.slice 子陣列. 傳入一個值就是回傳指定位置到最後, 傳入兩個值就回傳指定位置間的值, 傳入負數就是相對於最後一個值開始算.
            var a = [1,2,3,4,5,6,7,8,9,10];
            alert( a.slice(5) ); // 6,7,8,9,10
            alert( a.slice(2, 4) ); //3,4
            alert( a.slice(4, 2) ); //無值
            alert( a.slice(-3) ); // 8,9,10
            alert( a.slice(-3,-1) ); // 8,9
            alert( a.slice(-1,-3) ); // 無值
                                
      9. Array.splice : 前兩個參數是要刪除的起點位置與要刪除的個數. 兩個之後的參數用來增加陣列到要刪除的位置.
        function test() {
            var a = [1,2,3,4,5];
            var as = a.splice(2, 1); // 從第 2 個位置刪掉一個值
            alert( a ); // 1,2,4,5
            alert( as ); // 3
        
            a = [1,2,3,4,5];
            as = a.splice(2, 2, 6,7,8,9); // 從第二個位置刪掉兩個值, 再加上 6,7,8,9
            alert( a ); // 1,2,6,7,8,9,5
            alert( as ); // 3,4
        }
                                
      10. Array.push / Array.pop : push 可把多個值放進陣列然後回傳最後一個. pop 會把陣列最後一個值移除然後回傳
        function test() {
            var a = [1,2,3,4,5];
            alert( a ); // 1,2,3,4,5
            alert( a.push(6,7,8) ); // 8
            alert( a ); // 1,2,3,4,5,6,7,8
            alert( a.pop() ); // 8
            alert( a ); // 1,2,3,4,5,6,7
        }
                                
      11. Array.unshift / Array.shift : unshift 把值放陣列最前面並回傳陣列長度. shift 移除最前面的值並回傳移除的值
        function test() {
            var a = [1,2,3,4,5];
            alert( a ); // 1,2,3,4,5
            alert( a.unshift(7,8,6) ); // 8 (IE6 是 undefined)
            alert( a ); // 7,8,6,1,2,3,4,5
            alert( a.shift() ); // 7
            alert( a ); // 8,6,1,2,3,4,5
        }
                                
      12. Array.toString / Array.toLocaleString : 會呼叫陣列中每個元素的 toString 與 toLocaleString

    星期日 十一月 16, 2008

    JavaScript - expression

    Learning JavaScript

    reference

    JavaScript 大全 - CH6

    focal points

    1. else 會與最近的 if 成一對, 下面的例子中, else 是 testb 2 的 else
      var testb = false;
      if ( testb ) 
        alert( 'if testb 1' )
        if ( testb ) 
            alert('if testb 2');
      else
        alert('else');
                          
                      
    2. swich 除了跟 java 一樣的用法以外, 還可以和字串以及算式比較. 不過注意 case 是用 === 做比較, 而非 ==. 然後 default 可以放任一個位置, 只是只能有一個.
      function test() {
        var q = [ 'abc', 'bcd', 'test', 'a' ];
        var v = 'testa';
        switch ( v ) {
            case 1 :
                alert( 1 );
                break;
            default : 
                alert( 'this is default option' );
            case 'a' :
                alert( 'a' );
                break;
            case 'ab' :
                alert( 'ab' );
                break;
            case q[0] :
                alert( q[0] );
                break;
            case q[2] + q[3] :
                alert( 'q2 + q3');
                break;
        }
      }
                      
    3. for/in :
      1. for 迴圈可以用索引數把陣列一個個印出, for/in 則是可以把物件內容一個個印出.
      2. 使用 for/in 的時候, 會把物件的屬性名稱交給 for/in 宣告的暫存變數.
        function test() {
            var obj = { a:'abc', b:true, c:123 };
            for ( var propName in obj ) {
                alert( propName + " : " + obj[propName] );
            }
        }
                                
      3. for/in 的暫存變數可以是計算式, 所以可以透過計算把物件屬性複製到其他陣列中.
        function test() {
            var obj = { a:'abc', b:true, c:123 };
            var i = 0; 
            var cloneObjPropNames = new Array();
            for ( cloneObjPropNames[i++] in obj ) {
                alert( 'clone obj propName : ' + cloneObjPropNames[i - 1] );
            }
            alert( cloneObjPropNames );
        }
                                
      4. 如果屬性在迴圈中被刪除就不會列舉出來, 可是如果迴圈中定義新屬性會不會被列舉就看瀏覽器實做 (FireFox 測試是不會列舉).
        function test() {
            var obj = { a:'abc', b:true, c:123 };
            for ( var propName in obj ) {
                delete obj.b;
                obj.d = 'ddd';
                alert( 'd' in obj );
                alert( propName );
            }
        }
                                
      5. for/in 並不會列舉物件的所有屬性, 唯讀, 永久保存, 不可列舉的屬性就無法列舉, JavaScript 內建屬性與 method 不會被列舉, 被繼承的使用者定義屬性會被列舉
        function TestObj() {
            var a = 0; // 不被列舉
            var b = 1; // 不被列舉
            function c() {} // 不被列舉
            TestObj.prototype.d = 2; // 會被列舉
            TestObj.prototype.e = function() {} // 會被列舉
        }
        function test() {
            Date.prototype.toString = function() {
                return 'custom toString'; 
            }
            Date.prototype.custom = function() {
                return 'custom'; 
            }    
            for ( var propName in new Date() ) {
                alert( propName ); // 除 custom function 外通通沒列舉 (包括 toString 也沒列舉)
            }
            for ( var propName in new TestObj() ) {
                alert( propName );
            }
        }
                                
    4. label 和 java 一樣用法. 且由於 label 和屬性或 function 名稱是無關的, 所以不用擔心 label 名稱和屬性或 function 名稱重複會有麼什麼問題.
      function test() {
          a :
              for ( var i = 0; i < 5; i++ ) {
                alert( 'a' );
                b :
                    for ( var j = 0; j < 5; j++ ) {
                      if ( i == 2 ) {
                          continue a;
                      }
                    }
              }
      }
                      
    5. var
      1. 用 var 很方便的就是可以不限制變數的型態.
        function test() {
            for ( var i = 0, b = 'a', c = true; i < 5; i++, b+='b', c = !c ) {
                alert( 'i=' + i + ',b=' + b + ',c=' + c );
            }
        }
                                
      2. 用 var 宣告的屬性無法 delete
        i = 1;
        var j = 0;
        function test() {
            alert( 'i' in window ); // true
            alert( 'j' in window ); // true
            delete i;
            delete j;
            alert( 'i' in window ); // false
            alert( 'j' in window ); // true
        }
                                
    6. function
      1. function 通常定義在程式碼頂層, 或是在 function 內部的頂層. 也就是 function 不能定在 while 或 if 之類的區塊中.
      2. function 在編譯時期就會被 JavaScript 儲存起來, 這會造成一個現象就是如果 function 和變數名稱同名, 而引用該名稱的點又早於變數名稱的宣告位置, 則早於變數名稱宣告的引用點會看到 function, 而變數名稱宣告後的引用點會看到新宣告變數的內容
        function test() {
          alert( r ); // 顯示 function 的內容
          var r = '2';
          alert( r ); // 顯示 2
          function r() {
              return '1';
          }
          alert( r ); // 顯示 2
        }
                                
    7. try/catch/finally
      1. 丟出例外的時候, 如果沒找到相對應的 catch 就會一直往外丟到有 catch 或是最外層, 到最外層都還沒有 catch 就會給 user 看到瀏覽器發出的 javascript 錯誤.
        function test() {
            try {
                while( true ) {
                    try {
                        throw new Error( 'something wrong' );
                    } catch (error) {
                        alert( 'inner error : name=' + error.name + ', message=' + error.message );
                        break;
                    } finally {
                        alert( 'inner finally' );
                    }
                }
            } catch (error) {
                alert( 'outer error : name=' + error.name + ', message=' + error.message );
            } finally {
                alert( 'outer finally' );
            }
        }
                                
      2. 如果 finally 的時候執行 return, break, continue 等動作, 程式就會進入新的狀態.
        function test() {
          for ( var i = 0; i < 10; i++ ) {
            try {
                alert( 'i = ' + i );
                if ( i < 5 ) {
                    throw new Error( ' eight ' );
                }
            } catch (error) {
                alert( error );
            } finally {
                // 因為這行所以只會 alert 一次還有一次 error
                break; 
            }
          }
        }
                                

    星期六 十一月 15, 2008

    JavaScript - variable, operator and operand

    Learning JavaScript

    reference

    JavaScript 大全 - CH4 CH5

    focal points

    1. Declare
      1. 用 var 宣告變數, 如果宣告兩次其效果和宣告一次再 assign 值一次是一樣的
      2. 如果直接 assign 一個值到變數而沒有用 var 宣告, 比方說直接 abc = 1; 則 JavaScript 會自動替你宣告一個 global variable, 然後再讓你 assign 值
      3. 如果還沒 assign 值給某變數就要讀取變數值就會出錯. 比方說直接呼叫 alert( abc ); 就會有 abc is not defined 的錯誤
      4. JavaScript 區域變數有個特別的地方就是, 就算有同名的 global variable, 在 function 的最後才用 var 宣告某變數, 在宣告前 function 內使用到該變數的話並不會讀到 global variable, 而是會讀到未定義的 local variable
        var v = 'global variable';
        window.onload = function() {
            alert( v ); // undefined
            var v = 'local variable';
            alert( v ); // local variable
        }
        alert( v ); // 先執行, 印出 global variable
                                
    2. GC
      1. 和 Java 一樣, 如果 JavaScript 發現物件已經沒有變數指向物件, 則該物件的記憶體就會被回收
    3. Variable
      1. JavaScript 有 global object 和 local object, 如果你宣告一個 global variable 其實就是把變數宣告在 global object, 如果宣告 local variable 就是把變數宣告在 local object. 那還有情形是物件裡面還有物件, 其實一樣就是會有範圍更小的 local object. 當 function 在存取變數的時候, JavaScript 會從範圍最小的 local object 開始找起. 所以像是 Infinity 或是 parseInt 這些變數和 function 其實都是預先定義在 global object 裡面的東西.
      2. 使用 window 變數和最外層的地方使用 this 都是指定到 global object
      3. JavaScript 有執行環境就是 execution time, global object 有 global object 的執行環境, local object 也有自己的執行環境.
      4. 瀏覽器的不同視框也有自己的執行環境, 這些執行環境又可以互相溝通參考.
    4. 運算子.比較特別的運算子有 :
      + 加法 如果其中一個運算元是字串, 另一個就也會轉字串, 如 alert( 5 + '6' ); 會印出 '56'. 為什麼加號遇到字串就不算數字結果而是字串串起來是因為 + 的字串運算元優先權大於數字運算元, 所以 '1' + '2' = '12'.
      另外 + 運算子是由左到右, 所以 1 + 2 + 'test' = '3test' 而 'test' + 1 + 2 = 'test12'
      = 指定運算子 = 是由右向左的結合性, 總之就是又變的值為主, 目前一直是這樣.
      += 加並指定 += 除了可計算數字以外還可用在字串上
      - * % 減法 乘法 求餘數 如果運算元不是數字就會被轉成數字
      - + 減號加號 放在運算元之前會把運算元轉數字, 如果轉不了會變 NaN
      例如本 alert( +'100' ); 會印出 100, alert( - '100b' ); 會印 NaN
      function TestObj() {
          TestObj.prototype.valueOf = function() {
              return 5;
          }
      }
      window.onload = function() {
          alert( + new TestObj() ); // 印出 5
          alert( - new TestObj() ); // 印出 -5
      }
                                  
      / 除法 因為 JavaScript 都是浮點數, 所以 5 / 2 = 2.5, 5 / 0 = Infinity, 0 / 0 = NaN
      delete 刪除屬性定義
      typeof 傳回資料型態
      function MyObj() {}
      function test() {
        alert( 'number' == typeof(123) ); 
        alert( 'boolean' == typeof(true) );
        alert( 'object' == typeof(new MyObj()) );
        alert( 'object' == typeof(null) );
        alert( 'string' == typeof('teststring') );
        alert( 'function' == typeof(test) );
        alert( 'undefined' == typeof(abc) );
      }
                                  
      new 建立物件 除了可以 new Object() 以外, 最特別的就是如果建構子不用參數, 可以不用括號. 比方說
      function MyObj() {
          MyObj.prototype.toString = function() {
              return 'this is MyObj';
          }
      }
      function test() {
        alert( new MyObj );
      }                                        
                                  
      delete 刪除屬性, 變數或陣列
      1. 如果刪除成功或刪除的屬性不存在或指定的不是屬性陣列變數之ㄧ時會回傳 true, 否則回傳 false
      2. javascript 一些原生的屬性不能刪除, var 宣告的也不能刪除
      3. 刪除的是變數而不是物件, 所以如果 a 和 b 指向同個物件, delete a 並不會影響到 b
      4. var a = 0;
        b = 1;
        function test() {
            alert( a ); // 0
            alert( b ); // 1
            alert( delete a ); // false
            alert( delete b ); // true
            alert( a ); // 0
            alert( b ); // runtime error because b is not defined
        }                                            
                                        
      void 回傳 undefined 如果需要一個運算式產生額外作用又不希望顯示結果就可以用 void
      <a href='javascript:void window.open();'>Test</a>
                                  
      instanceof 撿查物件型態是否為特定型態 像在 java 一樣使用
      function TestObj() {}
      function test() {
          var a = new TestObj();
          alert( a instanceof TestObj );
      }                                        
                                  
      && AND 左右兩個運算元都要 true 才回傳 true. 如果左邊的運算元為 false 就直接回傳左邊的值, 否則就回傳右邊運算元的值
      alert( 'abc' && new Date() ); // alert new Date().toString()
      alert( 'abc' && null ); // alert null
      alert( undefined && true ); // alert undefined
                                  
      || OR 兩個運算元只要有一個為 true 就回傳 true 的那個
      alert( 'abc' || new Date() ); // alert 'abc'
      alert( 'abc' || null ); // alert 'abc'
      alert( undefined || true ); // alert true
                                  
      | NOT 就是變成布林的相反值, 有個特別的用法就是連續兩個 ! 就可以把值換成原來的布林值
      alert( !!'abc' ); // alert true
                                  
      位元運算子
      1. 位元運算子的數字運算元必須為整數, 因為 JavaScript 使用 32 位元, 不能用浮點數來操作位元
      2. 如果數字超過 32 位元就會變成 NaN
      3. 如果操作位元超過 32 位元, 則小數部份或超過 32 位元的部份會被捨棄掉
      & AND 如果兩個位元都是 1 就回傳 1
      alert( Number(9).toString(2) ); // alert 1001 
      alert( Number(6).toString(2) ); // alert 0110
      alert( Number( 9 & 6 ).toString(2) ); // alert 0
      alert( Number( 9 & 1 ).toString(2) ); // alert 1
                                  
      | OR 只要有一個是 1 結果就是 1
      alert( Number(9).toString(2) ); // alert 1001 
      alert( Number(6).toString(2) ); // alert 0110
      alert( Number(1).toString(2) ); // alert 0001
      alert( Number( 9 | 6 ).toString(2) ); // alert 1111
      alert( Number( 9 | 1 ).toString(2) ); // alert 1001
                                  
      ^ XOR 兩個一樣為 0 不一樣為 1
      alert( Number(9).toString(2) ); // alert 1001 
      alert( Number(6).toString(2) ); // alert 0110
      alert( Number(1).toString(2) ); // alert 0001
      alert( Number( 9 ^ 6 ).toString(2) ); // alert 1111
      alert( Number( 9 ^ 1 ).toString(2) ); // alert 1000
                                  
      ~ NOT 把位元變成相反, 1 -> 0, 0 -> 1.
      alert( Number(9).toString(2) ); // alert 1001 
      alert( Number(6).toString(2) ); // alert 0110
      alert( Number(1).toString(2) ); // alert 0001
      alert( Number( ~9 ).toString(2) ); // alert 0110
      alert( Number( ~6 ).toString(2) ); // alert 1001
      alert( Number( ~1 ).toString(2) ); // alert 1110
                                  
      << 位元左移 移動的位元必須在 0 - 31 個, 如果超出 31 就取 31 的餘數
      alert( Number(9).toString(2) ); // alert 1001 
      alert( Number(6).toString(2) ); // alert 0110
      alert( Number(-10).toString(2) ); // alert -1010
      alert( Number( 9 << 2 ).toString(2) ); // alert 100100
      alert( Number( 6 << 3 ).toString(2) ); // alert 110000
      alert( Number( -10 << 5 ).toString(2) ); // alert -101000000
                                  
      >> 有號位元右移 有號就是說位元往右移, 如果原本的數是正號就補 0, 負號就補 1.
      alert( Number(9).toString(2) ); // alert 1001 
      alert( Number(6).toString(2) ); // alert 0110
      alert( Number(-10).toString(2) ); // alert -1010
      alert( Number( 9 >> 2 ).toString(2) ); // alert 10
      alert( Number( 6 >> 3 ).toString(2) ); // alert 0
      alert( Number( -10 >> 10 ).toString(2) ); // alert -1
                                  
      >>> 無號位元右移 和 >> 一樣, 差別是都補 0
      in 撿查屬性是否存在 覺得這很有用, 可以先檢查有沒有某個屬性再做動作, 很適合 function 對未知型態的參數做動作之前預先處理以防錯誤發生
      function test() {
          var o = { a : 1, b : 2 };
          alert( 'a' in o ); // true
          alert( 'c' in o ); // false
      }                                        
                                  
      == 傳回是否相等
      1. 如果型態不同, === 回傳 false
      2. 如果兩個都是數字或字串或布林值且相同則 === 和 == 為 true
      3. 如果兩個都是 NaN 則 === 和 == 為 false
      4. 如果一個是 null 一個是 undefined, 則 == 為 true 而 === 為 false
      5. 如果兩個都是 null 或 undefined 則 == 和 === 為 true
      6. 如果兩個變數指向同物件, 則 == 和 === 為 true
      7. 如果兩個變數指向不同物件, 即使是相同型態, === 和 == 還是回傳 false (這比較奇怪, 因為原本我以為同物件, 同值的話 == 就會回傳 true. 但測試結果並非如此)
        var a = new String('a');
        var b = new String('a');
        alert( a == b ); // false
        alert( a === b ); // false
                                            
      8. 如果拿字串和數字比, 字串會先轉成數字, 如果轉換過後的數字相同, 則 == 回傳 true 而 === 回傳 false
        var a = new String('123');
        var b = 123;
        alert( a == b ); // true
        alert( a === b ); // false
                                            
        可這裡要注意如果數字也是個物件的話就不能比了
        var a = new String('123');
        var b = new Number(123);
        alert( a == b ); // false
        alert( a === b ); // false
                                            
      9. 如果拿布林值和數字比, 則 true 會先變 1, false 會先變 0. 之後才比
        var a = true;
        var b = 1;
        alert( a == b ); // true
        
        a = new Boolean(true);
        alert( a == b ); // true
        
        b = new Number(1);
        alert( a == b ); // false
                                            
      10. 如果物件和數字或字串做比較, 會先把物件轉成基本型態, 也就是呼叫 valueOf, 如果沒有 valueOf 就呼叫 toString. (只有 Date 物件是先呼叫 toString)
        var a = new A();
        var b = 'test';
        A.prototype.toString = function() {
            return 'test';
        }
        alert( a == b ); // true
        
        b = 5;
        alert( a == b ); // false
        A.prototype.valueOf = function() {
            return 5;
        }
        alert( a == b ); // true
                                            
      11. 使用比較運算子的時候如果用如果和字串或數字比就會先呼叫 valueOf 或 toString 轉成基本型態後才比較
        function A() {
            A.prototype.toString = function() {
                alert( 'calling toString' );
                return 'aa';
            }
            A.prototype.valueOf = function() {
                alert( 'calling valueOf' );
                return 5;
            }
        }
        function test() {
            var a = new A();
            alert( 6 > a ); // call valueOf and return true
            
            alert( 'bb' > a ); // call valueOf and return false
            A.prototype.valueOf = undefined;
            
            alert( 'bb' > a ); // call toString and return true
        }
                                            
      12. 如果比較一個字串與數字, 或是物件轉成字串與數字, 比較運算子就會把字串轉成數字比較, 如果無法轉數字就會變 NaN 最後回傳 false. 如果兩個運算元都不能轉數字或字串就回傳 false. 不過注意 Date 拿來比較的時候會呼叫 valueOf.
        function TestString() {
            TestString.prototype.toString = function() {
                return '124';
            }
        }
        function TestNumber() {
            TestNumber.prototype.valueOf = function() {
                return 123;
            }
        }
        function test() {
            var a = 'aaa';
            var b = 123;
            alert( a > b ); // false because aaa will be NaN
            
            a = '124';
            alert( a > b ); // true
            
            a = new TestString();
            b = new TestNumber();
            alert( a > b ); // 124 > 123 return true
            
            TestString.prototype.valueOf = function() {
                return '122';
            }
            alert( a > b ); // 122 > 123 return false
            
            TestString.prototype.valueOf = function() {
                return 'ab';
            }
            alert( a > b ); // ab => NaN > 123 return false
            
        }
                                            
      === 傳回是否相同

    星期一 十一月 10, 2008

    JavaScript - 基本的資料型態

    Learning JavaScript

    reference

    JavaScript 大全 - CH3

    focal points

    1. 數值 Number
      1. JavaScript 的型態有數值, 文字字串, boolean, Object (又分為以名稱為 key 的集合, 以編號為 key 的集合, 也就是陣列, 以及 function, function 可透過被呼叫執行操作.), Date, RegExp, Error...
      2. JavaScript 沒有整數與浮點數的分別, 所有的數值都是浮點數 (IEEE 754, 64 位元).
      3. 16 進位以 0x 開頭, 如 0xFF 為 255
      4. 浮點數支援指數標記, 就是加上 e 或 E. 如 1.01e5 就是 1.01 * ( 10 的 5 次方), 1.01-5 就是 1.01 * ( 10 的 -5 次方 )
      5. 無限大是 Infinity, 負無限大是 -Infinity, 可用 isFinite() 測試
      6. 不是一個數字是 NaN, 可用 isNaN 測試
      7. 特殊常數有 : Infinity, NaN, Number.MAX_VALUE, Number.MIN_VALUE, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY
    2. 字串 String
      1. JavaScript 用單引號或雙引號包起一段文字就是字串, 要表達字元只要長度為 1 的字串即可
      2. 字串跳脫用反斜線, 比方說可用 \' 來表示單引號的一點, \n 是換行, 還有其他的特殊字元比方說 \x 開頭可表示 Latin-1 的字元如 \xA9 是 ©
      3. 數字碰到字串時會自動轉成字串, 比方說 5 + 'test' = '5test'
      4. 數字要轉字串的方式還可透過 String(5) 或 Number(5).toString()
      5. Number.toString 可以指定基數, 預設為十進位, 也可用 Number(255).toString(16) 轉成 ff 或 Number(5).toString(2) 轉成 101
      6. Number.toFixed 可指定小數點後幾位, 比方說 Number(1.123456789).toFixed(5) 就會轉成 1.12346
      7. Number.toExponential 可轉成基數標記法, 比方說 Number(123456.789).toExponential(2) 轉成 1.23e+5
      8. Number.toPrecision 可指定有意義的數字位數, 如果數字超出指定的位數就會以基數標記法顯示. 比方說 Number(123456.789).toPrecision(7) 是 123456.8, Number(123456.789).toPrecision(5) 則是 1.2346e+5
      9. 除了 + 以外, 兩個數字內容的 -*/ 計算都可以被當成數字計算, 如 '5' + '5' = 55, '5' - '5' = 0, '5' * '5' = 25, '5' / '5' = 1.
      10. 也可透過 '5' - 0 或 Number('5') 把字串轉數字, 如 Number('5') + Number('5') = 10 或 ('5' - 0) + ('5' - 0) = 10
      11. 還可透過 parseInt, parseFloat 轉換成數字
        // 一般的 parseInt, parseFloat 
        alert( parseInt('5 anc') ); // 5, 就是數字之後的單位會被忽略
        alert( parseInt('0xFF') ); // 255 
        alert( parseFloat( '123.45 abcc' ) ); // 123.45 就是數字之後的單位會被忽略
        alert( parseFloat( '1.23e5' ) ); // 123000
        alert( parseFloat('12.23') ); // 12.23
        
        // parseInt 可指定基數 (parseFloat 就不行)
        alert( parseInt('101', 2) ); // 5
        alert( parseInt('ff', 16) ); // 255
        alert( parseInt('0123456', 10) ); // 123456
        
        // 數字之前有非數字字串就不能轉換成功
        alert( parseInt('$100') ); // NaN
        alert( parseFloat('$100') ); // NaN
                                
    3. 布林值 Boolean
      1. 轉換的測試如下
         
            // Boolean to Number                    
            alert( Number(true) ); // 1
            alert( Number(false) ); // 0
        
            // Boolean to String
            alert( String(true) ); // 'true'
            alert( String(false) ); // 'false
        
            // Object to Boolean
            alert( Boolean(new Object()) ); // true
            alert( Boolean(null) ); // false
            alert( Boolean(Number(0)) ); // false
            alert( Boolean(Number(NaN)) ); // false
                                
    4. 函式 function
      1. JavaScript 的 function 不只可以執行, 而且還是資料型態, 所以可以存在變數和陣列或物件中, 還可以當成參數傳遞.
      2. function 宣告方式如下
        // 一般的宣告方式
        function cal(a) {
            return a + a;
        }
        // function literal 或稱 lambda function
        var cal = function(a) {
            return a + a;
        }
        // 用字串組成 function
        var cal = new Function( 'a', 'return a + a' );
                                
    5. 物件 Object
      1. 使用關聯式陣列可以把物件屬性名稱當成陣列的 key 到物件中取值
        var objA = new Object();
        objA.x = 1;
        objA.y = 2;
        alert( objA.x ); // 1
        alert( objA['x'] ); // 1
        alert( objA['y'] ); // 2
                                
      2. 可透過括號和逗號建立新物件, 而且物件的屬性名稱不一定要是識別字, 也可以是字串
        var A = { name : 'A' };
        alert( A.name ); // A
        
        var B = {
            name : 'B',
            children : {
                C : { name : 'C' },
                D : { name : 'D' }
            }
        };
        alert( B.name ); 
        alert( B.children.C.name ); // C
        
        var E = { 
            name : 'E', 
            'F' : { 
                name : 'FF' 
            } 
        };
        alert( E.F.name ); // FF
                                
      3. 物件自動轉成 Boolean 時會變 true, 如果自動轉字串就會呼叫物件的 toString, 物件轉數字的時候會呼叫 valueOf
        var TestO = function() {
            TestO.prototype.toString = function() {
                return 'This is toString of TestO';
            }
            TestO.prototype.valueOf = function() {
                return 100;
            }
        }
        window.onload = function() {
            alert( new TestO() ); // 'This is toString of TestO'
            alert( new TestO() + 50 ); // 150
        }
                                
    6. 陣列 Array
      1. 物件的關聯式陣列用變數名稱當 key, 陣列則是用以 0 開始的 index.
      2. 建立陣列的方式
        var a1 = new Array();
        a1[0] = 'a';
        a1[1] = 1.2;
        a1[2] = true;
        a1[3] = { name : 'a3' }
        a1[5] = 'a5'
        alert( a1[3].name ); // a3
        alert( a1[4] ); // undefined
        
        var a2 = new Array( 'a', 1.2, true, {name : 'a3'} );
        alert( a2[3].name ); // a3
        
        var a3 = new Array(10); // 建立長度 10 的陣列
        var a4 = [ 'a', 1.2, true, {name : 'a4'} ];
        alert( a4[3].name ); // a4
        
        var a5 = [ [1,2,3], ['a','b'], [] ];
        alert( a5[1][0] ); // a
                                
    7. null
      1. 轉 boolean 會變 false, 轉數字是 0, 轉字串是 'null'
    8. undefined
      1. 轉 boolean 會變 false, 轉數字是 NaN, 轉字串是 'undefined'
      2. undefined 是一個未定義的值, 和 null 是不一樣的, 不過透過 == 比較卻會回傳 true, 要用 === 才會回傳 false
        alert( null == undefined ); // true
        alert( null === undefined ); // false
                                
    9. Date
      1. 幾種使用 Date 的方式如下
        alert( new Date() ); // today
        alert( new Date(2008, 10, 17) ); // 2008/11/17
        
        var someD = new Date();
        someD.setFullYear( someD.getFullYear() + 1 );
        alert( someD ); // today add one year
        alert( someD.toLocaleDateString() ); // 顯示區域的時間, 如 2008年11月9日                            
                                
    10. Regular Expression
      1. Regular Expression 宣告透過 / 把 pattern 包起來, 如 /^abc*/
    11. Wrapper
      1. 字串, 布林值與數值分別有 String, Boolean, Number 三個 wrapper, 也各自定義了一些 method
        alert( 'abcdefghijklmnopqrstu'.substr(1, 5) );
        alert( true.toString() );
        alert( 123.45678.toFixed(2) );                            
                                
      2. String, Boolean, Number 都可以透過 new Object 來起始
        alert( new Object('te') + new Object('st') ); // test
        alert( !new Object(true) ); // false
        alert( new Object(1) + new Object(2) ); // 3
                                
      3. 當使用 'abcde'.substr 的時候, 並不是說 'abcde' 是一個字串物件, 而是在基本型態 字串 被呼叫到字串物件的 method 時, JavaScript 自動建立一個暫時的字串物件來使用物件的 method, 不過一旦呼叫完以後這個物件就會被丟棄, 回歸到基本型態(字串). 也就是說, JavaScript 在這裡做了臨時物件的轉換. (原本 String 物件轉成字串使用也有同樣的效果)
      4. 雖然字串和 String 因為 JavaScript 會臨時轉換而沒什麼差別, 但有時還是不一樣, 例如使用 eval 的時候
        var TestObj = function() {
            TestObj.prototype.test = function() {
                alert( 'you are calling test' );
            }
        }
        window.onload = function() {
            var t = new TestObj();
            t.test();
            eval( 'new TestObj().test()' ); // alert
            eval( 't.test()', t ); // alert
            eval( new String('t.test()'), t ); // do nothing..
        }
                                
    12. 轉回基本型態
      1. 轉 boolean : 只要不是空物件, 比方說 null, 則轉成 boolean 就會是 true. 即使是 new Boolean(false) 也一樣
        var b = new Boolean( false );
        alert( b ); // false
        alert( Boolean( b ) ); // true
                                
      2. 轉數字 : 轉換成數字的時候, 會先呼叫 valueOf, 可是如果沒實做 valueOf, 物件會繼承 Object 的 valueOf, 就是回傳物件本身, 但又不會回傳物件的基本值, 所以就會去呼叫 toString.
        function TestObj() {}
        var t = new TestObj();
        alert( 5 - t ); // 沒有實做 valueOf 和 toString, 所以 t 會取得 '[object Object]' 的字串, 5 - '[object Object] 就因為不是數字而印出 'NaN'
        TestObj.prototype.toString = function() {
            return '6';
        }
        alert( 5 - t ); // 實做了 toString, 但沒有 valueOf. 所以 t 會在發現 valueOf 沒有回傳基本值之後去取 toString, 就是實做回傳的 '6'. 因為 '6' 可以直接轉成數字, 所以這行程式就印出 -1
        TestObj.prototype.valueOf = function() {
            return 7;
        }
        alert( 5 - t ); // 實做了 valueOf, 所以直接取得 valueOf 的回傳值就能計算了, 最後印出 -2
                                
      3. 一般而言, 當物件和運算子一起使用的時候, 由於無法判斷該當成哪種基本型態, 字串還是數字來與運算子搭配, 所以會先呼叫物件的 valueOf, 如果 valueOf 有回傳基本型態, 就用該回傳的基本型態來操作, 否則就去呼叫 toString, 以字串來操作. 不過 Date 是個例外, 因為 Date 的 valueOf 與 toString 都有實做, 所以 new Date() + 1 會回傳字串, new Date() - 1 則是回傳數字
      4. 有種現象是當一個物件實做了 toString 與 valueOf, 但是 valueOf 回傳字串, 會導致一旦與運算子搭配的時候遇到加號會無法正卻的計算結果, 如下所示
        window.onload = function() {
            function TestObj() {}
            TestObj.prototype.valueOf = function() {
                return '1';
            }
            TestObj.prototype.toString = function() {
                return '2';
            }
            var t = new TestObj();
            alert( t ); // 直接呼叫 toString -> 2
            alert( 1 + t ); // 加號所以呼叫 valueOf, 但是回傳字串, 變 11
            alert( 1 - t ); // 減號所以呼叫 valueOf, 1 - 1 = 0
            TestObj.prototype.valueOf = function() {
                return 1;
            }
            alert( 1 + t ); // 加號所以呼叫 valueOf, 回傳數字 1 所以結果變 2
        }
                                
      5. valueOf 並非總是回傳數字, valueOf 的定義是要回傳物件的基本值, 所以必要的時候仍要回傳字串
    13. call by value 與 call by reference
      1. 基本型態因為資料量很少所以是 call by value
      2. 物件因為大小很不固定所以用 call by reference
      3. 字串比較特別, 因為字串是基本型態, 但可能會很大. 所以字串設計成不可變動的(也就是說如果產生另一個字串, 就不再是同個 reference), 但傳輸的時候是 call by reference.
      4. call by value 與 call by reference 的例子如下
        function TestObj() {
            this.a = 100;
            this.addA = function( a ) {
                this.a = this.a + a;
            }
            this.getA = function() {
                return this.a;
            }                
        }
        
        function changeInsA( insA ) {
            insA = new TestObj();
            alert( insA.getA() ); // 100
        }
        
        function testCalByRef( insA, valA ) {
            insA.addA( valA );
        }
        
        function testCallByVal( a, b ) {
            a = a + b;
        }
        
        window.onload = function() {
            var a = 0;
            var b = 1;
            testCallByVal(a, b);
            alert( a ); // call by value, 所以 a 沒改變, 仍為 0
        
            var c = new TestObj();
            alert( c.getA() ); // 100
            testCalByRef( c, 5 );
            alert( c.getA() ); // call by reference, 所以 c.a 從 100 變成 105, 也不用拿變數去接
                        
            changeInsA( c );
            alert( c.getA() ); // 雖然在 changeInsA 中傳過去的變數被改成新的 TestObj 但 c 變數的 instance 仍存在, 所以 105 不變
        }                            
                                
      5. 字串物件的比較 : 字串是不可變的, 傳遞雖然當成 reference, 但比較的時候卻是比值而不比參考
        function compareStr( strA, strB ) {
            return strA == strB;
        }
        window.onload = function() {
            var a = new String('hello');
            var b = new String('hell');
            var c = new String('o');
            alert( compareStr( a, b + c ) ); // true
            alert( compareStr( b, a.substr(0, 4) ) ); // true
        }
                                

    星期三 十月 08, 2008

    JavaScript - 非同步發 request

    description

    包含 DOM parse xml 的部份.

    reference

    Head First JavaScript

    codes

    AjaxRequest.js
    function AjaxRequest() {
        var request = null;
        if ( window.XMLHttpRequest ) {
            try {
                request = new XMLHttpRequest();
            } catch (e) {
                request = null;
            }
        } else if ( window.ActiveXObject ) {
            try {
                requet = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                try {
                    request = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) {
                    request = null;
                }
            }
        }        
        
        AjaxRequest.prototype.send = function (type, url, handler, postDataType, postData) {
            if ( request == null ) {
                return;
            }
            request.abort();
            url += (( url.indexOf('?') > -1 ) ? "&" : "?") + "dummy=" + new Date().getTime();
            try {
                request.onreadystatechange =  handler ;
                request.open( type, url, true ); // always asynchronous (true)
                if ( type.toLowerCase() == "get" ) {
                    request.send( null );
                } else {
                    request.setRequestHeader("Content-Type", postDataType);
                    request.send( postData );
                }
            } catch ( e ) {
                alert( "Ajax error communicating with the server.\n" + "Details: " + e );
            }
        }
        
        AjaxRequest.prototype.getReadyState = function () {
            return request.readyState;
        }
        
        AjaxRequest.prototype.getStatus = function () {
            return request.status;
        }
        
        AjaxRequest.prototype.getResponseText = function () {
            return request.responseText;
        }
        
        AjaxRequest.prototype.getResponseXML = function() {
            return request.responseXML;
        }
        
    }
    
    AjaxRequestServlet.java
    ...
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.println(request.getParameter("test"));
        } 
    ...
    
    AjaxRequestXMLServlet.java
    ...
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/xml;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            StringBuffer sb = new StringBuffer();
            sb.append( "" );
            for ( int i = 0; i < 5; i++ ) {
                sb.append( "" );
                sb.append( "" + request.getParameter( "name" ) + i + "" );
                sb.append( "" + request.getParameter( "age" ) + i + "" );
                sb.append( "" );
            }
            sb.append( "" );
            String xmlString = sb.substring(0);
            logger.info( "xmlString=" + xmlString );
            out.println( xmlString );
        } finally { 
            out.close();
        }
    } 
    ...
    
    demo jsp
    <%@page contentType="text/html" pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>JSP Page</title>
            <script type="text/javascript" src="AjaxRequest.js"> </script>
            <script type="text/javascript">
                function testText() {
                    var ajaxRequest = new AjaxRequest();
                    var handler = function() {
                        if ( ajaxRequest.getReadyState() == 4 && ajaxRequest.getStatus() == 200 ) {
                            alert( 'response text = ' + ajaxRequest.getResponseText() );
                        }
                    }
                    var urlString = "${pageContext.servletContext.contextPath}/AjaxRequestServlet";
                    ajaxRequest.send("POST", urlString, handler, "application/x-www-form-urlencoded; charset=UTF-8", "test=" + document.getElementById('personName').value);
                }
                function testXML() {
                    var ajaxRequest = new AjaxRequest();
                    var handler = function() {
                        if ( ajaxRequest.getReadyState() == 4 && ajaxRequest.getStatus() == 200 ) {
                            var responseXML = ajaxRequest.getResponseXML();
                            var names = responseXML.getElementsByTagName('name');
                            var ages  = responseXML.getElementsByTagName('age');
                            var context = document.getElementById('personContexts');
                            clearElmt( context );
                            if ( names.length == ages.length ) {
                                for ( var i = 0; i < names.length; i++ ) {
                                    var nameText = document.createTextNode( elmtToText(names[i]) );
                                    var ageText = document.createTextNode( elmtToText(ages[i]) );
                                    var br = document.createElement("br");
                                    context.appendChild( nameText );
                                    context.appendChild( ageText );
                                    context.appendChild( br );
                                }                            
                            }
                        }
                    }
                    var urlString = "${pageContext.servletContext.contextPath}/AjaxRequestXMLServlet";
                    var params = "name=" + document.getElementById('personName').value;
                    params += "&age=" + document.getElementById('personAge').value;
                    ajaxRequest.send("POST", urlString, handler, "application/x-www-form-urlencoded; charset=UTF-8", params);
                }
                function elmtToText(ele) {
                    var firstChildNodeValue = ele.firstChild.nodeValue;
                    return firstChildNodeValue ? firstChildNodeValue : "";
                }
                function appendElmtsToText(ele) {
                    var result = "";
                    if ( !ele ) { return; }
                    for ( var i = 0; i < ele.childNodes.length; i++ ) {
                        var child = ele.childNodes[i];
                        if ( child.nodeValue ) {
                            result += child.nodeValue;
                        } else {
                            if ( child.childNodes && child.childNodes[0].nodeValue ) {
                                result += child.childNodes[0].nodeValue;
                            }
                        }
                    }
                    return result;
                }
                function clearElmt( elmt ) {
                    if ( elmt == null ) { return; }
                    while ( elmt.firstChild ) {
                        elmt.removeChild( elmt.firstChild );
                    }
                }
            </script>
        </head>
        <body>
            <h2>Hello World 2!</h2>
            <input type="button" value="testText" onclick="testText();"/>
            <input type="button" value="testXML" onclick="testXML();"/>
            <input type="text" id="personName"/>
            <input type="text" id="personAge"/>
            <div id="personContexts"></div>
        </body>
    </html>
    

    星期二 九月 30, 2008

    JavaScript - instance method, class owned instance method, class method

    description

    不記一下馬上就忘了...
    分成 instance method, 就是每個 instance 都會有一份
    class owned instance method, 就是 instance 共用一份還可存取 this instance
    還有 class method, 就是不能存取 this method, 而是要傳 instance 進去.

    reference

    Head First JavaScript

    codes

    <!-- 
        Document   : TestObject
        Created on : 2008/9/29, 上午 02:42:07
        Author     : Administrator
    -->
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <script type="text/javascript">
            // add class owned instance method into standard class
            Date.prototype.toClassOwnedInstanceString = function() {
                return "[DateClassOwnedInstanceString date=" + this.toString();
            }
            // add class owned method into standard class
            Date.toClassOwnedString = function(date) {
                return "class owned string date=" + date;
            }
            function Blog(date, body, imgName) {
                // instance property : all instances have different properties
                this.date = date;
                this.body = body;
                this.imgName = imgName;
                
                // class property : all instances have same class property
                Blog.prototype.prototypeProperty = "prototype property"; 
                
                Blog.classProperty = "classProperty";
                
                // instance method : all instances have different functions (even if same context)
                this.toInstanceString = function() {
                    return "[instanceString date=" + this.date + ",body=" + this.body + ",imgName=" + this.imgName + "]";
                }
                
                // class owned instance method : all instances have same class owned instance method, can access "this" instance
                Blog.prototype.toClassOwnedInstanceString = function() {
                    return "[classOwnedInstanceString date=" + this.date + ",body=" + this.body + ",imgName=" + this.imgName + "]";
                }
                
                // class method : all instances has same class method, can't access "this" instance
                Blog.toClassOwnedString = function(blog) {
                    return "[class-owned-string date=" + blog.date + ",body=" + blog.body + ",imgName=" + imgName + ",classProperty=" + Blog.prototype.classProperty + "]";
                }
                
            }
            window.onload = function() {
                var blog = new Blog(new Date("08/08/2008"), "testbody");
                var blogWithImg = new Blog(new Date("08/09/2007"), "testbodywithImgName", "test.png");
                alert( blog.toInstanceString() );
                alert( blog.toClassOwnedInstanceString() );
                alert( Blog.toClassOwnedString(blog) );
                alert( new Date().toClassOwnedInstanceString() );
                alert( blog.date.toClassOwnedInstanceString() );
                alert( Date.toClassOwnedString(new Date()) );
                alert( blogWithImg.toInstanceString() );
                alert( Blog.prototype.prototypeProperty );
                alert( Blog.classProperty );
            }
        </script>
      </head>
      <body>
          <div></div>
      </body>
    </html>
    

    星期日 九月 28, 2008

    JavaScript - 操作 style, 排序, 自建物件

    description

    少用還真的會忘記, 記錄一下.

    reference

    Head First JavaScript

    codes

    用 DOM - standard
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>test</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <script type="text/javascript">
            function Blog(body, date) {
                this.body = body;
                this.date = date;
            }
            function getBlogs() {
                return [ 
                    new Blog( "Hello1", new Date("08/19/2009") ),
                    new Blog( "Hello1", new Date("08/19/2008") ),
                    new Blog( "Hello1", new Date("08/19/2007") ),
                    new Blog( "Hello1", new Date("08/19/2006") ),
                    new Blog( "Hello1", new Date("08/19/2005") ),
                    new Blog( "Hello2", new Date("09/19/2004") ),
                    new Blog( "Hello3", new Date("10/19/2003") ),
                    new Blog( "Hello4", new Date("11/19/2002") )
                ];
            }
            function showBlog() {
                var blogs = getBlogs();
                blogs.sort(function(blog1, blog2) {
                    return blog1.date - blog2.date;
                });
                var blogBody = document.getElementById('blog');
                for ( var i = 0; i < blogs.length; i++ ) {
                    var bodyPTag = document.createElement("p");
                    if ( i % 2 == 0 ) {
                        bodyPTag.style.backgroundColor = "#EEEEEE";
                    }
                    var strongTag = document.createElement("strong");
                    var dateStr = blogs[i].date.getMonth() + "/" + ( blogs[i].date.getDate() + 1 )+ "/" + blogs[i].date.getFullYear();
                    strongTag.appendChild(document.createTextNode(dateStr) );
                    bodyPTag.appendChild( strongTag );
                    bodyPTag.appendChild( document.createElement("br") );
                    bodyPTag.appendChild( document.createTextNode(blogs[i].body) );
                    blogBody.appendChild( bodyPTag );
                }
            }
            window.onload = function() {
                document.getElementById('showAll').onclick = function() {
                    showBlog();
                }
            }
        </script>    
      </head>
      <body>
          <input type="button" id="showAll" value="Show All Blog Entries"/>
          <div id="blog"></div>
      </body>
    </html>
    
    用 innerHTML - 非 standard
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>test</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <script type="text/javascript">
            function Blog(body, date) {
                this.body = body;
                this.date = date;
            }
            function getBlogs() {
                return [ 
                    new Blog( "Hello1", "08/19/2008" ),
                    new Blog( "Hello2", "09/19/2008" ),
                    new Blog( "Hello3", "10/19/2008" ),
                    new Blog( "Hello4", "11/19/2008" )
                ];
            }
            function showBlog() {
                var blogs = getBlogs();
                var blogBody = "";
                for ( var i = 0; i < blogs.length; i++ ) {
                    if ( i % 2 == 0 ) {
                        blogBody += "<p style='background-color:#EEEEEE'>";
                    } else {
                        blogBody += "<p>";
                    }
                    blogBody += "<strong>" + blogs[i].date + "</strong><br/>" + blogs[i].body + "</p>";
                }
                document.getElementById('blog').innerHTML = blogBody;
            }
            window.onload = function() {
                document.getElementById('showAll').onclick = function() {
                    showBlog();
                }
            }
        </script>    
      </head>
      <body>
          <div id="blog"></div>
          <input type="button" id="showAll" value="Show All Blog Entries"/>
      </body>
    </html>
    

    星期一 九月 22, 2008

    JavaScript - cookie 的 CRUD ( javascript 和 html 分開 )

    基礎基礎...反正就是把 javascript 和 html 分開, 就像是邏輯和資料分開放一樣.

    reference

    Head First JavaScript

    codes

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
        <head>
            <title></title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            <script type="text/javascript">
                var showAllCookie = function(showerObj) {
                    showerObj.value = document.cookie;
                }
                var clearCookies = function(){
                    var cookieAry = document.cookie.split(';');
                    for ( var cookieCnt = 0; cookieCnt < cookieAry.length; cookieCnt++ ) {
                        var cookieKey = cookieAry[cookieCnt].split('=')[0];
                        deleteCookie( cookieKey );
                    }
                }
                var deleteCookie = function( key ) {
                    writeCookie( key, '', -1 );
                }
                var readCookie = function( key ) {
                    var cookieAry = document.cookie.split(';');
                    for ( var cookieCnt = 0; cookieCnt < cookieAry.length; cookieCnt++ ) {
                        var cookieStr = cookieAry[cookieCnt];
                        while ( cookieStr.charAt(0) == ' ' ) {
                            cookieStr = cookieStr.substr(1, cookieStr.length);
                        }
                        if ( cookieStr.indexOf( key + '=', 0) == 0 ) {
                            return cookieStr.substring( (key + '=').length, cookieStr.length );
                        }
                    }
                    return null;
                }
                var writeCookie = function( key, value, days ) {
                    if ( isNaN(days) ) {
                        days = 1;
                    }
                    var expireDate = new Date();
                    expireDate.setTime( expireDate.getTime() + ( days * 24 * 60 * 60 * 1000 ));
                    var expireDateStr = "; expires=" + expireDate.toGMTString();
                    var strToCookie = key + "=" + value + expireDateStr + "; path=/";
                    document.cookie = strToCookie;
                }
                
                window.onload = function(){
                    var cookieKeyObj     = document.getElementById('cookieKey');
                    var cookieValueObj   = document.getElementById('cookieValue');
                    var expireDaysObj    = document.getElementById('expireDays');
                    var writeCookieObj   = document.getElementById('writeCookieBtn');
                    var showAllCookieObj = document.getElementById('showAllCookieBtn');
                    var showerObj        = document.getElementById('shower');
                    var findCookieObj    = document.getElementById('findCookieBtn');
                    var clearCookieObj   = document.getElementById('clearCookieBtn');
                    var deleteCookieObj  = document.getElementById('deleteCookieBtn');
                    writeCookieObj.onclick  = function(){ 
                        writeCookie( cookieKeyObj.value, cookieValueObj.value, expireDaysObj.value )
                        showAllCookie( showerObj );
                    };
                    showAllCookieObj.onclick = function() {
                        showAllCookie( showerObj );
                    };
                    findCookieObj.onclick = function() {
                        showerObj.value = readCookie( cookieKeyObj.value );
                    };
                    clearCookieObj.onclick = function() {
                        clearCookies();
                        showAllCookie( showerObj );
                    };
                    deleteCookieObj.onclick = function() {
                        deleteCookie( cookieKeyObj.value );
                        showAllCookie( showerObj );
                    }
                }
            </script>
        </head>
        <body>
            cookie key :   <input type="text" id="cookieKey"/><br/>
            cookie value : <input type="text" id="cookieValue"><br/>
            Expire days :  <input type="text" id="expireDays"/><br/>
            <input type="button" id="writeCookieBtn"   value="write cookie"/>
            <input type="button" id="showAllCookieBtn" value="show all cookie"/>
            <input type="button" id="findCookieBtn"    value="find cookie"/>
            <input type="button" id="deleteCookieBtn"  value="delete cookie" />
            <input type="button" id="clearCookieBtn"   value="clear cookies" /><br/>
            <textarea cols="50" rows="20" id="shower"></textarea><br/>
        </body>
    </html>
    

    星期六 九月 20, 2008

    JavaScript - cookie 的 CRUD

    呃...我知道這很基礎啦...

    reference

    Head First JavaScript

    codes

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
        <head>
            <title></title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            <script type="text/javascript">
                function clearCookies() {
                    var cookieAry = document.cookie.split(';');
                    for ( var cookieCnt = 0; cookieCnt < cookieAry.length; cookieCnt++ ) {
                        var cookieKey = cookieAry[cookieCnt].split('=')[0];
                        deleteCookie( cookieKey );
                    }
                }
                function deleteCookie( key ) {
                    writeCookie( key, '', -1 );
                }
                function readCookie( key ) {
                    var cookieAry = document.cookie.split(';');
                    for ( var cookieCnt = 0; cookieCnt < cookieAry.length; cookieCnt++ ) {
                        var cookieStr = cookieAry[cookieCnt];
                        while ( cookieStr.charAt(0) == ' ' ) {
                            cookieStr = cookieStr.substr(1, cookieStr.length);
                        }
                        if ( cookieStr.indexOf( key + '=', 0) == 0 ) {
                            return cookieStr.substring( (key + '=').length, cookieStr.length );
                        }
                    }
                    return null;
                }
                function writeCookie( key, value, days ) {
                    if ( isNaN(days) ) {
                        days = 1;
                    }
                    var expireDate = new Date();
                    expireDate.setTime( expireDate.getTime() + ( days * 24 * 60 * 60 * 1000 ));
                    var expireDateStr = "; expires=" + expireDate.toGMTString();
                    var strToCookie = key + "=" + value + expireDateStr + "; path=/";
                    document.cookie = strToCookie;
                }
           </script>
        </head>
        <body>
            cookie key : <input type="text" id="cookieKey"/><br/>
            cookie value : <input type="text" id="cookieValue"><br/>
            Expire days : <input type="text" id="expireDays"/><br/>
            <input type="button" value="write cookie" onclick="writeCookie(document.getElementById('cookieKey').value,document.getElementById('cookieValue').value,document.getElementById('expireDays').value);"/>
            <input type="button" value="show all cookie" onclick="document.getElementById('textArea').value=document.cookie" />
            <input type="button" value="find cookie" onclick="document.getElementById('textArea').value=readCookie(document.getElementById('cookieKey').value);"/>
            <input type="button" value="delete cookie" onclick="deleteCookie( document.getElementById('cookieKey').value );"/>
            <input type="button" value="clear cookies" onclick="clearCookies();"/><br/>
            <textarea cols="50" rows="20" id="textArea"></textarea><br/>
        </body>
    </html>