Code Guidelines

D

Dmitry A. Soshnikov

[snip]
Yes, Dmitry is correct, you're both right. I wasn't explicit enough, or
used terminology that was open to interpretation.

Changed to:
* Use of == where strict equality is required
Where strict equality is required, the strict equality operator must be
used. The strict equality operator should always be used to compare
identity.

The previous description (with ===) was quite understandable too, and
goes without saying, I meant not that. I mean that there's no any
reason to use === with `typeof' operator. So you can easily always
use:

if (typeof blaBla == 'undefined') {
...
}

'cause, repeat, algorithms are equivalent in this case as `typeof'
always returns value of string type.

/ds
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
8924D9443D28E23ED5CD>, Tue, 22 Dec 2009 11:04:07, John G Harris
Spaces are preferred, I hope.

Some like larger indents. And a good viewing system can be set to make
a tab equivalent to two or three spaces. On the Web, however, tabs will
normally be worth up to 8 spaces, and should not be used as the indent
unit since most readers will find that too big.

AFAICS, however, tabs are fine for comment and in code strings and in
simple tables.

The primary objection is not to tabs as such, but to an over-wide indent
unit however produced.

AFAIK, in browser output, it is safe to assume tab stops at 8n+1; it may
not be so if the output goes, or is taken, elsewhere.

It should not be at all difficult to write script that will take tabbed
textarea content and replace tabs by appropriate spaces, including tab
stops at 2n+1 in leading whitespace, otherwise 8n+1. That should slim
the broader authors reasonably well. The FAQ site could, on another
page, offer such routines.



On long lines : while it is nice if everything fits in 72 characters,
that is not of prime importance. What matters is that the sending agent
must not machine-wrap lines of code (as it rightly will for text). A
well-designed receiving agent (such as yours) should not impose a fixed
right margin, and material that really needs longer lines should be sent
with those longer lines.

Desperate readers with primitive newsreaders should always be able to
save the article and read it a la Notepad.
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Mon, 21 Dec 2009 15:52:55, Garrett Smith
Rich Internet Application Development Code Guildelines (Draft)

Remember the spelling/grammar checker.

* non-localized strings

You should mean non-internationalised strings, since that is a list of
bad things.

Generally, one does better to list the good things.

* inconsistent return types
Methods should return one type; returning null instead of an Object
may be a fair consideration.

Where successful operation of a routine cannot return anything
resembling false, ISTM reasonable and useful to return something
resembling false if the routine has failed to do what was wanted.

Strategies:
* Modifying built-in or Host object in ways that are either error
prone or confusing (LOD).

Include : using without explanation jargon or any other than well-known
acronyms & abbreviations

Strings:
* use of concatenation to repeatedly (repeatedly create and
discard temporary strings)
Instead use String.prototype.concat(a, b, c) or htmlBuf.push(a, b, c);

Remember that the chief use of JavaScript by the untended FAQ readership
is to produce smallish pieces of code without much looping, running in a
Web browser as a result of a user action. There is no point at all in
telling a general FAQ reader that the less machine-efficient methods are
wrong, if they work perfectly well and the code will complete within 100
ms.

Writing the fastest code in a nice intellectual exercise for consenting
participants, but it should not be imposed overall. The best code is
easily-maintainable code, which generally means using a form of code
which will be readily understandable to the author in three months time.

RegularExpressions
Be simple, but do not match the wrong thing.

Generally - test not only that it works when it should, but also that it
does not work when it should not.



Include - don't repeat code where it can reasonably be avoided. Use
temporary variables, or functions or methods, for this.
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Tue, 22 Dec 2009 23:12:58, Garrett Smith
<[email protected]> posted:

{with RegExps]
Trying to match leap years would be excessively complex.
Instead, the date validation can be addressed where the expression is used.

Well, in the context I agree. But, for the festive season, the
following code alerts 7777 only (that was added to show that the loop
runs); the middle line does with a reasonably simple RegExp tell whether
Y is leap.

for (Y=0 ; Y<11111 ; Y++) if (
/([^5AF]0|[48CG])$/i.test((Y+400).toString(20))
!= !!(2-new Date(Y+400, 1, 29).getMonth() ) || Y == 7777 ) alert(Y)

