一切福田,不離方寸,從心而覓,感無不通。

Category Archives: JavaScript

Javascript面向对象编程(二):构造函数的继承

 这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例。 今天要介绍的是,如何生成一个"继承"多个对象的实例。 比如,现在有一个"动物"对象的构造函数, function Animal(){     this.species = "动物";   } 还有一个"猫"对象的构造函数, function Cat(name,color){     this.name = name;     this.color = color;   } 怎样才能使"猫"继承"动物"呢? 1. 构造函数绑定 最简单的方法,大概就是使用call或apply方法,将父对象的构造函数绑定在子对象上,也就是在子对象构造函数中加一行:   function Cat(name,color){     Animal.apply(this, arguments);     this.name = name;     this.color = color;   }   var cat1 = new Cat("大毛","黄色");   alert(cat1.species); // 动物 2. prototype模式 更常见的做法,则是使用prototype属性。 如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。   Cat.prototype = new Animal();   Cat.prototype.constructor = Cat;   var cat1 = new Cat("大毛","黄色");   alert(cat1.species); // 动物 代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。   Cat.prototype = new Animal(); 它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢?   Cat.prototype.constructor = Cat; 原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。也就是说,Cat.prototype 这个对象的constructor属性,是指向Cat的。 我们在前一步已经删除了这个prototype对象原来的值,所以新的prototype对象没有constructor属性,所以我们必须手动加上去,否则后面的"继承链"会出问题。这就是第二行的意思。 总之,这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,   o.prototype = {}; 那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。   o.prototype.constructor = o; 3. 直接继承prototype 由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。 现在,我们先将Animal对象改写:   function […]

龙生   26 Dec 2011
View Details

