TreeLogic
Software Engineering
Coding
Standard: Web Languages
January
14, 2005
01/21/06
Christopher
M. Balz
1
Markup and Style Code
1.1 HTML
Validity:
All html must conform to the xhtml 1.0 Strict or Frameset standard,
and must validate at w3c.org:
http://validator.w3.org/
Files:
To maintain a clean separation between static document structure
elements and interactive gui elements, most or all dynamic html
should only be written dynamically, and should not be present in
static html files.
Whitespace:
See the section on whitespace in JavaScript, below.
1.2 CSS
Validity:
All css must conform to the css-2 standard, and must validate at
w3c.org. Do not check-in any css files before validating them at:
http://jigsaw.w3.org/css-validator/validator-upload.html
Files:
Css files, except ‘skin.css’, must correspond to major
application gui elements. For example, there should be separate files
for user controls/viewport, image-layers, and plate. Only the
necessary css properties should be defined.
The
‘skin.css’ file defines all surface-related properties,
such as border-style, image background, etcetera, for all major
screen elements. The css code in the individual css files extends the
css classes defined in ‘skin.css’. No skin-related css
should be present in the other individual css files.
Whitespace:
See the section on whitespace in JavaScript, below.
1.2.1.1 Inline CSS
Inline css is css in an
html file, or mixed in with a literal html element tag definition
(for example, in a dynamic ‘write’ operation to the html
document, coded in a JavaScript file).
There will be no inline
css.
1.31.3 XML
Validity: All xml and
operations on xml documents will conform to the xml
1.0 standard, by the w3c.
Files: Non-xhtml xml
will be used in this application in document object model form with
the ‘XmlHttpRequest’ object. No xml files are expected.
2
Executable Code
2.1 JavaScript
JavaScript v1.5 will be
the only kind of executable code used on the client.
2.1.1
Introduction
JavaScript offers many
avenues to accomplish almost any given task. When in doubt, follow
Java’s model as closely as possible instead of other
alternatives. When JavaScript offers a significant, unique tool –
such as ‘eval’ – use it to advantage.
JavaScript is used in
conjunction with the Document Object Model (DOM), as defined by the
w3c. DOM is the bridge from executable code to markup code.
2.1.2 Inheritance
Inheritance is
implemented using the built-in mechanism for inheritance in
JavaScript. These two types of statements should be implemented in
the extending class file:
Directly before the
constructor declaration, in the global scope of the file containing
the extending class:
//
Set the prototype chain for efficient inheritance:
Console.prototype
= new AbstractWidget();
And then in the
constructor of the extending class, as the first call (as in Java):
//
Complete the inheritance.
this.superC("console",
objParent);
Never put any
assignments of values of type ‘object’ (including the
‘null’ value) in the abstract class constructor
definition unless you specifically intend the assigned value to be
common (truly shared) by all objects made from classes your abstract
class.
Put all any assignments
of values of type ‘object’ (hashes, arrays, other
objects) in the method referenced by ‘this.superC’,
above.
Do put all
primitive-value assignments in the abstract class constructor
definition (strings, function (“method”) bindings,
numbers, booleans). This is important for performance.
2.1.3
DOM Operations
All DOM operations must
conform to the specification of DOM-Level
3, by the w3c. The shortcut ‘innerHTML’,
which has been cross-browser for five years, is acceptable.
2.1.4
Events
All synthetic and DOM
events must be passed through the event architecture only. For
example:
self.gloScope.registerEvent("console",
this,
this.strWindowId, "onresize");
See section 2.1.6,
“Inline JavaScript”, for example code showing how to make
the DOM/Html elements send events into the event architecture.
When taking events
directly from html attribute event handlers (i.e.,
onclick='doSomething()'), use
a static global event registry in order to avoid a circular
document-object-model-to-custom-JavaScript reference situation.
(Some browsers may present listener-attaching methods that exhibit
the same circular reference forming problem).
Note that the event
architecture requires that the event or events be received by the
listening object in a method named ‘receiveEvent’. This
makes all asynchronous operations self-documenting in any given class
file.
Always name event
handler methods with only lowercase letters and ensure that the names
always begin with either ‘on’, ‘onbefore’, or
‘onafter’. Conversely, only name true event handler
methods in this manner or with those prefixes.
Document events in the
class file header with the following JavaScriptDoc tags:
@listens-to-event
@emits-event
In the case of
@listens-to-event, be sure to document where the listened-to event
comes from. For example,
@listens-to-event
<code>onresize</code> from the <code>window</code>
document object.
@emits-event
<code>onresize</code>.
2.1.5 Global Variables
The only custom global
variable will be ‘self.gloScope’. All other top-level
custom JavaScript properties that are not class definitions must be
properties of ‘self.gloScope’.
2.1.6 Inline JavaScript
There will be no inline
JavaScript except for the dynamically-written calls to ‘handleEvent’.
Inline JavaScript is JavaScript in an html file.
An example of a call to
‘handleEvent’ is the following:
“.
. . onclick=’self.Environment.handleEvent(this.id, “onresize”,
this.e);’ . . . “
(The dots represent an
elision; do not use these in your actual code). Note that if
necessary (say, for example with ‘window.onresize’, which
cannot be rendered through dynamically-written html), the event
handler assignment may be done in a method called ‘bindEvents’.
For example,
function
Console_bindEvents() {
window.id
= this.strWindowId;
window.onresize
= function() {
self.Environment.handleEvent(this.id,
'onresize',
this.e);
};
2.1.7 Code Formatting
2.1.7.1
Whitespace
Use a presentable code
indenting style, with spaces, not literal tab characters, as the
indenting character. (This means that you can use ‘tab’
to indent your code, but your editor should put down ‘space’
characters for each ‘tab’. Editors can be set to
automatically substitute multiple spaces for single tab characters.
For the Gnu
Emacs development environment (available on
Windows/Mac/Unix), the following indenting, whitespace, and syntax
coloring set-up is recommended:
;;
-------- Begin JavaScript editing section. --------
;;
Use c-mode indenting for the C-family language, JavaScript:
(autoload
'c-mode "cc-mode.elc")
(setq
auto-mode-alist (cons '("\\.js$" . c-mode)
auto-mode-alist))
;;
This is the container for my custom js editing mode
;;
Include this line to use no tabs for cross-editor compatibility:
;;
(setq indent-tabs-mode nil)
(defun
my-js-indent-setup ()
(setq
c-default-style "bsd")
(setq
indent-tabs-mode nil)
(setq
c-basic-offset 4))
;;
Add the above hook to the c-mode.
(add-hook
'c-mode-hook 'my-js-indent-setup)
;;
-------- End JavaScript editing section. --------
;;
---------- Begin Global Tabs Section
;;
Make sure that tabs are being used (default behavior, but doesn't
hurt in case something got changed):
;;
(setq indent-tabs-mode t)
;;
Make sure that no tab characters are used:
(setq
indent-tabs-mode nil)
;;
Set the variable default-tab-width.
(setq
default-tab-width 4)
;;
---------- End Global Tabs Section
;;
Enable syntax coloring.
(global-font-lock-mode
t)
To configure your Gnu
Emacs installation to take these settings, put the code above into a
file named ‘.emacs’ that is read by Emacs when it first
starts up. To do this, follow the steps below. It is not hard.
First, ensure that the
HOME user environment variable exists. Create it if needed. Set it to
your home directory (or any directory you want to be home). Next
copy-paste the code above into a file named ‘.emacs’ in
your home directory. See below for details on carrying out these
operations under Windows.
Setting up your .emacs
file under Windows:
If Windows will
not let you name your file ‘.emacs’, just create the
file with Gnu Emacs.
To set the HOME
environment under Windows:
Go to: Start ->
Settings -> Control Panel -> System -> Advanced ->
Environment Variables
If you don’t
see a ‘HOME’ environment variable under ‘User
variables . . . ‘ at the top of the dialog, create a new one
by clicking ‘New’ and typing in ‘HOME’ (no
quote marks.
Set the value to
your home directory path.
2.1.7.2 Line Length
Keep line length under
115 characters per line. This prevents erosion of the indenting
effect by line wrapping.
2.1.7.3
Spacing
Use two consecutive
blank lines between each function definition.
Do not use more than
one consecutive blank line inside a function.
2.1.7.4 Curly Braces
To allow more lines of
code to be visible on the screen, keep the opening curly brace on the
same line as the code that precedes it. I.e.:
function
MyConstructor() {
this.intMyVariable
= 1;
}
To avoid
difficult-to-find bugs, always enclose if-then-else blocks in curly
braces, even if only one statement is in a block. I.e., do this:
if
(booReady) {
alert(“Ready”);
}
else {
alert(“Not
ready”);
}
2.1.8 Cache Repeated or Long References
JavaScript is a slow
language, and memory access decreases in speed exponentially with the
allocation of memory on the web browser. So it is important to cache
long or repeated object references. For example, do:
var
objConStyle = this.getDomRef().style
and use ‘objConStyle’
(a local variable) instead of repeated uses of
‘this.getDomRef().style’.
For another important
example, do this:
var
iL = this.arrArray.length, i=0;
for
(; i<iL; i++) {
}
Instead of this:
var
iL = this.arrArray.length, i=0;
for
(var i=0; i< this.arrArray.length; i++) {
}
The assumption all
these examples is that a local variable allocation is much cheaper
than the reference hops, which require searching by the interpreter
at each object level involved for the desired property. Notice also
how the ‘var i’ is done differently in the two examples.
Some JavaScript parsers may have to skip over the ‘var’
statement that is inside the ‘for’ statement each time.
2.1.9 ‘with’ statement Use
Only use the ‘with’
statement where everything referenced in the ‘with’ block
is an object property or sub-property (i.e., ‘this’ . . .
) of the object provided to the ‘with’ statement.
2.1.10 ‘switch’ Statement Pitfalls
The ‘switch’
statement in JavaScript has some surprising behavior that can result
in some hard-to-find bugs. Be sure to check a reference on ‘switch’
each time before using it if you have any doubts whatsoever about
exactly how the ‘switch’ statement works in JavaScript.
2.1.11 Documentation
2.1.11.1 Basic Standard
Document every class
file completely, according to Javadoc conventions and the special
notes below.
Document every class
constructor completely, according to Javadoc conventions and the
special notes below.
Document every method
completely, according to Javadoc conventions and the special notes
below.
Mark to-do notes in this format only:
to-do:
<your comment here>
2.1.11.2 Rules for Documenting JavaScript Datatypes
Class-level
documentation (the “class file header”):
Ensure that the
first line of the class file header gives the fully qualified name
of the class (i.e., com.treelogic_swe.frameworks.aspectes.Aspect).
Give the
licensing terms.
Describe the
class itself.
List the
principal authors in an html list.
Insert a tag for
version control.
Document any
immediate superclass in the following manner on a new line:
@inherits-from
<code>AbstractMyClass</code>
Next document any
events that class or objects made from the class will listen to, in
this manner on a new line (with extra linefeeds inserted for the
formatting of this document): @listens-to-event
<code>onmoveup</code> from the
<code>NavigationPad</code> custom JavaScript object.
Next document any
events that the class or objects made from the class will emit
(“fire”): @emits-event
<code>onmoveup</code>.
Next document any
class properties, and then document any object properties,
following the rules below. List, in the following order:
The datatype:
If a value is
not explicitly created with:
a constructor
(i.e., a number value
created by invoking the Number
constructor or an array created with the Array constructor
function)
or object
initializer syntax (i.e., an array created with square brackets
( [ ] ), or an object created with curly braces ( { } ))
then use the actual
result of the application of a typeof
statement to the value.
For example:
Given var intA = 2;
typeof intA gives
number. We then list
number as the
datatype.
For example:
Given var booGood = true;
typeof booGood gives
boolean. We then list
boolean as the
datatype.
Some common
primitive values in JavaScript are: boolean,
string, number,
function
If this value
is explicitly created with a constructor or object initalizer
syntax (see above), list the name of the function that
constructed the object (or the name of the function that
corresponds to the initializer syntax) as the datatype.
This name is
given in the result of the constructor
property of the object.
For example:
Given var
arrMyArr = new Array(); arrMyArr.constructor gives the
function
function
Array() { [native code for Array.Array, arity=1] }
In
this case, list Array as
the datatype for the property.
Given var
objMyObj = {}; objMyObj.constructor gives the function
function
Object() { [native code for Object.Object, arity=1] }
In
this case, list Object as
the datatype for the property.
Given var
strS = new String(“fred”); strS.constructor
gives the function
function
String() { [native code for String.String, arity=1] }
In
this case, list String as
the datatype for the property.
Given
function Fred() { this.intA
= 1; }; var objF = new Fred();
objF.constructor;
gives the function
function
Fred() { this.intA = 1; }
In
this case, list Fred as
the datatype for the property.
Then list the
property name, with its proper type prefix.
Next give a
complete description of the property, ensuring that line length
does not exceed 115 characters and that the description text does
not wrap underneath the datatype listing discussed above. Break
the line with a linefeed to prevent this from happening.
Here is a
complete example of a class-property JavaScriptdoc line (with
extra linefeeds inserted for the formatting of this document):
*
@class-prop <code>Object</code> <code>hshWidgets</code>
A hash containing all instantiated widget objects, keyed by widget
i.d.
Here is a
complete example of an object-property JavaScriptdoc line (with
extra linefeeds inserted for the formatting of this document):
*
@object-prop <code>FastLinkedList</code>
<code>objNewImagesList</code>
A linked list of the image urls required
*
for the new view on the <code>plate</code> document
object model object.
For every
parameter in every method or constructor signature:
List the name of
the parameter first (after @param)
and, in the sole exception to the rule, do *not* surround it with
code tags if using the JavaScriptdoc tool from the Rhino JavaScript
project or its derivatives.
Then list the
datatype on the same line, according to the rules above
Next, if the
parameter is optional, indicate that on the same line with the
word: OPTIONAL, all in capital letters.
Next, if the
parameter may be null, indicate that on the same line with the
statement MAY BE null.
Note that in
JavaScript, even variables intended for primitive values may be
set to null.
This one of the
advantages of JavaScript’s loose typing. Since we pay the
penalty for loose typing by definition, we should use it to our
advantage where possible.
Next put the
description, starting on the same line, and ensuring that line
length does not exceed 115 characters and that the description text
does not wrap underneath the parameter name listing discussed
above. Break the line with a linefeed to prevent this from
happening.
Here is an
example of proper method documentation:
/**
*
This method receives any events that this object
*
listens to and routes them to the proper methods.
*
@param pStrEventSourceElementId <code>string</code> The
i.d.
*
of the document object model
*
from which the event sprang.
*
@param pStrEventType <code>string> The event type of the
*
object (e.g., 'onclick', etc).
*
@param pObjEvent <code>Object</code> The event object
*
itself.
*/
Here is another
example of proper method documentation:
/**
*
Return the HTML of the object managed by
*
the <code>Plate</code> widget and its child widgets.
*
@return <code>string</code> A string representing
*
the HTML of the
*
object managed by the widget and
*
its child widgets.
*/
For every return
value, list the datatype of the return value after the @return
tag, selecting the datatype to list per the rules in item 1 of this
list.
If the value
returned is ordinarily a primitive value, but may also be returned
as null, list that return
value as in the step above, resulting in an additional @return
tag entry for the method.
Please note:
Always use the 'code' tag where you are naming something with the
same name as used in the code, unless in a case noted otherwise
above. This is per the Javadoc conventions but is repeated here since
the violation of this rule is a common mistake.
2.1.11.3 Functional and Procedural Code
Do not write any
functional or procedural code. Inner functions may be used, since
they are effectively inner methods.
Do not use plain 'callbacks', or dynamically-bound
methods outside of a formal aspect definition.
Do not rely upon lexically-scoped variables (via
the function closure) except when it is by far the most effective way
to accomplish the task at hand. Always document reliance upon
function closures in the JavaScriptdoc.
2.1.12 Class Files and Packages
Keep only one outer (top-level) JavaScript class
definition per file, and nothing else -- just as in Java.
Keep related classes in a package directory, and
name these packages with the organizational namespace, as in Java.
2.1.13
Methods
Create JavaScript
methods either by binding them in the class constructor, in this
manner:
this.receiveEvent
= Console_receiveEvent;
or
by creating them as inner functions within other methods that are
bound as the above, to a nested level no greater than two levels
deep.
Prefix
JavaScript methods with the class name and an underbar. For example,
function
MyClass_myMethod(pIntMyArg) {
}
The above method is
made to be a method by binding it in the JavaScript class
constructor:
function
MyClass() {
this.myMethod
= MyClass_myMethod;
}
Never
use the arguments object as a
vehicle for method parameters instead of named method parameters.
Instead, always fully declare all parameters in the method signature.
(Use of arguments.length is
fine).
Create
real private methods when possible by using JavaScript inner methods.
JavaScript inner methods entail some performance impact, but the
true encapulation they provide (rare in JavaScript) is well worth
some performance sacrifice.
JavaScript
inner methods are function declarations nested inside other function
declarations. Follow this stylistic convention for inner methods:
Example:
When putting an inner method called upArrow
(for example) inside a method called, for example,
Navigation_getHtml,
prepend the last chunk of the method name beginning with an underbar
and add an underbar to it. In this case, the prepended fragment would
be _getHtml_. So the final
name of the inner method would be _getHtml_upArrow.
Additionally, demarcate
‘private’ JavaScript methods with the prefix ‘pri’:
function
MyClass_priMyMethod(myArg) {
}
2.1.14 Static Definitions
Use JavaScript
pseudo-static properties where you would Java static objects; i.e.:
AbstractWidget.intUniqueNum
= 0;
2.1.15 Data Types
2.1.15.1 Maintaining Type Correctness
Never
let a variable or property fall into a state where its true datatype
does not agree with its type prefix. For example, if testing for the
existance of a string match, do not do this:
var booIsRainy =
strWeatherReport.indexOf( “rain” ) + 1;
instead,
do this:
var booIsRainy = new
Boolean( strWeatherReport.indexOf( “rain” ) ).valueOf();
2.1.15.2 Type Prefixes
Because
JavaScript is a loosely typed language, a prefix notation has been
used. This helps to avoid pernicious bugs. A particularly dangerous
mistake to make in JavaScript is to treat an array as a plain object.
Here is an illustration of the pitfall of this mistake straight from
the Rhino JavaScript prompt:
js>
hshA = { 2 : 2, 0 : 0, 1 : 1 };
[object
Object]
js>
arrA = [ 1, 2, 3 ];
1,2,3
js>
print(hshA[0]);
0
js>
print(arrA[0]);
1
js>
If you know that ‘hshA’
is a hash (a plain JavaScript object), the reason why ‘hshA’
does not return ‘2’ is clear. However, if you thought
that ‘hshA’ was an array, you would expect ‘hshA’
to return ‘2’. Therefore, always mark hashes with the
prefix ‘hsh’ and arrays with the prefix ‘arr’.
Furthermore, because of
the wide variety of types of objects supported by JavaScript and the
complexity that JavaScript code may reach, other type prefixes are
used. This provides a shield against possible pitfalls beyond the
example given above, and makes the code much more readable.
2.1.15.3 Type Prefixes Table
Use of the following
datatype and structural prefixes is required:
|
Prefix
|
JavaScript Type
|
Comment
|
|
Abstract
|
Function
|
An function that is an abstract class
constructor.
|
|
any
|
Any
|
JavaScript can put
any type into a given identifier.
In rare but
important cases, use of this facility can
be helpful to code clarity, compactness, and
speed.
|
|
arr
|
Array
|
A JavaScript array object, for ordered access
only.
|
|
boo
|
Boolean
|
A JavaScript boolean.
|
|
ct
|
Object
|
A Controller object.
|
|
dom
|
Object
|
A JavaScript object
that represents an object in the
browser’s Document Object Model.
|
|
evt
|
Object.
|
An event object.
|
|
flt
|
Number
|
A JavaScript Number
to be treated as a floating
point number.
|
|
fra
|
Object
|
A top-level Window frame, or an inline frame.
|
|
fun
|
Function
|
A JavaScript
function, never accessed as an
object method.
|
|
hsh
|
Object
|
Used as a pure unordered set.
|
|
int
|
Number
|
A JavaScript Number to be treated as an
integer.
|
|
obj
|
Object
|
An object that is a
combination of data and
methods.
|
|
p
|
(any type)
|
Function Argument;
capitalize the other type
prefix: i.e., pIntNumCards.
|
|
rxp
|
Regular Expression
|
A JavaScript regular expression.
|
|
str
|
String
|
A JavaScript string.
|
|
super
|
Function
|
A JavaScript
function that is bound as an object
method and is used
to bestow unique copies of
objects upon objects
created from inheriting
classes.
|
|
und
|
Undefined
|
A JavaScript object of type “undefined”.
|
|
wgt
|
Object
|
A GUI widget object.
|
2.1.16 Code Optimization
Follow standard code optimization practices
detailed in any c-family language code optimization reference and
in good JavaScript books, such as:
Caching the length of 'for' loops
Minimizing reference hops by caching nested
object references in local var
variables
Where performance is not absolutely
critical, take advantage of the encapsulation provided by
JavaScript's inner methods and inner classes.
Using the with
statement to advantage (see above).
Only using eval
where necessary.