星期日, 9月 10, 2006

Aptana Javascript(Object Oriented)備忘記

JavaScript 起源於 1995 年, 主要目的是為了減輕當時 Server-side Perl 的 validation 問題.
Netscape Navigator 改良後並稱它為 JavaScript,
目的是減輕服務器端的工作, 並提供客戶端更多功能.


由於微軟出現另一套 IE VBScript, 使得客戶端 Script 不相容.
後來 ECMA (ECMA-262)及 ISO/IEC (ISO/IEC-16262) 標準化為 ECMAScript.


JavaScript 主要由三部份組成:
ECMASCript 核心:描述語法及基本 objects
DOM (Docucment Object Model):描述網頁工作內容的方法及介面
BOM (Browser Object Model):描述瀏覽器互動的方法及介面



開始備忘記
[1] 安裝 Aptana
[2] 建立第一個 Aptana project
[3] 使用 zInherit library 實作上例
[4] DHTML 繪圖(Bar Chart)
[5] 分析百度 scrolling text
[6] 實作 Mouse Over Block
[7] 實作 Sorting Table:
[8] 其他 Javascript 常用實例


[1] 安裝 Aptana:
下載 Aptana_IDE_Setup_Windows_Next.zip
http://www.aptana.org/download_windows.php
http://www.web20.com/downloads/next/Windows/Aptana_IDE_Setup_Windows_Next.zip
解壓縮至 D:\aptana
點擊 D:\aptana\aptana.exe 開啟 aptana


[2] 建立第一個 Aptana project:
Aptana: File -> Project... -> Simple -> Project 按 Next
Project name:FirstAptanaTest 按 Finish


右鍵點擊 FirstAptanaTest -> New -> HTML File
Container:/FirstAptanaTest
File name:InheritanceTest.html


<!----------------- InheritanceTest.html ----------------->
<script>
function Polygon(iSides) {
this.sides = iSides;

if (typeof Polygon._initialized == "undefined") { // dynamic prototyping
Polygon.prototype.getArea = function () {
return 0;
};
Polygon._initialized = true;
}
}


function Triangle(iBase, iHeight) {
Polygon.call(this, 3); // method 1. Object masquerading inheritance
// Polygon.apply(this, new Array(3)); // method 2
// this.pointer = Polygon; // method 3.
// this.pointer(3);
// delete this.pointer;
this.base = iBase;
this.height = iHeight;
}
Triangle.prototype = new Polygon(); // Prototype chaining inheritance
Triangle.prototype.getArea = function () {
return 0.5 * this.base * this.height;
};


function Rectangle(iLength, iWidth) {
Polygon.call(this, 4);
this.length = iLength;
this.width = iWidth;

if (typeof Rectangle._initialized == "undefined") { // dynamic prototyping
//Rectangle.prototype = new Polygon();
// 為甚麼沒有將上面與 Polygon 的 prototype chaining 放在這裡?
// 由於 prototype 在 物件 new 時才產生關聯.
// 所以當 Rectangle 物件初始化時已經與預設的 prototype object 發生關聯,
// 故第一個 Rectangle 物件初始化並沒有關聯至 Polygon,
// 只有在第二個 Rectangle 物件之後的初始化才開始發生關聯


Rectangle.prototype.getArea = function () {
return this.length * this.width;
};
Rectangle._initialized = true;
}
}
Rectangle.prototype = new Polygon();

function Area(polygon){
if(polygon instanceof Polygon){
return polygon.getArea();
}
return "Invalid class!";
}

var triangle = new Triangle(3, 4);
var rectangle = new Rectangle(5, 6);


document.write("Failed:" + Area("blah") + "<br/>");
document.write("Triable area:" + Area(triangle) + "<br/>");
document.write("Rectangle area:" + Area(rectangle) + "<br/>");
</script>
<!----------------- InheritanceTest.html ----------------->


按 code panel 左下角按 preview , 輸出為
Failed:Invalid class!
Triable area:6
Rectangle area:30


如下圖所示


