Trait-like CSS on HTML

Defining some CSS types

One way of structuring the CSS is to define some types over the CSS classes. What these types might be may be is open, but one instance might be Behaviour, Layout and Roles. The types are not used explicitly in the CSS, but are there to help organize and abstract the CSS classes.

On these types the following CSS classes can be defined:

  • Roles – sidebar, menu, toolbar, group, item, title, input, post, search, button
  • Behaviour – inactive, hidden, selectable, grabable, highlighted
  • Layout – flow{L,R}, break{,L,R}, padded{,L,R,T,B,V,H}, spaced{,L,R,T,B,V,H}

    where {} mean “choose from” and letters are abbreviations for Left, Right, Top, Bottom, Vertically, Horizontally

For Roles it is important to add classes that can be combined or nested. For instance, toolbar and menu might both contain item and group. Furthermore, a sidebar might contain a toolbar or menu. Experimenting and identifying such classes as Roles (or any types) can help simplify or find reoccuring structures in the CSS.

Tagging the HTML elements with CSS classes

Depending on the design of the HTML page, HTML elements may be tagged as you see fit with CSS classes. The variations of Roles is a result of how the HTML elements are nested. These elements can further be tagged with Behaviour and Layout classes.

Example:

Setting the appearance of composed CSS classes

The visual appearance of the HTML elements is specified by a set of selectors, each having a pattern to match and the corresponing CSS attributes to set.

Guidelines for writing the selectors:

  • More specific compositions of CSS classes override less specific
  • Selector A B specifies node B when nested inside A
  • Selector AB specifies node with A and B
  • Selector A>B specifies node B when child of A
  • Selector A+B specifies node B if after A
  • Selector A B, C+D specifies two selectors using the same attributes

Example:

.break { clear: both; }
.breakL { clear: left; }
.breakR { clear: right; }

.spaced { margin: 4px; }
.spacedL { margin-left: 4px; }
.spacedV { margin-top: 4px; margin-bottom: 4px; }
.spacedH { margin-left: 4px; margin-right: 4px; }

.menu {
  background-color: #404040;
}

.menu .title {
  color: #a0a0a0;
  font-weight: bold;
}

.menu .item {
  color: #a0a0a0;
}

/* Here .item.selectable is node with both */
.menu .item.selectable#hover {
  color: #f0f0f0;
}

References

Reminders on Scala syntax

Reminders

The companion object (both for own code and Scala’s API)

// The class definition with primary constructor
class Node protected (val a: String, private var b: String) {
  // imports definitions in companion object
  import Node._ 

  // attributes and primary constructor code
  var busy = false

  // methods
  override def toString() = "A"
}

// Companion object comes after the class/trait definition
object Node {
  // apply and unapply methods for object pattern matching (like case classes)
  // and object construction
  apply(arg: String): A = new A("a", arg)

  // static methods and variables
}

val node = Node("test","me")

Setter and getter functions are defined implicitly for val, var and referenced arguments in primary constructor. To set these manually

class A {
  private[this] var v: Int = 0 // avoids implicit setters and getters
  def value(): Int = v
  def value_=(t: Int): Unit = { v = t } // setters returns Unit
}

Adding indexing operators

// Here T give the type parameter for the class
class A[T] (size: Int) {
  private val array = new Array[T](size)
  def apply(index: Int): T = { /** get index value */ }
  def update(index: Int, value: T): Unit = { /** set index value*/ }
}

val a = new A[Int](100)
a(0) = 1
val n = a(0)

Binding a variable during pattern matching

str match {
  case bound @ "A match!" => println(bound)
  case _ => // no match
}

Defining an extractor for pattern matching (example lacks validation)

object Email {
  def apply(user: String, domain: String): String =
    user + "@" + domain

  def unapply(str: String): Option[(String,String)] = {
    str.split("@").toList match {
      case user :: domain :: Nil => Some(user,domain)
      case _ => None
    }
  }
}

"me@host.com" match {
  case Email(name,host) => println("Username is " + name)
  case _ => println("Invalid email address")
}

Imports can be grouped

import scala.collection.mutable.{Map,Stack,Queue}

Types can be aliased

