docs: function Mark(typeName, props, contents) {
  		// handle special shorthand
  		if (arguments.length === 1 && (typeName[0] === '{' || typeName[0] === '[' || ws.indexOf(typeName[0]) >= 0)) { 
  			return MARK.parse(typeName); 
  		}
  		
  		// 1. prepare the constructor
  		if (typeof typeName !== 'string') {
  			if (this instanceof Mark) { // called through new operator
  				this[$length] = 0;
  				// no need to do further construction
  				// props, contents are not supported at the moment
  				return;
  			}
  			throw "Type name should be a string";
  		}
  		let con = $ctrs[typeName];
  		if (!con) {
  			con = $ctrs[typeName] = function(){};
  			// con.prototype.constructor is set to con by JS
  			// sets the type name
  			Object.defineProperty(con, 'name', {value:typeName, configurable:true}); // non-writable, as we don't want the name to be changed
  
  			// con.prototype.__proto__ = Array.prototype; // Mark no longer extends Array; Mark is array like, but not array.
  			// con is set to extend Mark, instead of copying all the API functions
  			// for (let a in api) { Object.defineProperty(con.prototype, a, {value:api[a], writable:true, configurable:true}); } // make API functions non-enumerable
  			Object.setPrototypeOf(con.prototype, Mark.prototype);
  		}
  		
  		// 2. create object
  		let obj = Object.create(con.prototype);
  		
  		// 3. copy properties, numeric keys are not allowed
  		if (props) { 
  			for (let p in props) {
  				// accept only non-numeric key; and we do no check key duplication here, last definition wins
  				if (isNaN(p*1)) { obj[p] = props[p]; }
  			}
  		}
  		
  		// 4. copy contents if any
  		obj[$length] = 0;
  		if (contents) { push.call(obj, contents); }
  		return obj;
  	}