@tuples – experimenting with lisp-like syntax as XML replacement

June 14th, 2009     ,     2 Comments

Motivation

The following post is just an experimentation on using a lisp-like syntax, with hints of JSON, for storing structured data as text. The main motivation for this was a need to have a less verbose alternative to XML, but still be readable and editable (weak point of JSON) and support pattern matching for transforming the data.

The current specification has errors and is incomplete. The programming elements are currently in the toy stage.

Specification

 
(@tuples
 
content: (##
 
  Info
    An experimental data-format using s-expression-like syntax for storing
    tree-based data as text.
 
    A tuple can be a named or unnamed parenthesis that enclose a series of
    expressions separated by space, where each expression is either another
    tuple, primitive or pair bound by a binary operator where
      : gives a key-value pair used for named attribute
      == gives an assertion-rule pair used for pattern matching
      -> gives a substitution-rule pair used for pattern matching
      => gives a combination-rule pair used for pattern matching
 
    The format also specifies primitives for common data like
    integers, floats, bytearrays, booleans and @tuples itself.
 
    An unpaired string can be regarded as a comment.
 
  Syntax
    () - encloses an (un)named tuple
 
    ## - begins a text block (nested text blocks are allowed but
         unbalanced text blocks must use quotation instead)
    ## - ends a text block
 
    "" - encloses a series of quotation escaped characters
    @string - gives one or more strings (explicit form of text block or quoted characters)
 
    :  - a key-value pair
    => - rule that matches the left side and then combines it with the right side
    -> - rule that matches the left side and then substitute it with the right side
    == - rule that matches left side and then asserts that the right side is present
    '' - encloses a variable that either specify a type or give a named reference
 
    @tuples - gives the special tuple that specify content and rules
 
    @bytes - gives a base64 encoded text containing unsigned bytes
    @int - gives one or more signed integers
    @float - gives one or more floating point numbers
    @bool - gives one or more booleans using true and false
 
  Pattern matching
    - Explicit application of pattern matching is handled at program level
    - The @tuple rules attribute stores pattern matching rules
    - The @tuple content attribute stores data
    - Pattern matching variables can be used
        (Data value: 'PI') - as a shared reference within the @tuples content attribute
        ('PI' -> (@float 3.14)) - for substitution within the @tuples rules attribute
        ((Data) == (value: '@float')) - to match type within the @tuples rules attribute
 
  Implicit primitives
    "some characters" - implicit typed string
    -4 1.05 3e-1 - implicit untyped number
    true false - implicit typed boolean
 
  Syntax examples
    - Primitives: "string" 1.05 true (@int 3) (@float 1.05) (@bytes "MTIz")
    - Named tuples: (Document title: "just another format" Document)
    - Unnamed tuples: (title: "just another format")
 
  Parsing syntax
    start            ::= tuple
 
    tuple            ::= unnamed<tuple-body>
                       | named<name,tuple-body>
                       | primitive
    tuple-body       ::= {pair | tuple, pad}
 
    unnamed<body>    ::= '(' body ')'
    named<tag,body>  ::= '(' tag (pad body)? (pad tag)? ')'
    name             ::= [a-zA-Z][-A-Za-z0-9]*
 
    pair             ::= keyvalue-pair | combine-rule | substitute-rule
                       | assert-rule
    keyvalue-pair    ::= name ':' pad tuple
    combine-rule     ::= tuple pad '=>' pad tuple
    substitute-rule  ::= tuple pad '->' pad tuple
    assert-rule      ::= tuple pad '==' pad tuple
 
    primitive        ::= tuples | string | text | bytes | int
                       | float | bool
    tuples           ::= named<'@tuples',tuple-body>
    bytes            ::= named<'@bytes',base64-body>
    base64-body      ::= '"' [a-zA-Z0-9+/=]* '"'
    int              ::= named<'@int',int-body>
    int-body         ::=
    float            ::= named<'@float',float-body>
    float-body       ::=
    bool             ::= bool-body
                       | named<'@bool',bool-body>
    bool-body        ::= 'true' | 'false'
    string           ::= '"' string-body '"'
                       | named<'@string',string-body>
    string-body      ::= <any characters until unescaped quote character>
    text             ::= text-begin text-body text-end
    text-begin       ::= '(##'
    text-end         ::= '##)'
    text-body        ::=  <any characters until balanced text-end>
 
    pad              ::= [/s]+
 
  Examples of invalid expressions
    (Data name: "A" name: "B") - duplicate key-value pair
    Data - neither a primitive or named tuple
    name: "A" - pair not enclosed in parenthesis
    @tuples - primitive not enclosed with parenthesis
    '@byte' - variable does not contain a valid type
 
  Examples of valid expressions
    (name: "Kyrre") - unnamed tuple
    "Some characters!" - implicit (@string "Some characters!")
    1.04 - implicit (@string "1.04")
    true - implicit (@bool true)
##)
 
@tuples)
 