type QMap = Map[Int,Queue[Int]]

Upper A <: T (A is a subtype of T) and lower A >: T (A is a supertype of T) bounded type parameters

class List[+A](elem: A, next: Option[List[A]]) {
  def prepend[B >: A](newElem: B): List[B] = new List(newElem, Some(this))
}
// scala> val l1 = new List(1.0, None)
// l1: List[Double] = List@1f49969
// scala> val l2 = new List(10,Some(l1))
// l2: List[AnyVal] = List@19bf996

def sort[T <: Ordered[T]](list: List[T]) { /** ... */ }

Writing () is optional with no arguments, and . is optional for methods with one argument

val b = new B
List(1,2,3) map (_ * 2)

The return keyword is optional (and curly braces on a single expression too)

def test() = "test"

The return type Unit is implicit when functions are declared without =

def printBig(str: String): Unit = {
  println(str.toUpperCase)
  () /** explicitly end the expression with the unit value */
}
def printBig(str: String) { println(str.toUpperCase) } /** same as previous */

References

Simple serialization with XML strings in Javascript

Introduction

Included in this post are some quick and simple functions for doing XML serialization in Javascript through strings. Reserved XML characters < > & ' " are escaped from input and if any invalid input are found an exception will be thrown.

List of XML utility functions:

  • toXmlHeader() – Creates the XML header.
  • toXmlElem(name: string, attributes: object) – Creates and closes an XML element.
  • toXmlElemOpen(name: string, attributes: object) – Creates and opens an XML element.
  • toXmlElemClose(name: string) – Closes an XML element.
  • toXmlText(text: string) – Creates XML text.

A serialization example

var xml = toXmlHeader();
xml += toXmlElemOpen("Library"); // No attributes given.

xml += toXmlElemOpen("Authors", {country: "Norway"});
xml += toXmlElem("Author", {name:"Petter Dass"});
xml += toXmlElemClose("Authors");

xml += toXmlElemOpen("Books"); // No attributes given.
xml += toXmlElem("Book", {title:"book1", author:"author1"});
xml += toXmlElemOpen("Book", {title:"book2", author:"author1"});
xml += toXmlText("Ach, So?");
xml += toXmlElemClose("Book");
xml += toXmlElem("Book", {title:"book3", author:"author2"});
xml += toXmlElem("Book"); // No attributes given.
xml += toXmlElemClose("Books");

xml += toXmlElemClose("Library");
alert(xml); // the complete xml is now contained inside the string.






Ach, So?

Sourcecode for the XML utility functions