在這個例題裡還未能解決 多繼承 及 dynamic prototyping 的問題.
下面將使用 zInherit library 解決上面兩個問題.



[3] 使用 zInherit library 實作上例.
下載 zInherit1.0.zip
http://www.nczonline.net/downloads/
http://www.nczonline.net/downloads/zInherit1.0.zip

解壓後將 zInherit\src\zinherit.js 拖放進 FirstAptanaTest project 裡
內容為:
/*----------------- zinherit.js ----------------- */
/*------------------------------------------------------------------------------
* JavaScript zInherit Library
* Version 1.0
* by Nicholas C. Zakas,
http://www.nczonline.net/
* Copyright (c) 2004-2005 Nicholas C. Zakas. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*------------------------------------------------------------------------------
*/

/**
* Inherits properties and methods from the given class.
* @scope public
* @param fnClass The constructor function to inherit from.
*/
Object.prototype.inheritFrom = function (fnClass /*: Function */) /*:void*/ {


/**
* Inherits all classes going up the inheritance chain recursively.
* @param fnClass The class to inherit from.
* @param arrClasses The array of classes to build up.
* @scope private
*/

function inheritClasses(fnClass /*:Function*/,
arrClasses /*:Array*/) /*:void*/ {

arrClasses.push(fnClass);


if (typeof fnClass.__superclasses__ == "object") {
for (var i=0; i < fnClass.__superclasses__.length; i++){
inheritClasses(fnClass.__superclasses__[i], arrClasses);
}
}
}

if (typeof this.constructor.__superclasses__ == "undefined") {
this.constructor.__superclasses__ = new Array();
}

inheritClasses(fnClass, this.constructor.__superclasses__);

for (prop in fnClass.prototype) {
if (typeof fnClass.prototype[prop] == "function") {
this[prop] = fnClass.prototype[prop];
}
}
};


/**
* Determines if the given object is an instance of a given class.
* This method is necessary because using
{@link #inheritFrom} renders
* the JavaScript <code>instanceof</code> operator useless.
* @param fnClass The constructor function to test.
* @return True if the object is an instance of the class, false if not.
* @scope public
*/

Object.prototype.instanceOf = function (fnClass /*:Function*/) /*: boolean */ {


if (this.constructor == fnClass) {
return true;
} else if (typeof this.constructor.__superclasses__ == "object") {
for (var i=0; i < this.constructor.__superclasses__.length; i++) {
if (this.constructor.__superclasses__[i] == fnClass) {
return true;
}
}
return false;
} else {
return false;
}
};
/*----------------- zinherit.js ----------------- */


右鍵點擊 FirstAptanaTest -> New -> HTML File
Container:/FirstAptanaTest
File name:InheritanceTest2.html


<!----------------- InheritanceTest2.html ----------------->
<script src="zinherit.js"></script>
<script>
function Polygon(iSides) {
this.sides = iSides;

if (typeof Polygon._initialized == "undefined") { // dynamic prototyping
Polygon.prototype.getArea = function () {
return 0;
};
Polygon._initialized = true;
}
}


function Triangle(iBase, iHeight) {
Polygon.call(this, 3); // method 1. Object masquerading inheritance
// Polygon.apply(this, new Array(3)); // method 2
// this.pointer = Polygon; // method 3.
// this.pointer(3);
// delete this.pointer;
this.base = iBase;
this.height = iHeight;
}
Triangle.prototype.inheritFrom(Polygon);
Triangle.prototype.getArea = function () {
return 0.5 * this.base * this.height;
};


function Rectangle(iLength, iWidth) {
Polygon.call(this, 4);
this.length = iLength;
this.width = iWidth;

if (typeof Rectangle._initialized == "undefined") { // dynamic prototyping
Rectangle.prototype.inheritFrom(Polygon);
Rectangle.prototype.getArea = function () {
return this.length * this.width;
};
Rectangle._initialized = true;
}
}

function Area(polygon){
if(polygon.instanceOf(Polygon)){
return polygon.getArea();
}
return "Invalid class!";
}

var triangle = new Triangle(3, 4);
var rectangle = new Rectangle(5, 6);