Javascript 面向对象编程(一):封装

 学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难。因为Javascript的Object模型很独特,和其他语言都不一样,初学者不容易掌握。 下面就是我的学习笔记,希望对大家学习这个部分有所帮助。我主要参考了以下两本书籍: 《面向对象的Javascript》(Object-Oriented JavaScript) 《Javascript高级程序设计(第二版)》(Professional JavaScript for Web Developers, 2nd Edition) 它们都是非常优秀的Javascript读物,推荐阅读。 笔记分成三部分。今天的第一部分是讨论"封装"(Encapsulation),后面的第二部分和第三部分讨论"继承"(Inheritance)。 ============================ Javascript 面向对象编程(一):封装 作者:阮一峰 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。 那么,如果我们要把"属性"(property)和"方法"(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢? 1. 生成对象的原始模式 假定我们把猫看成一个对象,它有"名字"和"颜色"两个属性。   var Cat = {     name : ",     color : "   } 现在,我们需要根据这个原型对象,生成两个实例对象。   var cat1 = {}; // 创建一个空对象     cat1.name = "大毛"; // 按照原型对象的属性赋值     cat1.color = "黄色";   var cat2 = {};     cat2.name = "二毛";     cat2.color = "黑色"; 好了,这就是最简单的封装了。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。 2. 原始模式的改进 我们可以写一个函数,解决代码重复的问题。   function Cat(name,color){     return {       name:name,       color:color     }   } 然后生成实例对象,就等于是在调用函数:   var cat1 = Cat("大毛","黄色");   var cat2 = Cat("二毛","黑色"); 这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。 3. 构造函数模式 为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。 比如,猫的原型对象现在可以这样写,   function Cat(name,color){ […]

龙生   26 Dec 2011
View Details

JavaScript Math 对象的参考手册

Math 对象 Math 对象用于执行数学任务。 使用 Math 的属性和方法的语法:

注释:Math 对象并不像 Date 和 String 那样是对象的类,因此没有构造函数 Math(),像 Math.sin() 这样的函数只是函数,不是某个对象的方法。您无需创建它,通过把 Math 作为对象使用就可以调用其所有属性和方法。 Math 对象属性 FF: Firefox, IE: Internet Explorer 属性 描述 FF IE E 返回算术常量 e,即自然对数的底数(约等于2.718)。 1 3 LN2 返回 2 的自然对数(约等于0.693)。 1 3 LN10 返回 10 的自然对数(约等于2.302)。 1 3 LOG2E 返回以 2 为底的 e 的对数(约等于 1.414)。 1 3 LOG10E 返回以 10 为底的 e 的对数(约等于0.434)。 1 3 PI 返回圆周率(约等于3.14159)。 1 3 SQRT1_2 返回返回 2 的平方根的倒数(约等于 0.707)。 1 3 SQRT2 返回 2 的平方根(约等于 1.414)。 1 3 Math 对象方法 FF: Firefox, IE: Internet […]

龙生   16 Dec 2011
View Details

IE与firefox在Javascript上的区别

 以下以 ie 代替 internet explorer,以 mf 代替 mozzila firefox 1. document.form.item 问题(1)现有问题:现有代码中存在许多 document.formname.item("itemname") 这样的语句,不能在 mf 下运行(2)解决方法:改用 document.formname.elements["elementname"](3)其它参见 2 2. 集合类对象问题(1)现有问题:现有代码中许多集合类对象取用时使用 (),ie 能接受,mf 不能。(2)解决方法:改用 [] 作为下标运算。如:document.forms("formname") 改为 document.forms["formname"]。又如:document.getelementsbyname("inputname")(1) 改为 document.getelementsbyname("inputname")[1](3)其它 3. window.event(1)现有问题:使用 window.event 无法在 mf 上运行(2)解决方法:mf 的 event 只能在事件发生的现场使用,此问题暂无法解决。可以这样变通:原代码(可在ie中运行):<input type="button" name="somebutton" value="提交" onclick="javascript:gotosubmit()"/>…<script language="javascript">function gotosubmit() {…alert(window.event); // use window.event…}</script> 新代码(可在ie和mf中运行):<input type="button" name="somebutton" value="提交" onclick="javascript:gotosubmit(event)"/>…<script language="javascript">function gotosubmit(evt) {evt = evt ? evt : (window.event ? window.event : null);…alert(evt); // use evt…}</script>此外,如果新代码中第一行不改,与老代码一样的话(即 gotosubmit 调用没有给参数),则仍然只能在ie中运行,但不会出错。所以,这种方案 tpl 部分仍与老代码兼容。 4. html 对象的 id 作为对象名的问题(1)现有问题在 ie 中,html 对象的 id 可以作为 document 的下属对象变量名直接使用。在 mf 中不能。(2)解决方法用 getelementbyid("idname") […]

龙生   16 Dec 2011
View Details

给firefox添加ie方法和属性

 <!--if(window.Event){// 修正Event的DOM    /*                                IE5        MacIE5        Mozilla        Konqueror2.2        Opera5    event                        yes        yes            yes            yes                    yes    event.returnValue            yes        yes            no            no                    no    event.cancelBubble            yes        yes            no            no                    no    event.srcElement            yes        yes            no            no                    no    event.fromElement            yes        yes            no            no                    no        */    Event.prototype.__defineSetter__("returnValue",function(b){//         if(!b)this.preventDefault();        return b;        });    Event.prototype.__defineSetter__("cancelBubble",function(b){// 设置或者检索当前事件句柄的层次冒泡        if(b)this.stopPropagation();        return b;        });    Event.prototype.__defineGetter__("srcElement",function(){        var node=this.target;        while(node.nodeType!=1)node=node.parentNode;        return node;        });    Event.prototype.__defineGetter__("fromElement",function(){// 返回鼠标移出的源节点        var node;        if(this.type=="mouseover")            node=this.relatedTarget;        else if(this.type=="mouseout")            node=this.target;        if(!node)return;        while(node.nodeType!=1)node=node.parentNode;        return node;        });    Event.prototype.__defineGetter__("toElement",function(){// 返回鼠标移入的源节点        var node;        if(this.type=="mouseout")            node=this.relatedTarget;        else if(this.type=="mouseover")            node=this.target;        if(!node)return;        while(node.nodeType!=1)node=node.parentNode;        return node;        });    Event.prototype.__defineGetter__("offsetX",function(){        return this.layerX;        });    Event.prototype.__defineGetter__("offsetY",function(){        return this.layerY;        });    }if(window.Document){// 修正Document的DOM    /*                                IE5        MacIE5        Mozilla        Konqueror2.2        Opera5    document.documentElement    yes        yes            yes            yes                    no    document.activeElement        yes        null        no            no                    no        */    }if(window.Node){// 修正Node的DOM    /*                                IE5        MacIE5        Mozilla        Konqueror2.2        Opera5    Node.contains                yes        yes            no            no                    yes    Node.replaceNode            yes        no            no            no                    no    Node.removeNode                yes        no            no            no                    no    Node.children                yes        yes            no            no                    no    Node.hasChildNodes            yes        yes            yes            yes                    no    Node.childNodes                yes        yes            yes            yes                    no    Node.swapNode                yes        no            no            no                    no    Node.currentStyle            yes        yes            no            no                    no        */    Node.prototype.replaceNode=function(Node){// 替换指定节点        this.parentNode.replaceChild(Node,this);        }    Node.prototype.removeNode=function(removeChildren){// 删除指定节点        if(removeChildren)            return this.parentNode.removeChild(this);        else{            var range=document.createRange();            range.selectNodeContents(this);            return this.parentNode.replaceChild(range.extractContents(),this);            }        }    Node.prototype.swapNode=function(Node){// 交换节点        var nextSibling=this.nextSibling;        var parentNode=this.parentNode;        node.parentNode.replaceChild(this,Node);        parentNode.insertBefore(node,nextSibling);        }    }if(window.HTMLElement){    HTMLElement.prototype.__defineGetter__("all",function(){        var a=this.getElementsByTagName("*");        var node=this;        a.tags=function(sTagName){            return node.getElementsByTagName(sTagName);            }        return a;        });    HTMLElement.prototype.__defineGetter__("parentElement",function(){        if(this.parentNode==this.ownerDocument)return null;        return this.parentNode;        });    HTMLElement.prototype.__defineGetter__("children",function(){        var tmp=[];        var j=0;        var n;        for(var i=0;i<this.childNodes.length;i++){            n=this.childNodes[i];            if(n.nodeType==1){                tmp[j++]=n;                if(n.name){                    if(!tmp[n.name])                        tmp[n.name]=[];                    tmp[n.name][tmp[n.name].length]=n;                    }                if(n.id)                    tmp[n.id]=n;                }            }        return tmp;        });    HTMLElement.prototype.__defineGetter__("currentStyle", function(){        return this.ownerDocument.defaultView.getComputedStyle(this,null);        });    HTMLElement.prototype.__defineSetter__("outerHTML",function(sHTML){        var r=this.ownerDocument.createRange();        r.setStartBefore(this);        var df=r.createContextualFragment(sHTML);        this.parentNode.replaceChild(df,this);        return sHTML;        });    HTMLElement.prototype.__defineGetter__("outerHTML",function(){        var attr;        var attrs=this.attributes;        var str="<"+this.tagName;        for(var i=0;i<attrs.length;i++){            attr=attrs[i];            if(attr.specified)                str+=" "+attr.name+'="'+attr.value+'"';            }        if(!this.canHaveChildren)            return str+">";        return str+">"+this.innerHTML+"</"+this.tagName+">";        });    HTMLElement.prototype.__defineGetter__("canHaveChildren",function(){        switch(this.tagName.toLowerCase()){            case "area":            case "base":            case "basefont":            case "col":            case "frame":            case "hr":            case "img":            case "br":            case "input":            case "isindex":            case "link":            case "meta":            case "param":                return false;            }        return true;        });     HTMLElement.prototype.__defineSetter__("innerText",function(sText){        var parsedText=document.createTextNode(sText);        this.innerHTML=parsedText;        return parsedText;        });    HTMLElement.prototype.__defineGetter__("innerText",function(){        var r=this.ownerDocument.createRange();        r.selectNodeContents(this);        return r.toString();        });    HTMLElement.prototype.__defineSetter__("outerText",function(sText){        var parsedText=document.createTextNode(sText);        this.outerHTML=parsedText;        return parsedText;        });    HTMLElement.prototype.__defineGetter__("outerText",function(){        var r=this.ownerDocument.createRange();        r.selectNodeContents(this);        return r.toString();        });    HTMLElement.prototype.attachEvent=function(sType,fHandler){        var shortTypeName=sType.replace(/on/,"");        fHandler._ieEmuEventHandler=function(e){            window.event=e;            return fHandler();            }        this.addEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);        }    HTMLElement.prototype.detachEvent=function(sType,fHandler){        var shortTypeName=sType.replace(/on/,"");        if(typeof(fHandler._ieEmuEventHandler)=="function")            this.removeEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);        else            this.removeEventListener(shortTypeName,fHandler,true);        }    HTMLElement.prototype.contains=function(Node){// 是否包含某节点        do if(Node==this)return true;        while(Node=Node.parentNode);        return false;        }    HTMLElement.prototype.insertAdjacentElement=function(where,parsedNode){        switch(where){            case "beforeBegin":                this.parentNode.insertBefore(parsedNode,this);                break;            case "afterBegin":                this.insertBefore(parsedNode,this.firstChild);                break;            case "beforeEnd":                this.appendChild(parsedNode);                break;            case "afterEnd":                if(this.nextSibling)                    this.parentNode.insertBefore(parsedNode,this.nextSibling);                else                    this.parentNode.appendChild(parsedNode);                break;            }        }    HTMLElement.prototype.insertAdjacentHTML=function(where,htmlStr){        var r=this.ownerDocument.createRange();        r.setStartBefore(this);        var parsedHTML=r.createContextualFragment(htmlStr);        this.insertAdjacentElement(where,parsedHTML);        }    HTMLElement.prototype.insertAdjacentText=function(where,txtStr){        var parsedText=document.createTextNode(txtStr);        this.insertAdjacentElement(where,parsedText);        }    HTMLElement.prototype.attachEvent=function(sType,fHandler){        var shortTypeName=sType.replace(/on/,"");        fHandler._ieEmuEventHandler=function(e){            window.event=e;            return fHandler();            }        this.addEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);        }    HTMLElement.prototype.detachEvent=function(sType,fHandler){        var shortTypeName=sType.replace(/on/,"");        if(typeof(fHandler._ieEmuEventHandler)=="function")            this.removeEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);        else            this.removeEventListener(shortTypeName,fHandler,true);        }    }//--></script>?> 

龙生   16 Dec 2011
View Details

firefox下window.event的解决方法

 在FireFox下编写事件处理函数是很麻烦的事. 因为FireFox并没有 window.event . 如果要得到 event 对象,就必须要声明时间处理函数的第一个参数为event. 所以为了兼容IE与FireFox,一般的事件处理方法为:btn.onclick=handle_btn_click;function handle_btn_click(evt){    if(evt==null)evt=window.event;//IE    //处理事件.}对于简单的程序,这不算麻烦. 但对于一些复杂的程序,某写函数根本就不是直接与事件挂钩的.如果要把event传进该参数,那么所有的方法都要把event传来传去..这简直就是噩梦. 下面介绍一个解决这个麻烦事的方法,与原理. JScript中,函数的调用是有一个 func.caller 这个属性的.例如 function A(){    B();}function B(){    alert(B.caller);}如果B被A调用,那么B.caller就是A 另外,函数有一个arguments属性. 这个属性可以遍历函数当前执行的参数:function myalert(){    var arr=[];    for(var i=0;i        arr[i]=myalert.arguments[i];    alert(arr.join("-"));}alert("hello","world",1,2,3)就能显示 hello-world-1-2-3(arguments的个数与调用方有关,而与函数的参数定义没有任何关系) 根据这两个属性,我们可以得到第一个函数的event对象:btn.onclick=handle_click;function handle_click(){    showcontent();}function showcontent(){    var evt=SearchEvent();    if(evt&&evt.shiftKey)//如果是基于事件的调用,并且shift被按下        window.open(global_helpurl);    else        location.href=global_helpurl;}function SearchEvent(){    func=SearchEvent.caller;    while(func!=null)    {        var arg0=func.arguments[0];        if(arg0)        {            if(arg0.constructor==Event) // 如果就是event 对象                return arg0;        }        func=func.caller;    }    return null;}这个例子使用了SearchEvent来搜索event对象. 其中 'Event' 是 FireFox 的 event.constructor .在该例子运行时,SearchEvent.caller就是showcontent,但是showcontent.arguments[0]是空.所以 func=func.caller 时,func变为handle_click .handle_click 被 FireFox 调用, 虽然没有定义参数,但是被调用时,第一个参数就是event,所以handle_click.arguments[0]就是event ! 针对上面的知识,我们可以结合 prototype.__defineGetter__ 来实现 window.event 在 FireFox 下的实现: 下面给出一个简单的代码.. 有兴趣的可以补充 if(window.addEventListener){ FixPrototypeForGecko();}function FixPrototypeForGecko(){ HTMLElement.prototype.__defineGetter__("runtimeStyle",element_prototype_get_runtimeStyle); window.constructor.prototype.__defineGetter__("event",window_prototype_get_event); Event.prototype.__defineGetter__("srcElement",event_prototype_get_srcElement);}function element_prototype_get_runtimeStyle(){ //return style instead… return […]

龙生   16 Dec 2011
View Details

window.event对象详细介绍

 1、event代表事件的状态,例如触发event对象的元素、鼠标的位置及状态、按下的键等等。event对象只在事件发生的过程中才有效。event的某些属性只对特定的事件有意义。比如,fromElement 和 toElement 属性只对 onmouseover 和 onmouseout 事件有意义。  2、属性: altKey, button, cancelBubble, clientX, clientY, ctrlKey, fromElement, keyCode, offsetX, offsetY, propertyName, returnValue, screenX, screenY, shiftKey, srcElement, srcFilter, toElement, type, x, y   3、属性详细说明: 属性名 描述 值 说明 altKey 检查alt键的状态 当alt键按下时,值为True否则为False 只读 shiftKey 检查shift键的状态 当shift键按下时,值为True否则为False 只读 ctrlKey 检查ctrl键的状态 当ctrl键按下时,值为True否则为False 只读 例:(点击按钮时显示那几个特殊键按下的状态) <input type="button" value="点击" onClick="showState()"/> <script> function show(){  alert("altKey:"+window.event.altKey   +"\nshiftKey:"+window.event.shiftKey   +"\nctrlKey:"+window.event.ctrlKey); }</script>  keyCode  检测键盘事件相对应的内码    可读写,可以是任何一个Unicode键盘内码。如果没有引发键盘事件,则该值为0 例:(按回车键让下一组件得到焦点,相当按Tab键) <input type="text" onKeyDown="nextBlur()"/> <input type="text"/> <script> function nextBlur(){  if(window.event.keyCode==13)//回车键的 code   window.event.keyCode=9;//Tab键的code } </script>  srcElement  返回触发事件的元素  Object  只读 例:(点击按钮时显示按钮的name值) <input type="button" value="闽" name="福建" onClick="show()"/> […]

龙生   16 Dec 2011
View Details

JavaScript判断浏览器类型及版本

 你知道世界上有多少种浏览器吗?除了我们熟知的IE, Firefox, Opera, Safari四大浏览器之外,世界上还有近百种浏览器。        几天前,浏览器家族有刚诞生了一位小王子,就是Google推出的Chrome浏览器。由于Chrome出生名门,尽管他还是个小家伙,没有人敢小看他。以后,咱们常说浏览器的“四大才子”就得改称为“五朵金花”了。        在网站前端开发中,浏览器兼容性问题本已让我们手忙脚乱,Chrome的出世不知道又要给我们添多少乱子。浏览器兼容性是前端开发框架要解决的第一个问题,要解决兼容性问题就得首先准确判断出浏览器的类型及其版本。        JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本。JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一种是通过分析浏览器的userAgent属性来判断的。在许多情况下,值判断出浏览器类型之后,还需判断浏览器版本才能处理兼容性问题,而判断浏览器的版本一般只能通过分析浏览器的userAgent才能知道。        我们先来分析一下各种浏览器的特征及其userAgent。        IE       只有IE支持创建ActiveX控件,因此她有一个其他浏览器没有的东西,就是ActiveXObject函数。只要判断window对象存在ActiveXObject函数,就可以明确判断出当前浏览器是IE。而IE各个版本典型的userAgent如下:          Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)        Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)        Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)        Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)       其中,版本号是MSIE之后的数字。        Firefox        Firefox中的DOM元素都有一个getBoxObjectFor函数,用来获取该DOM元素的位置和大小(IE对应的中是getBoundingClientRect函数)。这是Firefox独有的,判断它即可知道是当前浏览器是Firefox。Firefox几个版本的userAgent大致如下:         Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1        Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070309 Firefox/2.0.0.3        Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12      其中,版本号是Firefox之后的数字。        Opera        Opera提供了专门的浏览器标志,就是window.opera属性。Opera典型的userAgent如下:          Opera/9.27 (Windows NT 5.2; U; zh-cn)        […]

龙生   14 Dec 2011
View Details

通过user-Agent获取浏览器和操作系统信息

 下面是我整理的一些userAgent大家先看看格式 //Firefox 1.0.XMozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050225 Firefox/1.0.1Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.8) Gecko/20050511 Firefox/1.0.4Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.10) Gecko/20050716 Firefox/1.0.6Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7//Firefox 1.XMozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20051111 Firefox/1.5Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-TW; rv:1.8) Gecko/20060109 Firefox/1.5 (pigfoot) Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2Mozilla/5.0 (Windows; U; […]

龙生   14 Dec 2011
View Details