function toXmlValid(str) {
  if (str === undefined || str === null || str.match === undefined || str.match(/<|>|&|'|"/) !== null) {
    throw("invalid string given");
  }

  return str;
}

function escapeXmlText(str) {
  if (str === undefined || str === null || str.replace === undefined) {
    throw("invalid string given");
  }

  // The order of replace is important because the & character is used in escaping.
  return str.replace(/&/g, "&").replace(/"/g, """).replace(//g, ">").replace(/'/g, "'");
}

function unescapeXmlText(str) {
  if (str === undefined || str === null || str.replace === undefined) {
    throw("invalid string given");
  }

  // The order of replace is important because the & character is used in unescaping.
  return str.replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'").replace(/"/g, "\"").replace(/&/g, "&");
}

function toXmlHeader() {
  return "";
}

function toXmlAttr(name, value) {
  return " " + toXmlValid(name) + "=\"" + escapeXmlText(value) + "\"";
}

function toXmlElem(tagName, attributes) {
  var str = "\n<" + toXmlValid(tagName);
	
  if (attributes !== undefined) {	
    for (var key in attributes) {
      if (attributes.hasOwnProperty(key) === true) {
        str += toXmlAttr(key, attributes[key]);
      }
    }
  }
	
  return str + "/>";
}

function toXmlElemOpen(tagName, attributes) {
  var str = "\n<" + toXmlValid(tagName);
	
  if (attributes !== undefined) {
    for (var key in attributes) {
      if (attributes.hasOwnProperty(key) === true) {
        str += toXmlAttr(key, attributes[key]);
      }
    }
  }
	
  return str + ">";
}

function toXmlElemClose(tagName) {
  return "";
}

function toXmlText(text) {
  return escapeXmlText(text);
}

References

Comparison against null and undefined in Javascript

Values in Javascript can come from

  • Object – object reference
  • String – immutable, 16-bit USC-2
  • Boolean – true, false
  • Number – only 64-bit floating point, IEEE 754 (double)
  • null – empty object reference
  • undefined – unassigned variables and function arguments, missing object properties

Boolean evaluation of values using == and != operators

Values that are interpreted as false:

  • false
  • null
  • undefined
  • "" – the empty string
  • 0 – the number 0
  • NaN – not a number, result of undefined math operation

All other values and objects are interpreted as true.

Working with comparison operators

The == and != operators will do implicit type conversion of it’s arguments to match their type.┬áSince the null and undefined values are both equal with these operators, errors can occur when a missing object property is used in a null-comparison.

By using the === and !== operators, no implicit type conversion will be done. The values null and undefined are not equal using these operators. These operators can be used to check against missing properties and unassigned arguments among other.

Notice: Be careful if updating your old javascript code with === and !== for null-comparison tests. It might be that an undefined-comparison should have been used instead (or both). Failure to recognise these cases can introduce hard-to-catch errors.

References

Code documentation for Javascript and Actionscript 3

Prerequisites for Actionscript documentation

Prerequisites for Javascript documentation

Actionscript 3 Code Documentation

Example documentation:

/**
* class-description
*/
class-declaration
/**
* method-description
*
* @see method-name
* @param name description
* @return description
*/
method-declaration.
/** attribute-description */
attribute-declaration

Extracting the documentation from the sourcecode:

asdoc.exe -source-path /source/directory -doc-sources /source/directory -output /output/directory -window-title "Project Title"

Javascript Code Documentation

Example documentation:

/**
 * class-description
 *
 * @author author
 * @extends class-name
 * @see class-name
 * @version version
 * @constructor
 */
class-constructor-declaration.
/**
 * method-description
 *
 * @see class-name#method-name
 * @param {data-type} name description
 * @return description
 * @type data-type-of-return
 */
method-declaration.

Extracting the documentation from the sourcecode:

perl jsdoc.pl /source/directory --directory /output/directory --project-name "Project Title"

References for this post

Multiple Inheritance in Javascript

Notice

Javascript does not support multiple inheritance, so the given method will break the instanceof operator! Javascript checks inheritance by traversing the linked list prototype.__proto__ for occurences of the requested prototype. This means that one prototype can only contain one reference to another prototype and in effect only inherit from one prototype. By discarding support for the instanceof operator, multiple inheritance can be simulated.

A support function is available that provide similar functionality as the instanceof operator.

Description and implementation details

The function responsible for applying inheritance between classes works by copying all missing attributes/methods from parent class(es) to the inheritance class. In addition, metadata in the class constructors allows for isInstanceOf() comparisons to to made on objects.

/**
 * Check if object is instance of given class.
 *
 * @param {Object} object Object to check inheritance of.
 * @param {Function} classConstructor Constructor function to check inheritance against.
 * @return Boolean indicating success of comparison.
 * @type {Boolean}
 */
function isInstanceOf(object, classConstructor) {
  // Check for class metadata
  if (object.constructor.meta === undefined || classConstructor.meta === undefined) {
    return object instanceof classConstructor; // Use standard inheritance test
  }
 
  // Use inheritance metadata to perform instanceof comparison
  return object.constructor.meta.fromClasses[classConstructor.meta.index] !== undefined;
}
 
/**
 * Applies inheritance to a class (first argument) from classes (second, third, and
 * so on, arguments).
 *
 * Notes:
 *  - This WILL BREAK the instanceof operator! Use the isInstanceOf() function instead.
 *  - Parent classes must be fully declared before calling this function.
 *  - Multiple classes will be copied in sequence.
 *  - Properties that already exists in class will not be copied.
 */
function applyInheritance() {
  // Validate arguments
  if (arguments.length < 2) {
    throw new Error("No inheritance classes given");
  }
 
  var toClass = arguments[0];
  var fromClasses = Array.prototype.slice.call(arguments, 1, arguments.length);

  // Check if class referencer has been created
  if (applyInheritance.allClasses === undefined) {
    applyInheritance.allClasses = [];
  }
 
  // Check for inheritance metadata in toClass
  if (toClass.meta === undefined) {
    toClass.meta = {
      index: applyInheritance.allClasses.length,
      fromClasses : [],
      toClasses: []
    };
    toClass.meta.fromClasses[toClass.meta.index] = true; // class links to itself
    applyInheritance.allClasses.push(toClass);
  }
 
  // Apply inheritance fromClasses
  var fromClass = null;
  for (var i = 0; i < fromClasses.length; i++) {
    fromClass = fromClasses[i];
		
    // Check for inheritance metadata in fromClass
    if (fromClass.meta === undefined) {
      fromClass.meta = {
        index: applyInheritance.allClasses.length,
        fromClasses: [],
        toClasses: []
      };
      fromClasses[i].meta.fromClasses[fromClass.meta.index] = true; // class links to itself
      applyInheritance.allClasses.push(fromClass);
    }
 
    // Link toClass and fromClass
    toClass.meta.fromClasses[fromClass.meta.index] = true;
    fromClass.meta.toClasses[toClass.meta.index] = true;
 
    // Copy prototype fromClass toClass
    for (var property in fromClass.prototype) {
      if (toClass.prototype.hasOwnProperty(property) === false) {
        // Copy missing property from the parent class to the inheritance class
        toClass.prototype[property] = fromClass.prototype[property];
      }
    }
  }
}

Multiple inheritance example:

function Animal(name) {
   // Attributes
   this.name = name;
}

Animal.prototype.makeSound = function (soundOutput) {
   soundOutput.playSound("Unknown");
};

Animal.prototype.getHierarchy = function () {
   return "Animal";
};

function Petable(personality) {
   // Attributes
   this.personality = personality;
}

Petable.prototype.isCompatible = function (personality) {
   return this.personality == personality;
};

function Dog(name, personality, breed) {
   // Call parent constructors
   Animal.call(this, name);
   Petable.call(this, personality);

   // Attributes
   this.breed = breed;
}
// Apply inheritance to Dog from Animal and Petable
applyInheritance(Dog, Animal, Petable);

An example of how to override a method of a parent class:

Dog.prototype.makeSound = function (soundOutput) {
   soundOutput.playSound("Bark");
};

An example of how an overidden method can delegate to it's parent method:

Dog.prototype.getHierarchy = function () {
   // returns "Animal.Dog"
   return Animal.prototype.getHierarchy.apply(this, arguments) + ".Dog";
};

An example of how to call a parent method:

Dog.prototype.isOwner = function (name, personality) {
   if (this.name != name) {
      return false;
   }

   return Petable.prototype.isCompatible.call(this, personality);
};

Notes on calling function objects

References

Associative Arrays in Javascript

Introduction

The associative array (also known as hash map) is similar to the ordinary array. Instead of indexing by integers, the associative array uses string keys. It also lacks the bookkeeping abilities of the ordinary array like sort(), splice() etc. In Javascript every object has the ability to act as an associative array.

Creating an associative array and filling it with some data:

var container = {};
container["someKey"] = 3;
container["someOtherKey"] = someObject;
container["anotherKey"] = "Some text";

// ... or equivalently
var container = {
  someKey: 3,
  someOtherKey: someObject,
  anotherKey: "Some text"
};

Accessing elements in the associative array:

var someValue = container["someKey"];
container["otherKey"] = someValue;

// ... or equivalently
var someValue = container.someKey;
container.otherKey = someValue;

Iterating through all the elements in the associative array:

for (var key in container) {
   // hasOwnProperty() checks that attribute is from non-inherited prototype
   if (container.hasOwnProperty(key)) {
      var element = container[key];
   }
}

Deleting a key in the associative array:

delete container["someKey"];

// ... or equivalently
delete container.someKey;

// Notice: Don't assign null to the deleted key as
// this will re-create the key!

Checking if a key exists in the associative array:

if (container["someKey"] === undefined) {
   // Key does not exist
}

// ... or equivalently
if (container.someKey === undefined) {
   // Key does not exist
}

References