document.write("Failed:" + Area("blah") + "<br/>");
document.write("Triable area:" + Area(triangle) + "<br/>");
document.write("Rectangle area:" + Area(rectangle) + "<br/>");
</script>
<!----------------- InheritanceTest2.html ----------------->



按 code panel 左下角按 preview , 輸出為
Failed:Invalid class!
Triable area:6
Rectangle area:30


輸出結果與上圖一樣.


[4] DHTML 繪圖(Bar Chart).
右鍵點擊 FirstAptanaTest -> New -> HTML File
Container:/FirstAptanaTest
File name:BarChartTest.html


<!----------------- BarChartTest.html ----------------->
<html>
<script type="text/javascript">

function BarChart(datas, width, height) {
var chart = document.createElement("div"); // 建立繪畫邊框 element

chart.style.position = "relative"; // 設定為使用相對路徑
chart.style.width = width + "px"; // 設定邊框的寬度
chart.style.height = height + "px"; // 設定邊框的高度
chart.style.border = "solid red 1px"; // 設定 border 的顏色及大小
chart.style.backgroundColor = "white"; // 設定背景顏色

var barwidth = Math.floor(width/datas.length); // 計算每條 bar 的寬度
var maxdata = Math.max.apply(this, datas); // 計算出最大的資料
var scale = height/maxdata; // 計算邊框高度與最大資料的比率

for(var i = 0; i < datas.length; i++) { // 繪畫每條 bar
var bar = document.createElement("div"); // 建立繪畫 bar 的 element
var barheight = datas[i] * scale; // Compute height of bar


bar.style.position = "absolute"; // 設定為使用相對路徑
bar.style.left = (barwidth*i)+"px"; // 設定每條 bar 與左面的位置
bar.style.top = height-barheight+"px"; // 設定每條 bar 與上面的位置
bar.style.width = (barwidth-2) + "px"; // 設定 bar 寬度, -2 作為間隔
bar.style.height = (barheight) + "px"; // 設定 bar 高度
bar.style.backgroundColor = "blue"; // 設定 bar 顏色


chart.appendChild(bar); // 將每個 bar element 加入到邊框 element 裡
}
document.body.appendChild(chart); // 將邊框 element 加入到 body 裡
return chart;


}
window.onload = function(){ // 設定當頁面 load 完後呼叫 BarChart function
BarChart([120,50,90,150,130,220,400], 400, 200);
}
</script>


<body></body>
</html>
<!----------------- BarChartTest.html ----------------->


按 code panel 左下角按 preview
輸出如下圖所示



[5] 分析百度 scrolling text:
右鍵點擊 FirstAptanaTest -> New -> HTML File
Container:/FirstAptanaTest
File name:ScollingTest.html


<!----------------- ScollingTest.html ----------------->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5" />


<script>
// 這個 scolling test 是參考百度
function startmarquee(lh,speed,delay) {
var p = false;
var t;
var o = document.getElementById("marqueebox");
o.innerHTML += o.innerHTML; // 重覆加入相同的內容, 防止重新轉動時顯示空白
o.style.marginTop=0;
o.onmouseover=function(){p=true;} // 當 mouse over 時停止轉動
o.onmouseout=function(){p=false;} // 當 mouse out 時啟動轉動
function start(){
t=setInterval(scrolling,speed); // 排程執行 scrolling ,間隔為 speed
if(!p){ // 這裡判斷是否要轉動
o.style.marginTop = parseInt(o.style.marginTop)-1+"px";
// 這裡作用是每次轉動 lh pixel 時, 作用是使其值在開始時不等於 lh 的倍數
}
}
function scrolling(){
if(parseInt(o.style.marginTop)%lh != 0){ // 判斷 marginTop 是否等於負 lh 的數值時
o.style.marginTop = parseInt(o.style.marginTop)-1+"px";
// 這裡將 marginTop 減 1, 向上移動一個 pixel
if(Math.abs(parseInt(o.style.marginTop)) >= o.scrollHeight/2){
o.style.marginTop=0;
}
}else{ // 如果 marginTop 等於負 lh pixel 時, 則停頓 delay 微秒
clearInterval(t); // 當轉動 lh pixel 時, 清除排程
setTimeout(start,delay);
}
}
setTimeout(start,delay);
}