The first 400 saves worrying about 1-digit years; the second compensates
and saves worrying about years before 100.

Tested only in FF 3.0.15.
 
D

David Mark

In comp.lang.javascript message <[email protected]
september.org>, Mon, 21 Dec 2009 15:52:55, Garrett Smith


Remember the spelling/grammar checker.


You should mean non-internationalised strings, since that is a list of
bad things.

Generally, one does better to list the good things.


Where successful operation of a routine cannot return anything
resembling false, ISTM reasonable and useful to return something
resembling false if the routine has failed to do what was wanted.

That's an over-generalization. Functions that return nothing result
in the undefined value. That shouldn't mean they failed. It really
depends on the function, but there is no general reason to return a
"truthy" value.
Include : using without explanation jargon or any other than well-known
acronyms & abbreviations

typeof x == 'unknown'; // ActiveX (is that okay?)
(x); // Boom
Remember that the chief use of JavaScript by the untended FAQ readership
is to produce smallish pieces of code without much looping, running in a
Web browser as a result of a user action.

"...as a result of a user action" is the operative phrase. Such
results _need_ to be as fast as possible. If you've ever typed into
an "autocomplete" widget, you know what I'm talking about.
There is no point at all in
telling a general FAQ reader that the less machine-efficient methods are
wrong, if they work perfectly well and the code will complete within 100
ms.

Huh? Repetitive string concatenation is a performance killer
(particularly in IE).
Writing the fastest code in a nice intellectual exercise for consenting
participants, but it should not be imposed overall.

I don't see what that has to do with excessive concatenation.
The best code is
easily-maintainable code, which generally means using a form of code
which will be readily understandable to the author in three months time.

Pushing strings to an array and then joining (instead of
concatenating) is not going throw anyone (at least not anyone who
should be programming JS).
Generally - test not only that it works when it should, but also that it
does not work when it should not.

Seems sensible.
Include - don't repeat code where it can reasonably be avoided.
Good.

 Use
temporary variables, or functions or methods, for this.

Temporary variables? And it's not exactly clear what "this" is. :)
 
G

Garrett Smith

Asen said:
I ever wondering about using RegExp for type checking.

/^\s*\bfunction\b/.test("" + f);
RegExp.test converts the argument to a string, so could be written as:-

/^\s*\bfunction\b/.test(f);

Type checking clutters the code up.

Type checking properties of Host object has been known to cause errors.
Safari 2 blows up when accessing a property off a NodeList.

The typeof operator can hide errors in IE, though, as discussed recently.
Example:-
javascript: alert(typeof document.styleSheets[-1] );

typeof hides error.

There should be a list of things that blow up in IE.
That will be check implementation depended string returned from
f.toString(); for as function, and that checking can be falsish.

f.toString() would resolve a toString property on the function before
calling it.

A function's toString property could be an own property. Otherwise, it
would be found in Function.prototype. The implementation-dependent
representation is required to be a FunctionDeclaration. In practice,
most implementations violate that as:-

(function(){}).toString();

The results is a string that is not a FunctionDeclaration (looks like a
FunctionDeclaration). The specification should change and I have
proposed this change to the ECMAScript committee. My proposal was to
change where it says "FunctionDeclaration" to "Function Definition".
This would legalize existing implementations' returning a string
representing a FunctionExpression.

Proposed:-
| 15.3.4.2 Function.prototype.toString ( )
|
| An implementation-dependent representation of the
| function is returned. This representation has the
| syntax of a Function Definition. Note in particular
| that the use and placement of white space, line
| terminators, and semicolons within the representation
| string is implementation-dependent.


https://mail.mozilla.org/pipermail/es-discuss/2009-September/009816.html
e.g.

var o = {
toString : function()
{
return 'function function';
}
};
window.alert(/^\s*\bfunction\b/.test("" + o)); //true

That is a different situation. Here we have an object that is not a
function, but has a toString that could result in a false positive.

new Object("Conjunction Junction, what's your function?");
 
A

Asen Bozhilov

Garrett said:
RegExp.test converts the argument to a string, so could be written as:-

/^\s*\bfunction\b/.test(f);

Yes, but do you can to explain next lines in IE:

var xhr = new ActiveXObject('Microsoft.XMLHTTP');
try {
String(xhr);
}catch(e) {
window.alert(e instanceof TypeError); //true
}
window.alert(('foo' + xhr).length); //3
window.alert(typeof ('foo' + xhr)); //string
window.alert(typeof (xhr + 'foo')); //undefined

| 15.5.1.1 String
| Returns a string value (not a String object)
| computed by ToString(value).

ToString for Object call ToPrimitive with hint `string`. ToPrimitve
call internal [[DefaultValue]] method of that Object. [[DefaultValue]]
throw TypeError if Object properties `toString' and `valueOf' is not a
objects.

| 11.6.1 The Addition operator ( + )
| The production AdditiveExpression :
| AdditiveExpression + MultiplicativeExpression
| is evaluated as follows:
| 1. Evaluate AdditiveExpression.
| 2. Call GetValue(Result(1)).
| 3. Evaluate MultiplicativeExpression.
| 4. Call GetValue(Result(3)).
| 5. Call ToPrimitive(Result(2)).
| 6. Call ToPrimitive(Result(4)).

Lets modified my example to use native object instead of host object:

var o = {toString : null, valueOf : null};
try {
String(o);
}catch(e) {
window.alert(e instanceof TypeError); //true
}
window.alert(('foo' + o).length); //3
window.alert(typeof ('foo' + o)); //string
window.alert(typeof (10 + o)); //number
window.alert(typeof (o + 'foo')); //object
window.alert(typeof (o + 10)); //object


window.alert(typeof (o + 'foo')); //object

Here i expected 'string' from 11.6.1 step 7.
| If Type(Result(5)) is String or Type(Result(6)) is String,
| go to step 12. (Note that this step differs from step 3 in
| the comparison algorithm for the relational operators,
| by using or instead of and.)


From what that i can see JScript use much complex algorithm in `The
Addition operator ( + )` and definitely they don't follow
documentation steps in `11.6.1 The Addition operator ( + )`. Can
anybody explain used algorithm of JScript?
 
T

Thomas 'PointedEars' Lahn

Garrett said:
It is a true statement, but not obvious to all.

A spoon must be used where a spoon is required. How stupid does one have to
be not to recognize the inherent veracity of such statements?


PointedEars
 
T

Thomas 'PointedEars' Lahn

For better understanding, the exact term "ETAGO delimiter" should be used.
No, it refers to markup in script (e.g. document.write calls).
ACK

Nothing to do with the tag type.

The *what*? I thought this was comp.lang.javascript, not
alt.scriptkiddie.misc.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Garrett said:
* Not escaping ETAGO, or using concatenation to escape ETAGO.
Inline script must not contain the character sequence "</"

http://www.w3.org/TR/WD-script-970314

There really is no reason here to go at lengths citing hopelessly outdated
material:

When concatenating strings of HTML in an inline script, a backslash
should appear before the solidus symbol in the sequence "</", resulting
in "<\/", and not - "<" + "/" -.

Do not confuse the solidus character (U+2044) with that of the US-ASCII-
compatible forward slash (U+002F), though.

Wrong. It is Valid in HTML 4.01 Transitional only. As for the "HTML 5"
reference: When will you ever learn? And it certainly would not apply to
the "XHTML variant" of "HTML 5", whatever that is supposed to become some
day.
True. Non-semantic class or ID lacks meaning (HTML and CSS). The same
applies to ID.

Instead, class and id should be meaningful, so the code is easier to
understand.
Indubitably.

Bad:
.errorButton, #warningMessage

Good:

IBTD.


PointedEars
 
G

Garrett Smith

Thomas said:
Garrett Smith wrote:
[...]
Instead, class and id should be meaningful, so the code is easier to
understand.
Indubitably.

Bad:
.errorButton, #warningMessage

Good:

IBTD.
Good catch.

I put the "good" line in after "bad". Those are good examples.

I have the text in the draft:

Selectors such as .redButton, #ItalicStatement are meaningless.
Instead, use class and id that represent an object or state.
Example:

.errorButton, #warningMessage, .active-panel

This makes the code is easier to understand and doesn't
become meaningless when the styles change.

I am still replying to Stockton's comments on this, and it is a Holiday
here, so spending time with people now.
 
G

Garrett Smith

Thomas said:
A spoon must be used where a spoon is required. How stupid does one have to
be not to recognize the inherent veracity of such statements?

