from:
http://mckoss.com/jscript/object.htm
Object Oriented Programming in JavaScript
by Mike Koss
January 14, 2006
Introduction
The first version of this paper , written in 2003, had several shortcomings, not the least of which was that the techniques described were specific to Internet Explorer. I've updated and improved on the original, to document the current state of the art, especially in light of the extensive interest in AJAX technology and the increasing adoption of the FireFox browser. All the examples presented here will follow the ECMA language standards and can be applied to Internet Explorer, FireFox, and ActionScript (in Macromedia Flash).
While early adopters of JavaScript used it as a simple scripting engine to create dynamic web pages, modern web designers have come to use more sophisticated object oriented techniques in building their code. I will present here, both the common paradigms used in object oriented JavaScript programming, and also suggest some helper functions that you can use in your code to streamline the process.
It should be noted that the current design of the JavaScript language, did not fully anticipate or fully implement an object oriented system. That is why the subject is somewhat mysterious and there are various implementations of object oriented programming techniques being used on the web today. I will describe what I believe to be the most main-stream and compatible implementation that fits most naturally into the design of the language.
Object Oriented Programming Goals
I assume that the reader has a basic familiarity with JavaScript, function calls, and the basic tenets of object oriented programming. I consider the three primary goals of object oriented programming to be:
- Encapsulation - Support for method calls on a JavaScript object as a member of a Class.
- Polymorphism - The ability for two classes to respond to the same (collection of) methods.
- Inheritance - The ability to define the behavior of one object in terms of another by sub-classing .
Through a series of examples (which, for the curious reader, are actually snippets of live JavaScript code embedded within this page), I will demonstrate how objects can be used in JavaScript and how these object oriented paradigms can be best implemented. I will cover techniques for:
- Defining a Class
- Defining and calling Methods in a Class
- Defining a Sub-Class
- Calling the Super-Class constructor from a Sub-Class
- Overriding Methods of a Super-Class in a Sub-Class
- Calling a Super-Class method from a Sub-Class
Simple Objects
Doug Crockford's book explains of the best parts of the JavaScript language.
-- Mike Koss
The simplest object oriented construct in JavaScript is the built-in Object data type. In JavaScript, objects are implemented as a collection of named properties. Being an interpreted language, JavaScript allows for the creation of any number of properties in an object at any time (unlike C++, properties can be added to an object at any time; they do not have to be pre-defined in an object declaration or constructor).
So, for example, we can create a new object and add several ad-hoc properties to it with the following code:
obj = new Object;
obj.x = 1;
obj.y = 2;
Which creates a JavaScript object which I will represent graphically like this:
obj |
x |
1 |
y |
2 |
Object.prototype |
constructor |
Object |
The left hand column displays the property name of each available property on the object, while the right hand column displays it's value. Note that in addition to the x and y properties that we created, our object has an additional property called constructor that points (in this case) to an internal JavaScript function. I will explain prototype properties, below.
Defining a Class - Object Constructors
A new JavaScript class is defined by creating a simple function. When a function is called with the new operator, the function serves as the constructor for that class. Internally, JavaScript creates an Object , and then calls the constructor function. Inside the constructor, the variable this is initialized to point to the just created Object. This code snippet defines a new class, Foo , and then creates a single object of that class.
function Foo()
{
this.x = 1;
this.y = 2;
}
obj = new Foo;
obj |
x |
1 |
y |
2 |
Foo.prototype |
constructor |
Foo |
Object.prototype |
(constructor) |
Object |
Note that we can now create as many Foo type objects as we want, all of whom will be properly initialized to have their x and y properties set to 1 and 2, respectively.
Prototypes Explained
In JavaScript, each Object can inherit properties from another object, called it's prototype . When evaluating an expression to retrieve a property, JavaScript first looks to see if the property is defined directly in the object. If it is not, it then looks at the object's prototype to see if the property is defined there. This continues up the prototype chain until reaching the root prototype. Each object is associated with a prototype which comes from the constructor function from which it is created.
For example, if we want to create an object, X, from constructor function B, whose prototype chain is: B.prototype, A.prototype, Object.prototype:
We would use the following code:
Object.prototype.inObj = 1;
function A()
{
this.inA = 2;
}
A.prototype.inAProto = 3;
B.prototype = new A; // Hook up A into B's prototype chain
B.prototype.constructor = B;
function B()
{
this.inB = 4;
}
B.prototype.inBProto = 5;
x = new B;
document.write(x.inObj + ', ' + x.inA + ', ' + x.inAProto + ', ' + x.inB + ', ' + x.inBProto);
1, 2, 3, 4, 5
x |
inB |
4 |
B.prototype |
constructor |
B |
inA |
2 |
inBProto |
5 |
A.prototype |
(constructor) |
A |
inAProto |
3 |
Object.prototype |
(constructor) |
Object |
inObj |
1 |
In FireFox and in ActionScript, an object's prototype can be explicitly referenced via the non-standard __proto__ property. But in standard JavaScript a prototype object can only by directly referenced through the object's constructor function object.
Defining and Calling Methods in a Class
JavaScript allows you to assign any function to a property of an object. When you call that function using obj.Function() syntax, it will execute the function with this defined as a reference to the object (just as it was in the constructor).
The standard paradigm for defining methods is to assign functions to a constructor's prototype. That way, all objects created with the constructor automatically inherit the function references via the prototype chain.
function Foo()
{
this.x = 1;
}
Foo.prototype.AddX = function(y) // Define Method
{
this.x += y;
}
obj = new Foo;
obj.AddX(5); // Call Method
obj |
x |
6 |
Foo.prototype |
constructor |
Foo |
AddX |
|
Object.prototype |
(constructor) |
Object |
Polymorphism is achieved by simply having different object classes implement a collection of methods that use the same names. Then, a caller, need just use the correctly named function property to invoke the appropriate function for each object type.
function A()
{
this.x = 1;
}
A.prototype.DoIt = function() // Define Method
{
this.x += 1;
}
function B()
{
this.x = 1;
}
B.prototype.DoIt = function() // Define Method
{
this.x += 2;
}
a = new A;
b = new B;
a.DoIt();
b.DoIt();
document.write(a.x + ', ' + b.x);
2, 3
a |
x |
2 |
A.prototype |
constructor |
A |
DoIt |
|
Object.prototype |
(constructor) |
Object |
b |
x |
3 |
B.prototype |
constructor |
B |
DoIt |
|
Object.prototype |
(constructor) |
Object |
Defining a Sub-Class
The standard paradigm, is to use the prototype chain to implement the inheritance of methods from a super class. Any methods defined on the sub-class will supersede those defined on the super-class.
function A() // Define super class
{
this.x = 1;
}
A.prototype.DoIt = function() // Define Method
{
this.x += 1;
}
B.prototype = new A; // Define sub-class
B.prototype.constructor = B;
function B()
{
A.call(this); // Call super-class constructor (if desired)
this.y = 2;
}
B.prototype.DoIt = function() // Define Method
{
A.prototype.DoIt.call(this); // Call super-class method (if desired)
this.y += 1;
}
b = new B;
document.write((b instanceof A) + ', ' + (b instanceof B) + '
');
b.DoIt();
document.write(b.x + ', ' + b.y);
true, true
2, 3
b |
x |
2 |
y |
3 |
B.prototype |
constructor |
B |
(x) |
1 |
DoIt |
|
A.prototype |
(constructor) |
A |
(DoIt) |
|
Object.prototype |
(constructor) |
Object |
Something to keep in mind is that each time a sub-class is defined, we explicitly call the constructor of the super-class in order to insert it into our prototype chain. So it is important to ensure that no undesirable side-effects will occur when this call is made. Conversely, if the super-class constructor should be called for each instance of every sub-class, code must be explicitly added to the sub-class's constructor to make this call (as is done in the above example).
An Alternate Sub-Classing Paradigm
As an alternate to using the prototype chain, I've developed a method which avoids calling the constructor of a super class when each sub-class is defined. Three methods are added to the Function object:
Function.prototype.DeriveFrom = function (fnSuper) {
var prop;
if (this == fnSuper) {
alert("Error - cannot derive from self");
return;
}
for (prop in fnSuper.prototype) {
if (typeof fnSuper.prototype[prop] == "function" &&
!this.prototype[prop]) {
this.prototype[prop] = fnSuper.prototype[prop];
}
}
this.prototype[fnSuper.StName()] = fnSuper;
}Function.prototype.StName = function () {
var st;
st = this.toString();
st = st.substring(st.indexOf(" ") + 1, st.indexOf("("));
if (st.charAt(0) == "(") {
st = "function ...";
}
return st;
}Function.prototype.Override = function (fnSuper, stMethod) {
this.prototype[fnSuper.StName() + "_" + stMethod] = fnSuper.prototype[stMethod];
}
Repeating the sub-classing example using this new paradigm:
function A() // Define super class
{
this.x = 1;
}
A.prototype.DoIt = function() // Define Method
{
this.x += 1;
}
B.DeriveFrom(A); // Define sub-class
function B()
{
this.A(); // Call super-class constructor (if desired)
this.y = 2;
}
B.Override(A, 'DoIt');
B.prototype.DoIt = function() // Define Method
{
this.A_DoIt(); // Call super-class method (if desired)
this.y += 1;
}
b = new B;
document.write((b instanceof A) + ', ' + (b instanceof B) + '
');
b.DoIt();
document.write(b.x + ', ' + b.y);
false, true
2, 3
b |
x |
2 |
y |
3 |
B.prototype |
constructor |
B |
DoIt |
|
A |
A |
A_DoIt |
|
Object.prototype |
(constructor) |
Object |
Unfortunately, this technique does not allow for the use of the instanceof operator to test for membership of a super-class. But, we have the added benefit that we can derive from more than one super class (multiple inheritance).
Private Members
Amazingly, JavaScript also can support private members in an object. When the constructor is called, variables declared in the function scope of the constructor will actually persist beyond the lifetime of the construction function itself. To access these variables, you need only create local functions within the scope of the constructor. They may reference local variables in the constructor.
function A()
{
var x = 7;
this.GetX = function() { return x;}
this.SetX = function(xT) { x = xT; }
}
obj = new A;
obj2 = new A;
document.write(obj.GetX() + ' ' + obj2.GetX());
obj.SetX(14);
document.write(' ' + obj.GetX() + ' ' + obj2.GetX());
7 7 14 7
obj |
GetX |
|
SetX |
|
A.prototype |
constructor |
A |
Object.prototype |
(constructor) |
Object |
obj2 |
GetX |
|
SetX |
|
A.prototype |
constructor |
A |
Object.prototype |
(constructor) |
Object |
I believe, however, that each instance of an object created in this way, has it's own copy of each local function. The local copy of the function can maintain a copy of the local scope (a closure ) of the constructor. This would be rather inefficient for object classes that construct many instances. Experiments with a single (shared) reference to a function reveal that they can only reference variables from a single instance of the class. Since the benefits of using private members is rather limited in the context of JavaScript (which is already lacking any form of type safety), I would not recommend making extensive use of the private member paradigm.
Update (June 2009): I've developed a simple library for building JavaScript Namespaces (public domain source code) - for a description see my blog .
#disqus_thread { margin-bottom: 10px; } .dsq-brlink { font-size: 10px; } .logo-disqus { font-family: Arial, sans-serif; text-transform: uppercase; font-size: 9px; font-weight: bold; } #dsq-content .dsq-alert-message { background-color:#FDF1D0; border:1px solid #FAD163; line-height: 1.25; padding: 5px 8px; color: #817134; margin: 10px 0; } #dsq-content iframe, #dsq-popup-alert iframe { border: 0; overflow-y: auto; overflow-x: hidden; *overflow-x: visible; width: 100%; background-color: transparent; height: 355px; } #dsq-content iframe.dsq-post-reply, #dsq-popup-alert iframe.dsq-post-reply { height: 300px; } #dsq-content iframe.dsq-post-reply-authenticated, #dsq-popup-alert iframe.dsq-post-reply-authenticated { height: 170px; } #dsq-content #dsq-comments .dsq-header-avatar .dsq-drop-profile { color: #333; font-size: 11px; font-family: Arial, Helvetica, sans-serif; float: left; left: 0px; position: relative; background: #f0f0f0; z-index: 2; border-left: 1px solid #888; border-right: 1px solid #888; border-bottom: 1px solid #888; -moz-border-radius: 0px 5px 5px 5px; -webkit-border-top-right-radius: 5px; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; display: none; min-width: 64px; /* IE6 */ _position: absolute; _top: 32px; _z-index: 9000; } /** * Popup */ .dsq-overlay { display: block; position:absolute; top:0; left:0; width:100%; height:100%; z-index:5000; background-color:#000; -moz-opacity: 0.8; opacity:.80; filter: alpha(opacity=80); } .dsq-overlay[id] { position:fixed; } .dsq-popup h4, .dsq-popup ul, .dsq-popup li, .dsq-popup ol, .dsq-popup div, .dsq-popup table, .dsq-popup td, .dsq-popup th, .dsq-popup p, .dsq-popup a, .dsq-popup cite, .dsq-popup img { border: 0; padding: 0; margin: 0; float: none; text-indent: 0; background: none; } .dsq-popup table { border-collapse: separate; border-spacing: 0; } .dsq-popup { font-size: 13px; color: #333; display: none; position: absolute; z-index: 9999; padding: 0; border: 0; text-align: left; font-family: Arial, Helvetica, sans-serif; } .dsq-popup[id] { position: fixed; } .dsq-popup img { max-width: none; } .dsq-popup ul, .dsq-popup li, .dsq-popup ol { list-style-type: none; list-style-image: none; background: none; display: block; } .dsq-popup li:before { content: ""; } .dsq-popup p, .dsq-popup ul { color: #333; line-height: 1.22em; } .dsq-popup a { color: #1C5392; } .dsq-popup a:hover { color: #869AAD; } .dsq-popup .dsq-popup-top { position: relative; text-align: right; width: 520px; height: 20px; background: transparent url(http://media.disqus.com/images/embed/popup-top.png) no-repeat; } .dsq-popup .dsq-popup-top img { margin: 12px 13px 0 0; *margin: 12px 13px 0 0; } .dsq-popup .dsq-popup-bottom { text-align: right; width: 520px; height: 20px; background: transparent url(http://media.disqus.com/images/embed/popup-bottom.png) no-repeat; } .dsq-popup .powered-by{ font-size: 90%; text-align: right; margin-top: 10px; padding-top: 10px; border-top: 1px solid #ddd; } .dsq-popup .dsq-popup-body div.powered-by a { color: #888; text-decoration:none; } .dsq-popup .dsq-popup-body { width: 520px; background: transparent url(http://media.disqus.com/images/embed/popup-body.png) repeat-y; } .dsq-popup .dsq-popup-body .dsq-popup-header a.dsq-close-link { color:#7aa5d5; position: absolute; top:-8px; right: 5px; text-decoration:none; } .dsq-popup .dsq-popup-body .dsq-popup-header a.dsq-close-link img { width: 23px; height: 24px; border: 0; } .dsq-popup .dsq-subscribe-submit { margin: 0 auto; padding: 0 10px; color: #222; } .dsq-popup .dsq-subscribe-submit input { font-size: 110%; width: 200px; } .dsq-popup p.dsq-popup-notice { padding: 5px; margin: 20px 0 0 0; background-color: #fdf1d0; border: 1px solid #fad163; } /** * New popup */ .dsq-popup .dsq-popup-container { position: relative; } .dsq-popup-container .dsq-popup-b { background:url(http://media.disqus.com/images/facebox/b.png); } .dsq-popup-container .dsq-popup-tl { background:url(http://media.disqus.com/images/facebox/tl.png); } .dsq-popup-container .dsq-popup-tr { background:url(http://media.disqus.com/images/facebox/tr.png); } .dsq-popup-container .dsq-popup-bl { background:url(http://media.disqus.com/images/facebox/bl.png); } .dsq-popup-container .dsq-popup-br { background:url(http://media.disqus.com/images/facebox/br.png); } .dsq-popup-container table { border-collapse: collapse; } .dsq-popup-container td { border-bottom: 0; padding: 0; } .dsq-popup-container .dsq-popup-body { padding: 10px; background: #fff; width: 370px; } .dsq-popup-container .dsq-popup-tl, .dsq-popup-container .dsq-popup-tr, .dsq-popup-container .dsq-popup-bl, .dsq-popup-container .dsq-popup-br { height: 10px; width: 10px; overflow: hidden; padding: 0; } .dsq-popup-container .dsq-popup-title { position: static; text-align: left; background: none; height: auto; width: auto; background-color: #E5EBED; margin: -10px -10px 10px; padding: 10px; border-bottom: 1px solid #2F414B; } .dsq-popup-container .dsq-popup-title img { margin: 0; } .dsq-popup-container .dsq-popup-title h3 { font-size: 18px; font-weight: bold; margin: 0; } .dsq-popup-container h4 { font-size: 14px; font-weight: bold; margin: 0; } /** * Popup : Blacklist */ .dsq-popup .dsq-blacklist-option { padding: 5px 10px; border-bottom: 1px dotted #E5EBED; } /** * Popup : Help */ .dsq-popup ul.dsq-popup-help { margin: 0 10px; } #dsq-popup-message.dsq-popup ul.dsq-popup-help li { margin: 0 0 15px 0; padding: 0 0 10px 0; border-bottom: 1px dotted #E5EBED; } .dsq-popup ul.dsq-popup-help li.dsq-help-otheraccts { font-weight: bold; font-size: 110%; border-bottom-width: 2px; border-bottom-style: solid; } #dsq-popup-message ul.dsq-list-tick li { list-style: none inside url(http://media.disqus.com/images/tick.png) !important; display: list-item; } #dsq-popup-message ul.dsq-list-bluebullet li { list-style: none inside url(http://media.disqus.com/images/bullet_blue.png); display: list-item; } #dsq-popup-message li { margin: 10px 0; padding: 0 10px; border-bottom: 1px dotted #E5EBED; } #dsq-popup-message li li { padding-left: 10px; } /* * Popup : Login */ .dsq-popup iframe#dsq-popup-login { margin-top: 10px; height:310px; width: 100%; } .dsq-popup-login .dsq-popup-content { width: 420px; } /** * Popup : Lightbox : Authenticate */ .dsq-lightbox .dsq-popup-content { width: 550px; } .dsq-lightbox .powered-by { display: none; } .dsq-lightbox img { border: 0 !important; height: auto !important; width: auto !important; } .dsq-lightbox .dsq-lightbox-register-reasons { padding: 0 10px !important; } .dsq-lightbox .dsq-lightbox-register-reasons li { background: url(http://media.disqus.com/images/small-tick.png) no-repeat; padding-left: 12px !important; margin: 0 0 0 6px !important; display: inline !important; font-size: 11px !important; color: #555; } .dsq-lightbox .dsq-lightbox-register-reasons li img { margin-bottom: -1px !important; } .dsq-lightbox .dsq-lightbox-auth-fields { } .dsq-lightbox .dsq-lightbox-submit { height: 1%; overflow: auto; padding: 0 5px; margin-top: 20px; } .dsq-lightbox .dsq-lightbox-auth-post { float: right; padding: 4px; background-color: #EAFFCD; -moz-border-radius: 4px; -webkit-border-radius: 4px; -webkit-border-radisu: 4px; } .dsq-lightbox .dsq-lightbox-auth-skip { float: left; margin-top: 5px; padding: 2px; } .dsq-lightbox .dsq-lightbox-switch-auth { width: 400px; font-size: 11px; text-align: right; margin: 0 auto; } .dsq-lightbox .dsq-lightbox-auth-fields table { padding: 8px; background-color: #EDFFC9; border: 1px solid #A5C469; -moz-border-radius: 4px; -webkit-border-radius: 4px; -webkit-border-radius: 4px; width: 400px; margin: 10px auto; } .dsq-lightbox .dsq-lightbox-auth-fields table td { padding: 4px; } .dsq-lightbox .errorlist { color: #c03000 !important; font-size: 11px; font-weight: bold; margin: 0; padding: 0; } .dsq-lightbox .errorlist li { margin: 5px 0 !important; padding: 0 !important; } .dsq-lightbox-errors p { color: #c03000 !important; font-size: 11px; font-weight: bold; } .dsq-lightbox .dsq-lightbox-auth-fields table td input { font-size: 13px; padding: 2px; } .dsq-lightbox-recognized table { width: 400px; margin: 10px auto; } .dsq-lightbox-recognized table td { padding: 4px; } /* * Popup : Profile */ .dsq-popup-profile .dsq-popup-content { width: 500px; } .dsq-popup-profile .dsq-popup-title { height: 1%; overflow: auto; } .dsq-popup-profile .dsq-popup-title td { vertical-align: top; } .dsq-popup-profile .dsq-popup-title img.dsq-popup-profile-avatar { width: 48px; height: 48px; } .dsq-popup-profile .dsq-popup-profile-user { padding: 0 10px; } .dsq-popup-profile .dsq-popup-profile-user h3 { font-size: 20px; font-weight: bold; margin: 0; } .dsq-popup-profile .dsq-popup-profile-user-stats span { margin-right: 10px; } .dsq-popup-profile .dsq-popup-profile-user-stats big { font-weight: bold; font-size: 16px; } .dsq-popup-profile .dsq-popup-profile-state { clear: both; padding: 15px; background-color: #f0f0f0; border-bottom: 1px solid #aaa; height: auto; overflow: hidden; margin: -10px -10px 0px; border-bottom: 1px solid #B2B2B2; } .dsq-popup-profile .dsq-popup-profile-status { padding: 10px; margin: 0 -10px 0; background-color: #EEF9FD; border-bottom: 1px solid #9FCDE3; } .dsq-popup-profile .dsq-popup-profile-snapshot { padding: 15px 0; } .dsq-popup-profile .dsq-popup-profile-snapshot table { width: 100%; } .dsq-popup-profile .dsq-popup-profile-snapshot table td { width: 50%; vertical-align: top; } .dsq-popup-profile .dsq-popup-profile-snapshot table td h4 { margin-bottom: 5px; } .dsq-popup-profile .dsq-popup-profile-snapshot table td ul { margin-bottom: 15px; max-height: 200px; overflow: auto; } .dsq-popup-profile .dsq-popup-profile-snapshot table td ul img { vertical-align: text-top; margin-right: 5px; } .dsq-popup-profile .dsq-popup-profile-snapshot table td ul li { margin-bottom: 5px; } .dsq-popup-profile .dsq-popup-profile-snapshot table td ul li a { font-size: 12px; text-decoration: none; text-transform: capitalize; } .dsq-popup .dsq-popup-body div.show-more{ padding-left: 10px; font-size: 95%; color:#7aa5d5; } .dsq-popup .dsq-popup-body .dsq-popup-body-padding { padding: 0 10px; font-size: 13px; } .dsq-popup .dsq-popup-body .dsq-popup-header { background-color: #e5ebed; padding: 0 10px; position: relative; padding-bottom: 10px; border-bottom: 1px solid #445460; } .dsq-popup .dsq-popup-body .dsq-popup-header img { border: 1px solid #fff; width: 32px; height: 32px; vertical-align: middle; } .dsq-popup .dsq-popup-body .dsq-popup-body-padding cite { margin-left: 5px; /* top: 8px; position: absolute;*/ font-style: normal; vertical-align: middle; } .dsq-popup .dsq-popup .dsq-popup-body .dsq-popup-body-padding cite { position: static; margin: 0; } .dsq-popup .dsq-popup-body .dsq-popup-body-padding cite span { font-weight: bold; font-size: 150%; font-style: normal; margin-right: 10px; vertical-align: middle; } .dsq-popup .dsq-popup-body .dsq-popup-body-padding .dsq-popuplink { margin: 0 0 0 5px; font-size: 90%; } .dsq-popup .dsq-clout { float: left; width: 72px; height: 32px; line-height: 32px; background: url('http://media.disqus.com/images/embed/clout-background.png') no-repeat top left; } .dsq-popup .dsq-clout.unverified { background: url('http://media.disqus.com/images/embed/unverified-background.png') no-repeat top left; line-height: 24px; } .dsq-popup .dsq-clout a{ float: left; width: 100%; text-align: center; color: #FFF; font-size: 16px; font-weight: bold; text-decoration: none; } .dsq-popup .dsq-clout.unverified a{ font-size: 11px; font-weight: normal; } .dsq-popup .dsq-clout a:hover { color: #fff; } .dsq-popup .dsq-profile-services { padding: 10px; background-color: #f0f0f0; border-bottom: 1px solid #aaa; height: auto; overflow: hidden; } .dsq-popup .dsq-profile-services .dsq-profile-userlvl { padding-bottom: 10px; margin-bottom: 15px; border-bottom: 1px solid #ddd; } .dsq-popup .dsq-profile-services span.dsq-profile-ontheweb { float: left; font-family: Trebuchet MS, Trebuchet, Verdana, sans-serif; font-size: 95%; color: #aaa; } .dsq-popup .dsq-profile-services ul { margin-left: 15px; display: inline; } .dsq-popup .dsq-profile-services ul li{ display: inline; margin-right: 15px; } .dsq-popup .dsq-profile-services ul li.dsq-service-labeltxt{ margin: 0; } .dsq-popup .dsq-profile-services ul li.dsq-service-labeltxt{ margin: 0; } .dsq-popup .dsq-profile-services span.dsq-services-description { font-size: 85%; color: #555; position: absolute; top: 25px; left: 5px; display: none; white-space: nowrap; } .dsq-popup .dsq-profile-services img { border:2px solid #fff; } .dsq-popup a.dsq-profile-follow { color:#7aa5d5; } .dsq-popup .dsq-profile-status, .dsq-popup .dsq-profile-recentcomments { clear: both; padding: 10px; } .dsq-popup .dsq-profile-status p, .dsq-popup .dsq-profile-recentcomments p{ padding: 0; } .dsq-popup .dsq-profile-status h4, .dsq-popup .dsq-profile-recentcomments h4 { font-size: 110%; border-bottom: 2px solid #D7DBDD; margin-bottom: 10px; } .dsq-popup .dsq-profile-status h4 span, .dsq-popup .dsq-profile-recentcomments h4 span { background-color: #D7DBDD; padding: 2px 5px; color: #555; font-weight: bold; } .dsq-popup p.dsq-profile-label { font-family: Trebuchet MS, Trebuchet, Verdana, sans-serif; font-size: 95%; color: #aaa; } .dsq-popup ul.dsq-profile-commentlist { margin-top: 10px; } .dsq-popup .dsq-profile-commentlist li{ padding: 5px 0; border-bottom: 2px solid #e9ebed; } .dsq-popup .dsq-profile-commentlist li img.avatar-small { width: 24px; height: 24px; border: 1px solid #DDD; float: left; } .dsq-popup .dsq-profile-commentlist li .comment-message { margin-left: 30px !important; *float: left !important; *margin-left: 5px !important; } .dsq-popup .dsq-profile-commentlist li span.comment-meta { clear: both !important; margin-left: 30px !important; display: block !important; font-size: 90% !important; background: none !important; float: none !important; width: auto !important; } .dsq-popup .dsq-profile-commentlist span{ color: #666; font-size: 95%; } /** * Menu */ .dsq-menu{ margin: 0 !important; left: 0px; position: absolute; _position: absolute; _left:; background: #f0f0f0 !important; z-index: 2 !important; border-width: 1px !important; border-color: #888 !important; border-style: solid !important; -moz-border-radius: 0px 0px 4px 4px; -webkit-border-bottom-right-radius: 4px; -webkit-border-bottom-left-radius: 4px; display: inline; padding: 5px 10px 5px 0 !important; list-style: none !important; } .dsq-menu li{ margin: 0 !important; padding: 0 !important; clear: both; line-height: 1.3em !important; font-size: 12px !important; margin-bottom: 2px !important; margin-left: 4px !important; white-space: nowrap !important; list-style: none !important; float: none; background: none; text-align: left; } .dsq-menu li:before { content: ""; } .dsq-menu li a.dsq-admin-toggle { font-weight: bold; } .dsq-menu li.dsq-admin-email, .dsq-menu li.dsq-admin-ip { color: #555; font-style: italic; cursor: default; } .dsq-menu li.dsq-menu-sep { border-bottom: 1px dotted #aaa; font-size: 1pt !important; } .dsq-menu li a{ text-decoration: none; color: #333; } .dsq-menu li a:hover { color: #869AAD; } /** * Drop profile */ #dsq-content #dsq-comments .dsq-header-avatar:hover .dsq-drop-profile { display: inline; } #dsq-content #dsq-comments .dsq-drop-profile li{ float: none; clear: both; line-height: 1.3em; padding: 2px 4px; white-space: nowrap; } #dsq-content #dsq-comments .dsq-drop-profile li img { float: none; height: 12px; width: 12px; margin: 0px 4px 0px 0; vertical-align: middle; } #dsq-content #dsq-comments .dsq-drop-profile li a { color: #1C5392; vertical-align: middle; } #dsq-content #dsq-comments .dsq-drop-profile li a:hover { font-weight: bold; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-services { height: 16px; padding: 4px 4px; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-services:hover { background-color: #ddd; outline-top: 1px solid #ccc; outline-bottom: 1px solid #ccc; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-services a { display: block; width: 100%; height: 16px; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-sep { border-bottom: 1px dotted #aaa; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-showlnk { padding: 4px 2px; border-bottom: 1px solid #ccc; text-align: center; height: 16px; -moz-border-radius: 0px 5px 0px 0px; -webkit-border-top-right-radius: 5px; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-showlnk a { font-weight: bold; font-size: 95%; display: block; width: 100%; height: 16px; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-badge { padding: 6px 2px; text-align: center; font-size: 95%; cursor: help; border-bottom: 1px solid #ddd; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-badge span { padding: 1px 3px; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-more { height:16px; background: url(http://media.disqus.com/images/drop-more.gif); background-position: 0px 0px; background-repeat: repeat-x; background-color: #fff; text-align: center; border-top: 1px solid #ddd; -moz-border-radius: 0px 0px 5px 5px; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-more:hover { background-position: 0px -20px; border-top: 1px solid #ccc; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-more a { font-size: 95%; height: 16px; width: 100%; display: block; } #dsq-content #dsq-comments .dsq-drop-profile li.dsq-drop-hidden { display: none; padding: 0; } /*----- Classic Theme -----*/ #disqus_thread #dsq-content iframe.dsq-post-video { height: 370px; overflow: hidden; } #dsq-content #dsq-comments .dsq-header-avatar img{ float: left; margin: 4px; width: 32px; height: 32px; } #dsq-content #dsq-comments .dsq-comment-body{ padding-top: 5px; } #dsq-content #dsq-comments .dsq-header-avatar { width: 32px; height: 34px; height: 43px; width: 40px; } #dsq-content #dsq-comments .dsq-comment:hover .dsq-header-avatar{ background: url('http://media.disqus.com/images/embed/avatar-frame-32.png') no-repeat top left; } #dsq-content #dsq-comments .dsq-comment-header { background: url('http://media.disqus.com/images/embed/header-grey.png') repeat-x; height: 30px; line-height: 30px; height: 38px; line-height: 38px; } #dsq-content { font-size: 13px; } img.dsq-record-img { border: 0; padding: 0; margin: 0; float: none; text-indent: 0; background: none; vertical-align: text-bottom; } a.dsq-brlink { font-size: 10px; color: #666; text-decoration: none; font-family: Trebuchet MS, Trebuchet, Verdana, Arial, sans-serif; } span.disqus { font-family: Trebuchet MS, Trebuchet, Verdana, Arial, sans-serif; font-size: 90%; text-transform: uppercase; color: #64747d; } span.logo-disqus { font-family: Trebuchet MS, Trebuchet, Verdana, Arial, sans-serif; font-size: 95%; text-transform: uppercase; font-weight: bold; } span.logo-disqus.color { color: #64747d; } span.logo-disq { font-family: Trebuchet MS, Trebuchet, Verdana, Arial, sans-serif; font-size: 95%; text-transform: uppercase; color: #64747d; font-weight: bold; } span.logo-us { font-family: Trebuchet MS, Trebuchet, Verdana, Arial, sans-serif; font-size: 95%; text-transform: uppercase; color: #ff9300; font-weight: bold; } span.dsq-downtri { font-size: 70%; } a.dsq-help { color: inherit; text-decoration: none !important; border-color: inherit !important; border-bottom-width: 1px !important; border-bottom-style: dotted !important; cursor: help !important; } #dsq-content #dsq-comments .dsq-editedtxt { margin-top: 15px; font-style: italic; font-size: 85%; opacity: .80; filter: alpha(opacity=80); } #dsq-content #dsq-comments .dsq-likedtxt { font-size: 85%; text-align: right; opacity: .80; filter: alpha(opacity=80); } #dsq-content small { font-size: .7em; } #disqus_thread #dsq-content iframe.dsq-post-reply, #dsq-popup-alert iframe.dsq-post-reply { height: 300px; } #disqus_thread #dsq-content iframe.dsq-post-reply-authenticated, #dsq-popup-alert iframe.dsq-post-reply-authenticated { height: 170px; } .clearfix:after { content:"."; display: block; height: 0; clear: both; visibility: hidden; } #dsq-content #dsq-comments img.icon-adjust { margin-bottom: -3px; } #dsq-content #dsq-comments img.dsq-mod-star { vertical-align: middle; _float: left; _margin-top: 5px; } #disqus_thread #dsq-content ul, #disqus_thread #dsq-content li, #disqus_thread #dsq-content ol, #disqus_thread #dsq-content cite, #disqus_thread #dsq-content img, /* dsq-content */ #dsq-content #dsq-comments ul, #dsq-content #dsq-comments li, #dsq-content #dsq-comments ol, #dsq-content #dsq-comments div, #dsq-content #dsq-comments p, #dsq-content #dsq-comments a, #dsq-content #dsq-comments cite, #dsq-content #dsq-comments img { border: 0; padding: 0; margin: 0; float: none; text-indent: 0; background: none; } #disqus_thread #dsq-content cite, #dsq-content #dsq-comments cite { font-style: normal; } #dsq-content #dsq-comments img { max-width: none; } #disqus_thread #dsq-content li, #disqus_thread #dsq-content ul, #disqus_thread #dsq-content ol, #dsq-content #dsq-extra-links li, #dsq-content #dsq-comments ul, #dsq-content #dsq-comments li, #dsq-content #dsq-comments ol { list-style-type: none; list-style-image: none; background: none; display: block; } #dsq-content #dsq-extra-links li:before, #dsq-content #dsq-comments li:before { content: ""; } #dsq-content #dsq-comments { width: 100%; list-style-type: none; padding: 0; border: 0; } #dsq-content #dsq-comments .dsq-remove-message { color: #555; list-style-type: none; margin: 10px 0; padding: 5px; border: 1px solid #c03000; background-color: #FDDFD0; } #dsq-content #dsq-comments .dsq-comment-alert, #dsq-content #dsq-alerts p { font-weight: bold; color: #555; margin: 15px 0; padding: 5px; background-color: #fdf1d0; border: 1px solid #fad163; } #dsq-content #dsq-comments .dsq-comment-header .dsq-hl-up { background-color: #92C72A; color: #fff; margin:0pt 5px; padding:0 2px; } #dsq-content #dsq-comments .dsq-comment-header .dsq-hl-down { background-color: #c03000; color: #fff; margin:0pt 5px; padding:0 2px; } #dsq-content #dsq-comments .dsq-hl-anchor { background-color: #ffff99 !important; color: #000 !important; } #dsq-content #dsq-comments .dsq-hl-anchor p { color: #000 !important; } #dsq-content #dsq-comments .dsq-hl-anchor a { color: #000 !important; } #dsq-content h3#dsq-comments-count { width: 99%; } /** * Misc Items */ #dsq-content .dsq-item-feed a { text-decoration: none; } /** * Auth Header */ #dsq-content #dsq-auth .dsq-auth-header{ margin-bottom: 10px; width: 99%; } #dsq-content #dsq-auth .dsq-authenticated { margin-bottom: 10px; display: none; height: auto; overflow: hidden; } #dsq-content #dsq-auth .dsq-authenticated-pic { float: left; } #dsq-content #dsq-auth .dsq-authenticated-pic img { height: 48px; width: 48px; } #dsq-content #dsq-auth .dsq-authenticated-info { float: left; } #dsq-content #dsq-auth .dsq-authenticated-info ul { padding: 0 0 0 5px; margin: 0; list-style-type:none; } #dsq-content #dsq-auth .dsq-authenticated-info ul li { margin-bottom: 5px; } #dsq-content #dsq-auth .dsq-authenticated-info ul li.logout { font-size: 0.8em; } #dsq-content #dsq-auth .dsq-authenticated-info ul li.logout a { text-decoration: none; } #dsq-content #dsq-auth .dsq-authenticated-info ul li.logout img { margin-bottom: -2px; } #dsq-content h3 { margin: 10px 0; } #disqus_thread #dsq-content h3 { font-weight: bold; } #dsq-content #dsq-auth.dsq-auth-bottom { margin-top: 20px; } #dsq-content .dsq-by { float: right; } #dsq-content #dsq-login { float: none; } #dsq-content #dsq-login .dsq-login-message { margin-bottom: 10px; } #dsq-content #dsq-comments .dsq-login-icon { margin-bottom: -2px; } #dsq-content .dsq-auth-header img, #dsq-content #dsq-options-toggle img { border: 0; margin: 0; padding: 0; max-width: none; float: none; } #dsq-content #dsq-options { margin-bottom: 20px; } #dsq-options .dsq-extra-meta { background: transparent url('http://media.disqus.com/images/embed/transp-line-10.png') repeat-x top center; margin-top:10px; padding-top:10px; } #dsq-extra-links { margin-top: 15px; font-size: 90%; } #dsq-extra-links img { margin-bottom: -3px; } /** * Reply bar */ #dsq-content #dsq-comments .dsq-reply-bar { margin: 15px 0; } #dsq-content #dsq-comments .dsq-reply-bar img { margin-bottom: -2px; } #dsq-content #dsq-comments a.dsq-reply-req-opt { font-size: 100%; margin-right: 5px; opacity: .80; filter: alpha(opacity=80); } #dsq-content #dsq-comments .dsq-reply-bar-items { float: right; } #dsq-content #dsq-comments .dsq-reply-bar-auth { font-size: .9em; } #dsq-content #dsq-comments .dsq-reply-bar-auth .dsq-no-anon-msg { margin: 5px 0 2px 0; } #dsq-content #dsq-comments .dsq-reply-bar-auth img.dsq-post-avatar { height: 24px; width: 24px; } #dsq-content #dsq-auth ul.dsq-media-items li, #dsq-content #dsq-comments ul.dsq-media-items li { display: inline; font-size: .90em; } /** * Pagination */ #dsq-content #dsq-pagination { margin: 20px 0; } #dsq-content #dsq-pagination a { font-weight: bold; } #dsq-content #dsq-pagination a, #dsq-content #dsq-pagination span { margin-right: 5px; } #dsq-content #dsq-pagination span.dsq-paginate-ellipsis { font-size: 110%; } #dsq-content #dsq-pagination a.dsq-paginate-arrows span { margin: 0; padding: 0; font-family: Lucida Sans Unicode, sans-serif; } #dsq-content .dsq-paginate-append-text { display: inline; } #dsq-content .dsq-paginate-append-button { display: none; } /** * Badges */ span.dsq-badge { font-family: Optima, Lucida Grande, Lucida Sans, Helvetica, Arial, sans-serif; padding: 2px 4px; -moz-border-radius: 0px 5px 0px 5px; -webkit-border-top-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; vertical-align: middle; } span.dsq-badge.dsq-badge-verified { background-color: #92C72A; border: 1px solid #6ca300; color: #fff; } span.dsq-badge.dsq-badge-registered { background-color: #fffe98; border: 1px solid #eae800; color: #000; } span.dsq-badge-verified, span.dsq-badge-registered, span.dsq-badge-guest, span.dsq-badge-facebook, span.dsq-badge-twitter, span.dsq-badge-openid { font-family: Optima, Lucida Grande, Lucida Sans, Helvetica, Arial, sans-serif; padding: 2px 4px; -moz-border-radius: 0px 5px 0px 5px; -webkit-border-top-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; vertical-align: middle; } span.dsq-badge-verified { background-color: #92C72A; border: 1px solid #6ca300; color: #fff; } span.dsq-badge-registered { background-color: #fffe98; border: 1px solid #eae800; color: #000; } span.dsq-badge-guest { background-color: #ddd; border: 1px solid #ccc; color: #333; } span.dsq-badge-facebook { background-color: #46639d; color: #fff; } span.dsq-badge-twitter { background-color: #31ceff; color: #fff; } span.dsq-badge-openid { background-color: #f9f9f9; border: 1px solid #aaa; color: #f7931e; } /** * OpenID */ table.dsq-openid-form { margin-left: 10px; margin-bottom: 20px; } table.dsq-openid-form img { width: 50px; height: 50px; margin-right: 10px; } table.dsq-openid-form td.dsq-openid-submit { padding-top: 5px; text-align: right; } /** * Linkbacks */ #dsq-content span.dsq-item-trackback { margin: 15px 0; } #dsq-content input.dsq-trackback-url { width: 150px; font-size: 10px; color: #666; } #disqus_thread #dsq-content ul#dsq-references li { margin-bottom: 20px; display: list-item; list-style: disc; margin-left: 15px; } #disqus_thread #dsq-content ul#dsq-references cite { margin-bottom: 0px; padding-bottom: 0px; font-weight: bold; } #disqus_thread #dsq-content ul#dsq-references p.dsq-meta { margin-top: 0px; padding-top: 0px; font-size: 95%; } /* Reactions */ #disqus_thread #dsq-content li.dsq-reaction { padding: 0; margin: 0; border: 0; list-style-type: none; margin-left: 0px; color: #000; margin-bottom: 20px; } #disqus_thread #dsq-content li.dsq-reaction:before { content: ""; } #disqus_thread #dsq-content .dsq-reaction-header { background: url('http://media.disqus.com/images/embed/header-grey.png') repeat-x; } #disqus_thread #dsq-content .dsq-reaction-header a { text-decoration: none; } #disqus_thread #dsq-content .dsq-reaction .dsq-header-avatar { position: relative; margin-top: 2px; float: left; height: 32px; width: 32px; } #disqus_thread #dsq-content .dsq-reaction .dsq-header-avatar img { float: left; width: 32px; height: 32px; } #disqus_thread #dsq-content .dsq-reaction .dsq-header-avatar img.dsq-service-icon { width: 12px; height: 12px; position: relative; margin-top: -12px; margin-left: 20px; } #disqus_thread #dsq-content .dsq-reaction-header cite { float: left; font-style: normal; font-weight: bold; margin: 0 5px; line-height: inherit; cursor: pointer; } #disqus_thread #dsq-content .dsq-reaction-header cite a { line-height: inherit; } #disqus_thread #dsq-content .dsq-reaction-header .dsq-header-meta { font-size: 90%; line-height: inherit; } #disqus_thread #dsq-content .dsq-reaction a.dsq-header-time { margin: 0 5px; color: inherit; line-height: inherit; } #disqus_thread #dsq-content .dsq-reaction-body { clear: left; padding-top: 5px; } #disqus_thread #dsq-content .dsq-reaction-footer { opacity: .35; filter: alpha(opacity=35); font-size: 90%; margin: 5px 0 0 0px; } #disqus_thread #dsq-content .dsq-reaction-footer a { color: inherit; text-decoration: none; } #disqus_thread #dsq-content .dsq-reaction-footer a:hover { text-decoration: underline; } #disqus_thread #dsq-content .dsq-reaction-retweets { border-left: solid 5px #666; margin-top: 10px; padding-left: 5px; } #disqus_thread #dsq-content .dsq-service-name { text-transform: capitalize; } #disqus_thread #dsq-content li#dsq-show-more-reactions { text-align: center; } /** * Introduced in Embed 2.0 */ #dsq-content #dsq-comments .dsq-comment-footer { height: auto; overflow: hidden; } #dsq-content #dsq-comments .dsq-comment-options { float: right; list-style: none; } #dsq-content #dsq-comments ul.dsq-list-style li { display: inline; background-image: url(http://media.disqus.com/images/middot.png); _background-image: url(http://media.disqus.com/images/middot.gif); background-repeat: no-repeat; background-position: 0 50%; padding-left: 1.6em; } #dsq-content #dsq-comments ul.dsq-list-style li.dsq-list-first { background: none; padding: 0; } /** * Reactions */ #disqus_thread #dsq-content ul#dsq-reactions li.dsq-reaction { padding: 0; margin: 0; border: 0; list-style-type: none; margin-left: 0px; color: #000; margin-bottom: 20px; } #dsq-content ul#dsq-reactions .dsq-reaction-header { background: url('http://media.disqus.com/images/embed/header-grey.png') repeat-x; height: 38px; line-height: 38px; *overflow-y: hidden; } #dsq-content ul#dsq-reactions .dsq-reaction-header a { text-decoration: none; } #dsq-content ul#dsq-reactions .dsq-header-avatar { position: relative; float: left; margin-top: 2px; height: 35px; width: 40px; } #dsq-content ul#dsq-reactions .dsq-header-avatar img { float: left; width: 32px; height: 32px; } #dsq-content ul#dsq-reactions .dsq-header-avatar img.dsq-service-icon { width: 12px; height: 12px; position: relative; margin-top: -12px; margin-left: 20px; } #dsq-content ul#dsq-reactions .dsq-reaction-header cite { float: left; font-style: normal; font-weight: bold; margin: 0 3px 0 5px; line-height: inherit; cursor: pointer; } #dsq-content ul#dsq-reactions .dsq-reaction-header cite a { line-height: inherit; } #dsq-content ul#dsq-reactions .dsq-reaction-header .dsq-header-meta { font-size: 90%; float: left; line-height: inherit; } #dsq-content ul#dsq-reactions a.dsq-header-time { margin: 0 5px; color: inherit; line-height: inherit; } #dsq-content ul#dsq-reactions .dsq-reaction-body { clear: both; padding-top: 5px; } #dsq-content ul#dsq-reactions .dsq-reaction-footer { font-size: 90%; margin: 5px 0 0 0px; } /*----- Comments (comments-1.css) -----*/ #dsq-content #dsq-comments .dsq-comment { list-style-type: none; padding: 0; margin: 0; border: 0; } #dsq-content #dsq-comments .dsq-comment { margin-bottom: 20px; } #dsq-content #dsq-comments .dsq-comment-rate { float: left; line-height: 1.22em; *margin-top: 15px; } #dsq-content #dsq-comments .dsq-comment-rate a, #dsq-content #dsq-comments .dsq-comment-rate img { border: 0; margin: 0; padding: 0; background-color: transparent; } #dsq-content #dsq-comments .dsq-arrows, #dsq-content #dsq-comments .not-votable .dsq-arrows:hover { opacity: .25; filter: alpha(opacity=25); _width: 16px; _height: 14px; }/ #dsq-content #dsq-comments .dsq-arrows.voted { opacity: .5; filter: alpha(opacity=50); } #dsq-content #dsq-comments .dsq-arrows:hover { opacity: 1; filter: alpha(opacity=100); } #dsq-content #dsq-comments .dsq-arrows img { _width: 16px; _height: 14px; } #disqus_thread #dsq-content #dsq-comments .dsq-header-avatar { position: relative; float: left; margin-top: -2px; } #dsq-content #dsq-comments .dsq-comment.special .dsq-comment-header { background: url('http://media.disqus.com/images/embed/header-blue.png') repeat-x; } #dsq-content #dsq-comments .dsq-comment-header a { text-decoration: none; } #dsq-content #dsq-comments .dsq-comment-header cite { float: left; font-style: normal; font-weight: bold; margin: 0 3px; line-height: inherit; } #dsq-content #dsq-comments .dsq-comment-header cite a { line-height: inherit; } #dsq-content #dsq-comments .dsq-comment-header .dsq-header-meta { font-size: 90%; line-height: inherit; } #dsq-content #dsq-comments a.dsq-header-time{ margin: 0 5px; color: inherit; line-height: inherit; } #dsq-content #dsq-comments span.dsq-header-points{ margin: 0 5px; color: inherit; line-height: inherit; } #dsq-content #dsq-comments .dsq-comment-footer { font-size: 90%; margin: 10px 0 0 0; } #dsq-content #dsq-comments .dsq-footer-alert { text-align: right; } blog comments powered by
References
ECMA-262 (PDF) - ECMAScript Standard Documentation
JavaScript Closures - Detailed (if lengthy) explanation of JavaScript closures, along with efficiency concerns especially with the inability of the garbage collector to clean up function calls in the presence of possible closures.
Add New Comment
66 Comments