Example

Data with redundant information removed:

 
(@tuples
 
(##
  At the program level the @tuples content will be transformed
  by pattern matching against the rules.
  (@tuples content: (@map content rules))
##)
 
(##
  Use substitution and combination rules to update missing information.
  This can be used for instancing and (re)naming tuples.
##)
rules: (
  (type: '') => (Node)
  (from: '' to: '') => (Connector)
  'test-data' -> (@bytes "QUI9PQ==")
  'instructions' -> (## some data in text format ##)
)
 
content: (
  name: "Group1" type: "Group"
  nodes: (
    (name: "Source" type: "Value" value: 'test-data'
     inputs: ((name: "In" datatype: "bytedata"))
     outputs: ((name: "Out" datatype: "bytedata")))
 
    (name: "Transform" type: "Process" data: 'instructions'
     inputs: ((name: "In" datatype: "bytedata"))
     outputs: ((name: "Out" datatype: "bytedata")))
 
    (name: "Target" type: "Value" value: ""
     inputs: ((name: "In" datatype: "bytedata"))
     outputs: ((name: "Out" datatype: "bytedata")))
  )
 
  connectors: (
    (from: (node: "Source" socket: "Out")
     to: (node: "Transform" socket: "In"))
 
    (from: (node: "Transform" socket: "Out")
     to: (node: "Target" socket: "In"))
  )
)
 
@tuples)
 

The resulting data after the content has been transformed by the rules:

 
(@tuples
 
content:
(Node name: "Group1" type: "Group"
  nodes: (
    (Node name: "Source" type: "Value" value: (@bytes "QUI9PQ==")
     inputs: ((Socket name: "In" datatype: "bytedata"))
     outputs: ((Socket name: "Out" datatype: "bytedata")))
 
    (Node name: "Transform" type: "Process" data: (## some data in text format ##)
     inputs: ((Socket name: "In" datatype: "bytedata"))
     outputs: ((Socket name: "Out" datatype: "bytedata")))
 
    (Node name: "Target" type: "Value" value: "output"
     inputs: ((Socket name: "In" datatype: "bytedata"))
     outputs: ((Socket name: "Out" datatype: "bytedata")))
  )
 
  connectors: (
    (Connector
      from: (node: "Source" socket: "Out")
      to: (node: "Transform" socket: "In"))
 
    (Connector
      from: (node: "Transform" socket: "Out")
      to: (node: "Target" socket: "In"))
  )
)
 
@tuples)
 

Ideas for extensions

  • Chaining together separate files
  • Nesting separate files with pattern matching variables - like referencing large bytedata and instantiating tuples
  • Use separate files for validation, update and typing of data

Structured CSS with tagged HTML

June 14th, 2009     ,     No Comments

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:

 
<div class="menu">
<div class="title">title</div>
<div class="spacedH flowL selectable item">item 1</div>
<div class="spacedH flowL selectable item">item 2</div>
<div class="spacedH flowL selectable item">item 3</div>
<div>
 

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

January 27th, 2009     ,     No Comments

Reminders

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

object A {
  // utility constructors
  apply(arg: String): A = new A("a", arg)
 
  // apply and unapply methods for object pattern matching (like case classes)
 
  // static methods
}
 
// The class definition with primary constructor
class A protected (private val arg1: String, arg2: String) {
  // imports from companion object
  import A._ 
 
  // attributes and primary constructor code
  var busy = false
 
  // methods
  override def toString() = "A"
}
 
val a = A("test")

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 // private variable to hold value
  def value(): Int = v
  def value_= (t: Int) { v = t } // setters returns Unit
}

Adding index 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
    }
  }
}
 