Not everyone knows the difference.

I have see a lot of code that uses == where === should have been used to
avoid the possibility of type conversion. When I see it, I point it out.

The mistake happens out of carelessness and inexperience.

I thought it would be useful to include this in the checklist, so that a
developer preparing the code for review could consider that.
 
D

Dr J R Stockton

In comp.lang.javascript message <7895e8b5-1f6f-4a3c-9e09-11d5379a3973@n1
3g2000vbe.googlegroups.com>, Thu, 24 Dec 2009 16:08:38, David Mark
That's an over-generalization. Functions that return nothing result
in the undefined value. That shouldn't mean they failed. It really
depends on the function, but there is no general reason to return a
"truthy" value.

"A implies B" does not imply "not A implies not B", although the
converse seems to be taught in American schools.

If the "main results" of a function are obtained other than through the
returned value of the function, then one can still return false if it
fails, but one should then return true if it succeeds.

But clearly I had in mind a function which returns its main results.
typeof x == 'unknown'; // ActiveX (is that okay?)
(x); // Boom

A pointless remark.
"...as a result of a user action" is the operative phrase. Such
results _need_ to be as fast as possible. If you've ever typed into
an "autocomplete" widget, you know what I'm talking about.

Utter nonsense. If the calculation and display take less than about 100
ms from user initiation, there is no benefit in speeding them up.

FF 3.0.15, XPsp3, P43GHz,
<URL:http://www.merlyn.demon.co.uk/js-misc0.HTM#MLS> :
100,000 sequential concatenations of a 9-character string, by Turtle,
takes under 0.06 seconds. That, for a member of the intended FAQ
readership, is rarely of any importance.
Huh? Repetitive string concatenation is a performance killer
(particularly in IE).

Perhaps you should test that assertion. Possibly you are an aged
person, raised on JavaScript in early browsers running on 486/33 CPUs or
similar, when code ran a hundred or more times slower than it does
today. Nevertheless, the real point which you show no signs of having
grasped is that, for most of the work of those who the FAQ is intended
to help the difference between the various methods will be at most a few
milliseconds, which DOES NOT MATTER.

If a string is short enough to be displayed of an ordinary Web page of
reasonable size, then any method of joining its natural parts will be
fast enough.


I don't see what that has to do with excessive concatenation.

Your problem.





Temporary variables? And it's not exactly clear what "this" is. :)

Your problem. It is repeating code where it can reasonably be avoided.

Examples :

Don't repeat getElementById on the same element unnecessarily; save the
reference in a temporary variable, for readability.

Don't multiply repeat, with different variables, such as M D h m s,
if (M<10) M = "0" + M
but use a function such as
function LZ(n) { return (n!=null&&n<10&&n>=0?"0":"") + n }
or function LZ(n) { return (n<10?"0":"") + n }
 
G

Garrett Smith

Dr said:
In comp.lang.javascript message <[email protected]
september.org>, Mon, 21 Dec 2009 15:52:55, Garrett Smith


Remember the spelling/grammar checker.

Code Guidelines for Rich Internet Application Development

What do you think?
You should mean non-internationalised strings, since that is a list of
bad things.

I meant non-localized. For example, a problematic script that has:
showMessage("please enter your last name");
Generally, one does better to list the good things.

The document is intended to be used primarily as a checklist for code
review preparation. Code must meet acceptibility criterion before going
into production, so that it can be determined if it is good or not.

It is foolish and omnipresent practice to ship messes to production.
This happens for many reasons, ignorance being the largest.

One such form of ignorance is the lack of criteria or standard by which
to assess code quality. This ignorance can be eliminated or at least
alleviated by providing a list of guidelines that defines code quality.

The code guidelines has been changed so that it is in the positive
sense, where appropriate, and negative sense, where appropriate. For
example:-

Markup:
* Use valid html.
* Use standards mode, with DOCTYPE first (no comment, xml prolog).
* Send html documents as text/html.
* Escape ETAGO with backwards solidus (backslash).
http://www.cs.tut.fi/~jkorpela/www/revsol.html
* Do not use javascript: pseudo protocol.

Sound better?

