docs: function chars(target, splitExp) {
  		var inst = core.base();
  		inst.textBuffer = '';
  		inst.enabled = true;
  		inst.splitExp = (splitExp || /(.*?)\r?\n/g);
  
  		inst.useTarget = function (target) {
  			inst.target = (target || {
  				// dummy
  				writeln: function () {
  					//nothing
  				}
  			});
  		};
  
  		inst.clear = function () {
  			inst.textBuffer = '';
  			inst.splitExp.lastIndex = 0;
  		};
  
  		inst.write = function (str) {
  			if (str === '') {
  				return;
  			}
  			if (inst.enabled) {
  				//fast path
  				inst.textBuffer += str;
  				inst.flush(true);
  			}
  		};
  
  		inst.writeln = function (str) {
  			if (inst.enabled) {
  				//fast path
  				if (arguments.length === 0) {
  					inst.textBuffer += '\n';
  				}
  				else {
  					inst.textBuffer += str + '\n';
  				}
  				inst.flush(true);
  			}
  		};
  
  		inst.flush = function (linesOnly) {
  			if (inst.textBuffer.length > 0) {
  				var match;
  				var end = 0;
  				//TODO verify if we really need a capture group?
  				//     instead not search for line break and use index + length of match + substing
  				while ((match = inst.splitExp.exec(inst.textBuffer))) {
  					inst.target.writeln(match[1]);
  					end = match.index + (match[0].length || 1);
  					inst.splitExp.lastIndex = end;
  				}
  				if (end > 0) {
  					inst.textBuffer = inst.textBuffer.substring(end);
  					inst.splitExp.lastIndex = 0;
  				}
  				if (!linesOnly && inst.textBuffer.length > 0) {
  					inst.target.writeln(inst.textBuffer);
  					inst.textBuffer = 0;
  					inst.splitExp.lastIndex = 0;
  				}
  			}
  		};
  
  		inst.has = function () {
  			return inst.textBuffer.length > 0;
  		};
  
  		inst.toString = function () {
  			return '<miniwrite-chars>';
  		};
  		// use target
  		inst.useTarget(target);
  		return inst;
  	}