function StringBuffer() {
this._strings = new Array;
}
StringBuffer.prototype.append = function (str) {
this._strings.push(str);
};
StringBuffer.prototype.toString = function () {
return this._strings.join("");
};


window.onload = function(){
var sb = new StringBuffer();
sb.append('<a href="
http://google.com/" target=_blank>Google</a><br/>');
sb.append('<a href="
http://yahoo.com/" target="_blank">Yahoo</a><br/>');
sb.append('<a href="
http://msn.com/" target="_blank">MSN</a><br/>');
document.getElementById("marqueebox").innerHTML = sb.toString();
// 將顯示的 html 加入至 div 裡
startmarquee(25,20,3000);
}
</script>


</head>
<body>
<div style="overflow:hidden;height:25px;line-height:25px">
<div id="marqueebox"></div>
</div>
</body>
</html>
<!----------------- ScollingTest.html ----------------->


按 code panel 左下角按 preview
輸出為 Goodle, Yahoo, MSN 在轉動



[6] 實作 Mouse Over Block:
右鍵點擊 FirstAptanaTest -> New -> HTML File
Container:/FirstAptanaTest
File name:MouseOverBlockTest.html


<!----------------- MouseOverBlockTest.html ----------------->
<html>
<head>
<title>Mouse Over Block Test</title>
<script type="text/javascript">
window.onload = function(){
var oBlock = document.createElement("div"); // 建立新的 div element
oBlock.setAttribute("id","block"); // 在 div element 裡加入 id attribute
oBlock.style.visibility = "hidden";
oBlock.style.position = "absolute";
oBlock.style.backgroundColor = "yellow";
oBlock.innerHTML = '<span>Show Content</span>';
document.body.appendChild(oBlock); // 將 div element 加入至 body element 裡
}

function showBlock(oEvent) { // 顯示 block content, oEvenet 為觸發事件的 object
var oDiv = document.getElementById("block");
oDiv.style.visibility = "visible";
oDiv.style.left = oEvent.clientX + 5;
oDiv.style.top = oEvent.clientY + 5;
}
function hideBlock(oEvent) { // 隱藏 block content
var oDiv = document.getElementById("block");
oDiv.style.visibility = "hidden";
}
</script>
</head>
<body>
<div style="background-color: blue; height: 100px; width: 100px"
onmouseover="showBlock(event)" onmouseout="hideBlock(event)">
<font color="white">MouseOver</font>
</div>
</body>
</html>
<!----------------- MouseOverBlockTest.html ----------------->


按 code panel 左下角按 preview
如下圖所示



[7] 實作 Sorting Table:
右鍵點擊 FirstAptanaTest -> New -> HTML File
Container:/FirstAptanaTest
File name:SortingTableTest.html


<!----------------- SortingTableTest.html ----------------->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5" />
<title>Sorting Table Test</title>


<script>
function generateCompareTRs(iCol) { // iCol 為需要比較的 table column
return function compareTRs(oTR1, oTR2) { // 比較 function 需要給予兩個參數
var sValue1 = oTR1.cells[iCol].firstChild.nodeValue;
var sValue2 = oTR2.cells[iCol].firstChild.nodeValue;
return sValue1.localeCompare(sValue2);
};

}
function sortTable(sTableID, iCol) {
var oTable = document.getElementById(sTableID);
var oTBody = oTable.tBodies[0];
var colDataRows = oTBody.rows;
var aTRs = new Array;
for (var i=0; i < colDataRows.length; i++) {
aTRs[i] = colDataRows[i];
}
if (oTable.sortCol == iCol) { // 當上一次比較的 column 與今次一樣,則逆排序
aTRs.reverse();
} else {
aTRs.sort(generateCompareTRs(iCol));
}
var oFragment = document.createDocumentFragment();
for (var i=0; i < aTRs.length; i++) {
oFragment.appendChild(aTRs[i]); // 將排序後的 tr elements 加入至 fragment 裡
}
oTBody.appendChild(oFragment);
// 將排序後的 fragment 加入 table body
// 由於這裡 fragment 裡的 tr 不是使用 createElement 建立出來, 故不會出現重覆資料
oTable.sortCol = iCol; // 記錄上一次比較的 column
}
</script>


</head>
<body>


<table border="1" id="sortTable">
<thead>
<tr>
<th onclick="sortTable('sortTable', 0)" style="cursor:pointer">Last Name</th>
<th onclick="sortTable('sortTable', 1)" style="cursor:pointer">First Name</th>
</tr>
</thead>
<tbody>
<tr><td>joey</td><td>chan</td></tr>
<tr><td>jane</td><td>law</td></tr>
<tr><td>mary</td><td>Blah</td></tr>
</tbody>
</table>


</body>
</html>
<!----------------- SortingTableTest.html ----------------->


按 code panel 左下角按 preview
如下圖所示



[8] 其他 Javascript 常用實例:
Javascript 的 Array object 裡 沒有 indexOf method, 以下在 Array 裡增加 indexOf method:
Array.prototype.indexOf = function (vItem) {
for (var i=0; i < this.length; i++) {
if (vItem == this[i]) {
return i;
}
}
return -1;
}


Javascript 裡可建立 printTenStar function 來返回十個星號
function printTenStar(){
var str = '';
for(var i=0; i<10; i++){
str += '*';
}
return str;
}

但字串是 immutable, 字串相加是相當耗費資源的.
使用 Array 的 join 可以大效解決這問題, 故建立 StringBuffer 如下所示:
function StringBuffer() {
this._strings = new Array;
}
StringBuffer.prototype.append = function (str) {
this._strings.push(str);
};
StringBuffer.prototype.toString = function () {
return this._strings.join("");
};


function printTenStar(){
var sb = new StringBuffer();
for(var i=0; i<10; i++){
sb.append('*');
}
return sb.toString();
}


Servlet 裡可使用 request.getParameter 取得提交變數,
Javascript 可實作如下所示:
function getParameter(sParameterName, sDefaultValue) {
var sQueryString = window.location.search.substring(1).toLowerCase();
var parameters = new Array();
parameters = sQueryString.split('&');
for(var i = 0; i < parameters.length; i++) {
if (parameters[i].indexOf(sParameterName.toLowerCase())>=0) {
var sParameterValue = new Array();
sParameterValue = parameters[i].split('=');
return sParameterValue[1];
}
}
return sDefaultValue;
}



http://www.nczonline.net/


How to Auto Include a Javascript File
http://www.webreference.com/programming/javascript/mk/


Faster DHTML in 12 Steps
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/perf/dhtmlperf.asp


Brendan Eich: JavaScript 2 and the Future of the Web
http://ajaxian.com/archives/brendan-eich-javascript-2-and-the-future-of-the-web


AJAX: Is your application secure enough?
http://www.darknet.org.uk/2006/04/ajax-is-your-application-secure-enough/


About Native XMLHTTP
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/aboutxmlhttp.asp


XMLHttpRequest Object
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/objects/obj_xmlhttprequest.asp


What You Should Know About AJAX Security: 24 Tutorials
http://www.maxkiesler.com/index.php/weblog/comments/what_you_should_know_about_ajax_security_24_tutorials/


Using Firefox 1.5 caching
http://developer.mozilla.org/en/docs/Using_Firefox_1.5_caching


Making Ajax Work with Screen Readers
http://juicystudio.com/article/making-ajax-work-with-screen-readers.php


Another IE Gotch - Dynamically created Radio Buttons
http://cf-bill.blogspot.com/2006/03/another-ie-gotcha-dynamiclly-created.html


Rethinking JavaScript Objects
http://www.sitepoint.com/article/javascript-objects

2 則留言:

匿名 說...

謝謝,寫得很詳細,很有幫助

匿名 說...

大大的文章都好有用, 雖然我不是完全都看得懂, 感謝分享好文章