It may be worth considering assigning priority labels to certain things.
For example: "Use valid HTML" an "Use standards mode" would be priority
1 guidelines, while "Use efficient string concatenation techniques"
might be a Priority 2, while
Where successful operation of a routine cannot return anything
resembling false, ISTM reasonable and useful to return something
resembling false if the routine has failed to do what was wanted.

Functions that return multiple type provide more variance to the caller.

Sometimes this happens on accident, where the function was
intended to return one type, but can return a value of a different type.

Functions that return only one value type (including undefined) do not
burden the caller with having to check the return type.

It is something to look for in code where the function may, for example,
return a number in one case and a string in another case.

Functions that return multiple type have their place. A method might
return an object, but return null if the operation failed. So it is not
a rule, but something to consider when looking the code.
Include : using without explanation jargon or any other than well-known
acronyms & abbreviations
"Don't modify objects you don't own".

A piece of code that modifies objects it does not own creates a coupling
with those modifications and anything using that piece of code.

This coupling creates a dependency of those modifications to all the
dependencies of the code that exists in that codebase. Dependency of
those modifications is maximized to every piece of code that uses the
module that has modified them. Sometimes dependencies may even collide.
For example, one piece of code may create Element.prototype.hide, and
another piece of code may create a different Element.prototype.hide.

Instead, a separate interface should be created. The interface can be as
simple as a function. For example: hideElement( el ).

A fair exception to that rule would be for adding or fixing properties
that are either not yet implemented, or do not function as specified in
the standard.

if(!String.prototype.trim) {
// Patch for missing ES5 method.
}

An additional problem occurs modifying Host objects or Host objects'
constructors' prototypes. The outcome of this isn't specified and won't
work consistently cross-browser. The interface-based API design of the
w3c DOM allows for sub-interface to provide a more specific
implementation that shadows the user-defined method of the same name
further up in the prototype chain.

The w3c DOM does not prevent implementations from creating their own
interfaces. Quite often implementations do create interfaces that are
interwoven through the interface hierarchy. Such interfaces may or may
not be accessible to code.
Remember that the chief use of JavaScript by the untended FAQ readership
is to produce smallish pieces of code without much looping, running in a
Web browser as a result of a user action. There is no point at all in
telling a general FAQ reader that the less machine-efficient methods are
wrong, if they work perfectly well and the code will complete within 100
ms.

The code guidelines doc will help identify principles that make code
good and problems in code.

A motivation for this is to help facilitate better code reviews.

Some people would define good code as code that works, but that is a
very low standard.

Many recent posts on this list are a large dump of unformatted code,
followed by, and interspersed with, non-technical remarks and insults
directed towards the code author. Usually missing all or most of the
actual bugs in the process.

There is no document that defines *good* set of principles by which to
judge code.

I recently saw this entry on stack overflow:-
http://stackoverflow.com/questions/87896/code-reviews-on-the-web-for-php-and-javascript-code

| What are the best places for freelancers or small companies to get
| code reviewed for PHP and JavaScript? Forums are an option, but are
| there any sites dedicated specifically to code reviews?

There is a lot of advice out there already. Some online documents that I
have read web advocate principles without justification, make false
statements, and show bias by advocating a particular library.
Writing the fastest code in a nice intellectual exercise for consenting
participants, but it should not be imposed overall. The best code is
easily-maintainable code, which generally means using a form of code
which will be readily understandable to the author in three months time.
Code optimizations don't always reduce clarity. The best solutions are
simple and efficient. String.prototype.concat is simple and easy way to
avoid IE's slow string handling.
Generally - test not only that it works when it should, but also that it
does not work when it should not.

That applies to functions, too.
Include - don't repeat code where it can reasonably be avoided. Use
temporary variables, or functions or methods, for this.
That's the basis for one of the DRY principle.

SOLID principles can also apply to js application architecture. SRP
applies to functions. LSP seems less closely related to js programming
where user-defined inheritance structures do not exist, though YUI panel
is an example of LSP violation.

SRP The Single Responsibility Principle
A class should have one, and only one, reason to change.
OCP The Open Closed Principle
You should be able to extend a classes behavior, without modifying it.
LSP The Liskov Substitution Principle
Derived classes must be substitutable for their base classes.
DIP The Dependency Inversion Principle
Depend on abstractions, not on concretions.
ISP The Interface Segregation Principle
Make fine grained interfaces that are client specific.
 