Email("me@host.com") match {
  case Some(name,host) => println("Username is " + name)
  case None => 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 <: (think required mixin) and lower >: (think subclass) bounded type parameters

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

Writing . and () is optional when methods have zero or one arguments

val b = new B
b add 2
println(b toString)

Return keyword is optional (and curly braces for 1-line expressions also)

def test() = "test"

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

def printBig(str: String): Unit = {
  println(str.toUpperCase)
  return () /** explicitly returns the unit value - does nothing but is
                required because functions are expressions */
}
def printBig(str: String) { println(str.toUpperCase) } /** same as previous */

References

Simple serialization with XML strings in Javascript

January 12th, 2009     , ,     No Comments

Introduction

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

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.
<?xml version="1.0" encoding="UTF-8"?>
<Library>
<Authors country="Norway">
<Author name="Petter Dass"/></Authors>
<Books>
<Book title="book1" author="author1"/>
<Book title="book2" author="author1">Ach, So?</Book>
<Book title="book3" author="author2"/>
<Book/></Books></Library>

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, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/'/g, "&apos;");
}
 
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(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&apos;/g, "'").replace(/&quot;/g, "\"").replace(/&amp;/g, "&");
}
 
function toXmlHeader() {
  return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
}
 
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 "</" + toXmlValid(tagName) + ">";
}
 
function toXmlText(text) {
  return escapeXmlText(text);
}

References

Comparison against null and undefined in Javascript

August 5th, 2008     ,     No Comments

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

Deep Pocket Layout – tree-based UI layout

February 22nd, 2008     ,     No Comments

Overall idea

The Deep Pocket Layout, to be regarded as an illustrative name of a common data-structure, uses a specialized tree data-structure for layout of user interface elements (widgets). This data-structure can be used for partitioning the screen-space, similar to the quadtree and kd-tree data-structures, and uses the naming conventions of border layout. In usage, it functions as a tree of either vertically or horizontally linked partitions, much like a merged border/horizontal/vertical layout.

In the following example, the data-structure will be explained as a container widget, as part of an inheritance hierarchy of other widgets.

Introduction by example

The figure above shows the named "pockets" of the data structure. The outmost pockets are used for linking the Pocket Layout Widget (PW) together with other PWs. The center pocket can contain a PW (thus forming a tree) or a different kind of Widget (leaf node). The center pocket specifies the screen-space, while the border pockets acts as internal links to other PWs. When the left or right pockets are linked, they become part of a horizontal layout (top and bottom pockets must always be empty). Conversely, when the top or bottom pockets are linked, they become part of a vertical layout (left and right pockets must always be empty).

The first example below shows how a widget (of any kind) can be inserted by using the center pocket of the PW.

PWs can be arranged into either vertical and horizontal layouts. The figure below shows how a horizontal layout is started. Since P1 it the root of the tree, a new PW P3 is created, replacing the center pocket content of P1. Then P2 is linked with P3, starting a horizontal layout.

The figure below shows how a vertical layout is started. This example is identical to the previous one, but starting a vertical layout instead. (The root constraint for P1 still applies.)

The final example shows how to split a PW in a horizontal layout into a vertical. Here P3 is already part of a horizontal layout. A new PW is created and replaced with P3s center pocket. The vertical layout is then started by linking P6 with P7.

The final deep pocket layout of this example is visualized in the following figure.

Summary

Some features:

  • Enables combination of tree-based vertical/horizontal layouts.
  • Allows the user to insert/remove any PWs in the hierarchy from any depth, thus controlling the complete layout with drag-and-drop.
  • User control over screen space that is delegated within the tree hierarchy.
  • PW linked in either a horizontal or vertical layout can adjust neighboring PWs or delegate request to parent PW.

Implementation extensions:

  • The example used "share equally", but custom ratios of the linked PWs can be handled by the parent PW.
  • Overlay of drag-and-drop targets when dragging is in progress.

References

Code documentation for Javascript and Actionscript 3

January 1st, 2008     , ,     No Comments

Prerequisites for Actionscript documentation

Prerequisites for Javascript documentation

Actionscript 3 Code Documentation

Example documentation:

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

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

December 26th, 2007     , ,     No Comments

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);
};

Note on calling function objects

References

Associative Arrays in Javascript

December 26th, 2007     ,     No Comments

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";

Accessing elements in the associative array:

var element = container["someKey"];
container["aKey"] = someData;

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[key];
// 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
}

References

Hello world!

December 25th, 2007         No Comments

This is a blog by Trond Olsen. It's mainly just a personal diary about programming stuff I've encountered. The content of the posts are not static and may change in the future.