E

Eric Bednarz

Garrett Smith said:
* Use valid html.

If that really was a valid requirement for web authoring, it wouldn’t be
joined by deep confusion about very basic and simple issues in almost
all cases.
* Use standards mode, with DOCTYPE first (no comment, xml prolog).

Well, that didn’t take long. The document type declaration *is part of
the prolog*, in both XML and SGML productions.
 
G

Garrett Smith

Garrett said:
Dr said:
In comp.lang.javascript message <[email protected]
september.org>, Mon, 21 Dec 2009 15:52:55, Garrett Smith

[...]

* Escape ETAGO with backwards solidus (backslash).
Changed in draft to "Escape ETAGO delimiter".
[...]
An additional problem occurs modifying Host objects or Host objects'
constructors' prototypes.

Should be "DOM objects' constructors' prototypes", to reflect ES5 change
to the definition of Host object, and the fact that a browser may
implement DOM object as Native object.

[...]
That's the basis for one of the DRY principle.

correction: "for the DRY principle", not "for one of the DRY principle".
 
D

David Mark

For better understanding, the exact term "ETAGO delimiter" should be used..



The *what*?  I thought this was comp.lang.javascript, not
alt.scriptkiddie.misc.

The type of tag serialized in the string (e.g. TD vs. SCRIPT). Are
you proposing to refer to these as *elements*? I think not as we are
talking about just one (ending) tag (not both).
 
G

Garrett Smith

Eric said:
If that really was a valid requirement for web authoring, it wouldn’t be
joined by deep confusion about very basic and simple issues in almost
all cases.


Well, that didn’t take long. The document type declaration *is part of
the prolog*, in both XML and SGML productions.
Maybe so, but for HTML, no comments and no prolog will trigger quirks
mode in IE. They should both be avoided for this reason.
 
G

Garrett Smith

Garrett said:
Eric said:
Garrett Smith <[email protected]> writes:
[...]
Well, that didn’t take long. The document type declaration *is part of
the prolog*, in both XML and SGML productions.
Maybe so, but for HTML, no comments and no prolog will trigger quirks
mode in IE. They should both be avoided for this reason.
Correction:
"comments and/or prolog will trigger quirks mode in IE."
 
D

Dmitry A. Soshnikov

[snip]
"Don't modify objects you don't own".

A piece of code that modifies objects it does not own creates a coupling
with those modifications and anything using that piece of code.

This coupling creates a dependency of those modifications to all the
dependencies of the code that exists in that codebase. Dependency of
those modifications is maximized to every piece of code that uses the
module that has modified them. Sometimes dependencies may even collide.
For example, one piece of code may create Element.prototype.hide, and
another piece of code may create a different Element.prototype.hide.

Instead, a separate interface should be created. The interface can be as
simple as a function. For example: hideElement( el ).

A fair exception to that rule would be for adding or fixing properties
that are either not yet implemented, or do not function as specified in
the standard.

if(!String.prototype.trim) {
   // Patch for missing ES5 method.

}

Just to be fair, you should mention, that augmenting (build-in, any)
objects in dynamic language with mutable objects - is in ideology of
such language. So, everyone chooses. You can *suggest* do not
augmenting "not own" object (with providing serious reason, which is
still just your meaning), but not statement this as rule.

For do not repeat myself: <URL:
http://groups.google.ru/group/comp....64462/bbfb83eb82ef9b41?hl=en#bbfb83eb82ef9b41>
and there's also link to other similar topic.

The only (the only.) reason - is using 3rd-party code, combined in
your application. But from this point of you, what exactly you are
afraid of - "another piece of code may create a different
Element.prototype.hide" - with the same success "another piece of code
may create a different hideElement( el )" which you suggest as a
"workaround", there's no any difference.

So, again, it's absolutely normal to augmenting objects in ES,
providing good documentation of what have you augmented (and for whole
code in general).

If you still wanna to write this as a rule, please mentioned, that
it's not the rule, but *just your own suggestion and own meaning*,
meanwhile other people can choose different (good) way augmenting
object and write in OOP-style such as `string.capitalize()' instead of
long ugly `Ext.util.Format.capitalize(string)'. Especially in own
project.

/ds
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top