diff --git a/format.js b/format.js
new file mode 100644
index 0000000..198e686
--- /dev/null
+++ b/format.js
@@ -0,0 +1,51 @@
+define([
+	"./stt/breadcrumb",
+	"./stt/comma",
+	"./stt/custom",
+	"./stt/email",
+	"./stt/filepath",
+	"./stt/formula",
+	"./stt/sql",
+	"./stt/underscore",
+	"./stt/url",
+	"./stt/word",
+	"./stt/xpath"
+], function (breadcrumb, comma, custom, email, filepath, formula, sql, underscore, url, word, xpath) {
+
+	function getHandler(type) { //jshint maxcomplexity: 15
+		switch (type) {
+		case "breadcrumb" :
+			return breadcrumb;
+		case "comma" :
+			return comma;
+		case "email" :
+			return email;
+		case "filepath" :
+			return filepath;
+		case "formula" :
+			return formula;
+		case "sql" :
+			return sql;
+		case "underscore" :
+			return underscore;
+		case "url" :
+			return url;
+		case "word" :
+			return word;
+		case "xpath" :
+			return xpath;
+		default:
+			return custom;
+		}
+	}
+
+	return {
+		getString: function (text, type, args, isRtl, locale) {
+			return getHandler(type).format(text, args, isRtl, false, locale);
+		},
+
+		getHtml: function (text, type, args, isRtl, locale) {
+			return getHandler(type).format(text, args, isRtl, true, locale);
+		}
+	};
+});
diff --git a/misc.js b/misc.js
new file mode 100644
index 0000000..d5259f2
--- /dev/null
+++ b/misc.js
@@ -0,0 +1,106 @@
+define([], function () {
+
+	var isBidiLocale = function (locale) {
+		var lang = !locale ? "" : locale.split("-")[0];
+		if (!lang || lang.length < 2) {
+			return false;
+		}
+		return ["iw", "he", "ar", "fa", "ur"].some(function (bidiLang) {
+			return bidiLang === lang;
+		});
+	};
+	var LRE = "\u202A";
+	var RLE = "\u202B";
+	var PDF = "\u202C";
+	var LRM = "\u200E";
+	var RLM = "\u200F";
+	var LRO = "\u202D";
+	var RLO = "\u202E";
+
+	return {
+		LRE: LRE,
+		RLE: RLE,
+		PDF: PDF,
+		LRM: LRM,
+		RLM: RLM,
+		LRO: LRO,
+		RLO: RLO,
+
+		getLocaleDetails: function (locale) {
+			if (!locale) {
+				locale = typeof navigator === "undefined" ? "" :
+					(navigator.language ||
+					navigator.userLanguage ||
+					"");
+			}
+			locale = locale.toLowerCase();
+			if (isBidiLocale(locale)) {
+				var full = locale.split("-");
+				return {lang: full[0], country: full[1] ? full[1] : ""};
+			}
+			return {lang: "not-bidi"};
+		},
+		
+		removeUcc: function (text) {
+			if (text) {
+				return text.replace(/[\u200E\u200F\u202A-\u202E]/g, "");
+			}
+			return text;
+		},
+
+		getDirection: function (text, dir, guiDir, checkEnd) {
+			if (dir !== "auto" && (/^(rtl|ltr)$/i).test(dir)) {
+				return dir;
+			}
+			guiDir = (/^(rtl|ltr)$/i).test(guiDir) ? guiDir : "ltr";
+			var txt = !checkEnd ? text : text.split("").reverse().join("");
+			var fdc = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(txt);
+			return fdc ? (fdc[0] <= "z" ? "ltr" : "rtl") : guiDir;
+		},
+
+		hasArabicChar: function (text) {
+			var fdc = /[\u0600-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(text);
+			return !!fdc;
+		},
+
+		showMarks: function (text, guiDir) { //jshint maxcomplexity: 15
+			var result = "";
+			for (var i = 0; i < text.length; i++) {
+				var c = "" + text.charAt(i);
+				switch (c) {
+				case LRM:
+					result += "<LRM>";
+					break;
+				case RLM:
+					result += "<RLM>";
+					break;
+				case LRE:
+					result += "<LRE>";
+					break;
+				case RLE:
+					result += "<RLE>";
+					break;
+				case LRO:
+					result += "<LRO>";
+					break;
+				case RLO:
+					result += "<RLO>";
+					break;
+				case PDF:
+					result += "<PDF>";
+					break;
+				default:
+					result += c;
+				}
+			}
+			var mark = typeof(guiDir) === "undefined" || !((/^(rtl|ltr)$/i).test(guiDir)) ? "" :
+				guiDir === "rtl" ? RLO : LRO;
+			return mark + result + (mark === "" ? "" : PDF);
+		},
+
+		hideMarks: function (text) {
+			var txt = text.replace(/<LRM>/g, LRM).replace(/<RLM>/g, RLM).replace(/<LRE>/g, LRE);
+			return txt.replace(/<RLE>/g, RLE).replace(/<LRO>/g, LRO).replace(/<RLO>/g, RLO).replace(/<PDF>/g, PDF);
+		}
+	};
+});
diff --git a/stt/breadcrumb.js b/stt/breadcrumb.js
new file mode 100644
index 0000000..97ba870
--- /dev/null
+++ b/stt/breadcrumb.js
@@ -0,0 +1,29 @@
+define(["./internal/stext"], function (stext) {
+	
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: args.dir ? args.dir : isRtl ? "rtl" : "ltr",
+					subs: {
+						content: ">",
+						continued: true,
+						subDir: isRtl ? "rtl" : "ltr"
+					},
+					cases: [{
+						args: {
+							subs: {
+								content: "<",
+								continued: true,
+								subDir: isRtl ? "ltr" : "rtl"
+							}
+						}
+					}
+					]
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/comma.js b/stt/comma.js
new file mode 100644
index 0000000..643c3c1
--- /dev/null
+++ b/stt/comma.js
@@ -0,0 +1,14 @@
+define(["./internal/stext"], function (stext) {
+	
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: "ltr",
+					points: ","
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/custom.js b/stt/custom.js
new file mode 100644
index 0000000..679a81b
--- /dev/null
+++ b/stt/custom.js
@@ -0,0 +1,14 @@
+define(["./internal/stext"], function (stext) {
+	
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+			var hArgs = {};
+			for (var prop in args) {
+				hArgs[prop] = args[prop];
+			}
+			hArgs.guiDir = isRtl ? "rtl" : "ltr";
+			hArgs.dir = args.dir ? args.dir : hArgs.guiDir;
+			return stext.displayStructure(text, hArgs, !!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/email.js b/stt/email.js
new file mode 100644
index 0000000..db4a588
--- /dev/null
+++ b/stt/email.js
@@ -0,0 +1,40 @@
+define(["./internal/stext", "./handlers/common", "../misc"], function (stext, handler, misc) {
+	function getDir(text, locale) {
+		if (misc.getLocaleDetails(locale).lang !== "ar") {
+			return "ltr";
+		}
+		var ind = text.indexOf("@");
+		if (ind > 0 && ind < text.length - 1) {
+			return misc.hasArabicChar(text.substring(ind + 1)) ? "rtl" : "ltr";
+		}
+		return "ltr";
+	}
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: getDir(text, locale),
+					points: "<>.:,;@",
+					cases: [{
+						handler: handler,
+						args: {
+							bounds: [{
+								startAfter: "\"",
+								endBefore: "\""
+							},
+							{
+								startAfter: "(",
+								endBefore: ")"
+							}
+							],
+							points: ""
+						}
+					}
+					]
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/filepath.js b/stt/filepath.js
new file mode 100644
index 0000000..e9809c6
--- /dev/null
+++ b/stt/filepath.js
@@ -0,0 +1,17 @@
+define(["./internal/stext", "../misc"], function (stext, misc) {
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+			if (!isHtml) {
+				text = misc.removeUcc(text);
+			}
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: "ltr",
+					points: "/\\:."
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/formula.js b/stt/formula.js
new file mode 100644
index 0000000..94fac1c
--- /dev/null
+++ b/stt/formula.js
@@ -0,0 +1,15 @@
+define(["./internal/stext"], function (stext) {
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: "ltr",
+					points: " /%^&[]<>=!?~:.,|()+-*{}",
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/handlers/common.js b/stt/handlers/common.js
new file mode 100644
index 0000000..094fb8a
--- /dev/null
+++ b/stt/handlers/common.js
@@ -0,0 +1,33 @@
+define(["./tools"], function (tools) {
+	
+	return {
+		handle: function (content, segments, args, locale) {
+			var cases = [];
+			if (Array.isArray(args.cases)) {
+				cases = args.cases;
+			}
+			var points = [];
+			if (typeof(args.points) !== "undefined") {
+				if (Array.isArray(args.points)) {
+					points = args.points;
+				} else if (typeof(args.points) === "string") {
+					points = args.points.split("");
+				}
+			}
+			var subs = {};
+			if (typeof(args.subs) === "object") {
+				subs = args.subs;
+			}
+			var aBounds = [];
+			if (Array.isArray(args.bounds)) {
+				aBounds = args.bounds;
+			}
+
+			tools.handleBounds(segments, args, aBounds, content, locale);
+			tools.handleSubcontents(segments, args, subs, content, locale);
+			tools.handleCases(segments, args, cases, content, locale);
+			tools.handlePoints(segments, args, points, content, locale);
+			return segments;
+		}
+	};
+});
diff --git a/stt/handlers/tools.js b/stt/handlers/tools.js
new file mode 100644
index 0000000..e7ada06
--- /dev/null
+++ b/stt/handlers/tools.js
@@ -0,0 +1,261 @@
+define(["../internal/TextSegment"], function (TextSegment) {
+	function initBounds(bounds) {
+		if (!bounds) {
+			return false;
+		}
+		if (typeof(bounds.start) === "undefined") {
+			bounds.start = "";
+		}
+		if (typeof(bounds.end) === "undefined") {
+			bounds.end = "";
+		}
+		if (typeof(bounds.startAfter) !== "undefined") {
+			bounds.start = bounds.startAfter;
+			bounds.after = true;
+		} else {
+			bounds.after = false;
+		}
+		if (typeof(bounds.endBefore) !== "undefined") {
+			bounds.end = bounds.endBefore;
+			bounds.before = true;
+		} else {
+			bounds.before = false;
+		}
+		var startPos = parseInt(bounds.startPos, 10);
+		if (!isNaN(startPos)) {
+			bounds.usePos = true;
+		} else {
+			bounds.usePos = false;
+		}
+		var bLength = parseInt(bounds.length, 10);
+		if (!isNaN(bLength)) {
+			bounds.useLength = true;
+		} else {
+			bounds.useLength = false;
+		}
+		bounds.loops = typeof(bounds.loops) !== "undefined" ? !!bounds.loops : true;
+		return true;
+	}
+	
+	function getBounds(segment, src) {
+		var bounds = {};
+		for (var prop in src) {
+			bounds[prop] = src[prop];
+		}
+		var content = segment.content;
+		var usePos = bounds.usePos && bounds.startPos < content.length;
+		if (usePos) {
+			bounds.start = "";
+			bounds.loops = false;
+		}
+		bounds.bStart = usePos ? bounds.startPos : bounds.start.length > 0 ? content.indexOf(bounds.start) : 0;
+		var useLength = bounds.useLength && bounds.length > 0 && bounds.bStart + bounds.length < content.length;
+		if (useLength) {
+			bounds.end = "";
+		}
+		bounds.bEnd = useLength ? bounds.bStart + bounds.length : bounds.end.length > 0 ?
+				content.indexOf(bounds.end, bounds.bStart + bounds.start.length) + 1 : content.length;
+		if (!bounds.after) {
+			bounds.start = "";
+		}
+		if (!bounds.before) {
+			bounds.end = "";
+		}
+		return bounds;
+	}
+	
+	return {
+		/* jshint maxcomplexity: 16 */
+		handleSubcontents: function (segments, args, subs, origContent, locale) { // jshint unused: false
+			if (!subs.content || typeof(subs.content) !== "string" || subs.content.length === 0) {
+				return segments;
+			}
+			var sLoops = true;
+			if (typeof(subs.loops) !== "undefined") {
+				sLoops = !!subs.loops;
+			}
+			for (var j = 0; true; j++) {
+				if (j >= segments.length) {
+					break;
+				}
+				if (segments[j].isParsed || segments.keep || segments[j].isSeparator) {
+					continue;
+				}
+				var content = segments[j].content;
+				var start = content.indexOf(subs.content);
+				if (start < 0) {
+					continue;
+				}
+				var end;
+				var length = 0;
+				if (subs.continued) {
+					do {
+						length++;
+						end = content.indexOf(subs.content, start + length * subs.content.length);
+					} while (end === 0);
+				} else {
+					length = 1;
+				}
+				end = start + length * subs.content.length;
+				segments.splice(j, 1);
+				if (start > 0) {
+					segments.splice(j, 0, new TextSegment({
+						content: content.substring(0, start),
+						keep: true
+					}));
+					j++;
+				}
+				segments.splice(j, 0, new TextSegment({
+					content: content.substring(start, end),
+					textDirection: subs.subDir
+				}));
+				if (end < content.length) {
+					segments.splice(j + 1, 0, new TextSegment({
+						content: content.substring(end, content.length),
+						keep: true
+					}));
+				}
+				if (!sLoops) {
+					break;
+				}
+			}
+		},
+		
+		/* jshint maxcomplexity: 17 */
+		handleBounds: function (segments, args, aBounds, origContent, locale) { // jshint unused: false
+			for (var i = 0; i < aBounds.length; i++) {
+				if (!initBounds(aBounds[i])) {
+					continue;
+				}
+				for (var j = 0; true; j++) {
+					if (j >= segments.length) {
+						break;
+					}
+					if (segments[j].isParsed || segments[j].inBounds || segments.keep || segments[j].isSeparator) {
+						continue;
+					}
+					var bounds = getBounds(segments[j], aBounds[i]);
+					var start = bounds.bStart;
+					var end = bounds.bEnd;
+					if (start < 0 || end < 0) {
+						continue;
+					}
+					var content = segments[j].content;
+					
+					segments.splice(j, 1);
+					if (start > 0) {
+						segments.splice(j, 0, new TextSegment({
+							content: content.substring(0, start),
+							keep: true
+						}));
+						j++;
+					}
+					if (bounds.start) {
+						segments.splice(j, 0, new TextSegment({
+							content: bounds.start,
+							isSeparator: true
+						}));
+						j++;
+					}
+					segments.splice(j, 0, new TextSegment({
+						content: content.substring(start + bounds.start.length, end - bounds.end.length),
+						textDirection: bounds.subDir,
+						inBounds: true
+					}));
+					if (bounds.end) {
+						j++;
+						segments.splice(j, 0, new TextSegment({
+							content: bounds.end,
+							isSeparator: true
+						}));
+					}
+					if (end + bounds.end.length < content.length) {
+						segments.splice(j + 1, 0, new TextSegment({
+							content: content.substring(end + bounds.end.length, content.length),
+							keep: true
+						}));
+					}
+					if (!bounds.loops) {
+						break;
+					}
+				}
+			}
+			for (i = 0; i < segments.length; i++) {
+				segments[i].inBounds = false;
+			}
+			return segments;
+		},
+			
+		handleCases: function (segments, args, cases, origContent, locale) { //jshint unused: false
+			if (cases.length === 0) {
+				return segments;
+			}
+			var hArgs = {};
+			for (var prop in args) {
+				hArgs[prop] = args[prop];
+			}
+			for (var i =  0; i < cases.length; i++) {
+				if (!cases[i].handler || typeof(cases[i].handler.handle) !== "function") {
+					cases[i].handler = args.commonHandler;
+				}
+				if (cases[i].args) {
+					hArgs.cases = cases[i].args.cases;
+					hArgs.points = cases[i].args.points;
+					hArgs.bounds = cases[i].args.bounds;
+					hArgs.subs = cases[i].args.subs;
+				} else {
+					hArgs.cases = [];
+					hArgs.points = [];
+					hArgs.bounds = [];
+					hArgs.subs = {};
+				}
+				cases[i].handler.handle(origContent, segments, hArgs, locale);
+			}
+			return segments;
+		},
+
+		/* jshint maxcomplexity: 12 */
+		handlePoints: function (segments, args, points, origContent, locale) { //jshint unused: false
+			for (var i = 0; i < points.length; i++) {
+				for (var j = 0; true; j++) {
+					if (j >= segments.length) {
+						break;
+					}
+					if (segments[j].isParsed || segments[j].keep || segments[j].isSeparator) {
+						continue;
+					}
+					var content = segments[j].content;
+					var pos = content.indexOf(points[i]);
+					if (pos >= 0) {
+						segments.splice(j, 1);
+						if (pos > 0) {
+							segments.splice(j, 0, new TextSegment({
+								content: content.substring(0, pos),
+								textDirection: args.subDir
+							}));
+							j++;
+						}
+						segments.splice(j, 0, new TextSegment({
+							content: points[i],
+							isSeparator: true
+						}));
+						if (pos + points[i].length + 1 < content.length) {
+							segments.splice(j + 1, 0, new TextSegment({
+								content: content.substring(pos + points[i].length),
+								textDirection: args.subDir
+							}));
+						}
+					}
+				}
+			}
+			for (i = 0; i < segments.length; i++) {
+				if (segments[i].keep) {
+					segments[i].keep = false;
+				} else {
+					segments[i].isParsed = true;
+				}
+			}
+			return segments;
+		}
+	};
+});
diff --git a/stt/internal/TextSegment.js b/stt/internal/TextSegment.js
new file mode 100644
index 0000000..791c8a2
--- /dev/null
+++ b/stt/internal/TextSegment.js
@@ -0,0 +1,18 @@
+define([
+	"dcl/dcl",
+	"decor/Stateful",
+], function (dcl, Stateful) {
+	var TextSegment = dcl(Stateful, {
+			content: "",
+			actual: "",
+			textDirection: "",
+			isVisible: true,
+			isSeparator: false,
+			isParsed: false,
+			keep: false,
+			inBounds: false,
+			newlyCreated: false
+		}
+	);
+    return TextSegment;
+});
\ No newline at end of file
diff --git a/stt/internal/stext.js b/stt/internal/stext.js
new file mode 100644
index 0000000..b35e17e
--- /dev/null
+++ b/stt/internal/stext.js
@@ -0,0 +1,84 @@
+define(["../handlers/common", "./TextSegment", "../../misc"], function (handler, TextSegment, utils) {
+	var stt = {};
+	
+	// args
+	//   handler: main handler (default - dbidi/stt/handlers/common)
+	//   guiDir: GUI direction (default - "ltr")
+	//   dir: main stt direction (default - guiDir)
+	//	 subDir: direction of subsegments
+	//   points: array of delimiters (default - [])
+	//   bounds: array of definitions of bounds in which handler works
+	//   subs: object defines special handling for some substring if found
+	//   cases: array of additional modules with their args for handling special cases (default - [])
+	function displayStructure(content, args, isHtml, locale) {
+		if (!content || !args) {
+			return content;
+		}
+		if (!args.guiDir) {
+			args.guiDir = "ltr";
+		}
+		if (!args.dir) {
+			args.dir = args.guiDir;
+		}
+		if (typeof(args.points) === "undefined") {
+			args.points = [];
+		}
+		if (!args.cases) {
+			args.cases = [];
+		}
+		if (!args.bounds) {
+			args.bounds = [];
+		}
+		args.commonHandler = handler;
+		var segments = [new TextSegment(
+			{
+				content: content,
+				actual: content
+			})];
+		var parse = handler.handle;
+		if (args.handler && typeof(args.handler) === "function") {
+			parse = args.handler.handle;
+		}
+		parse(content, segments, args, locale);
+		// Actual work (adding UCC or creating <bdi> elements - TODO)
+		return getResult(segments, args, isHtml);
+	}
+	
+	function getResult(segments, args, isHtml) { //jshint unused: false, maxcomplexity: 11
+		var result = "";
+		var checkedDir = "";
+		for (var i = 0; i < segments.length; i++) {
+			if (segments[i].isVisible) {
+				var dir = segments[i].textDirection;
+				if (dir === "auto") {
+					dir = utils.getDirection(segments[i].content, dir, args.guiDir);
+				}
+				if ((/^(rtl|ltr)$/i).test(dir)) {
+					result += (dir === "rtl" ? utils.RLE : utils.LRE) + segments[i].content + utils.PDF;
+					checkedDir = dir;
+				}
+				else {
+					result += segments[i].content;
+					checkedDir = utils.getDirection(segments[i].content, dir, args.guiDir, true);
+				}
+				if (checkedDir !== args.dir && i < segments.length - 1) {
+					result += args.dir === "rtl" ? utils.RLM : utils.LRM;
+				}
+			}
+		}
+		var sttDir = args.dir === "auto" ? utils.getDirection(segments[0].actual, args.dir, args.guiDir) : args.dir;
+		if (sttDir !== args.guiDir) {
+			result = (sttDir === "rtl" ? utils.RLE : utils.LRE) + result + utils.PDF;
+		}
+		return result;
+	}
+	
+	function restore(text, isHtml) { //jshint unused: false
+		return text;
+	}
+	
+	stt.displayStructure = displayStructure;
+	stt.restore = restore;
+	
+	return stt;
+});
diff --git a/stt/sql.js b/stt/sql.js
new file mode 100644
index 0000000..2823600
--- /dev/null
+++ b/stt/sql.js
@@ -0,0 +1,56 @@
+define(["./internal/stext", "./handlers/common"], function (stext, handler) {
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: "ltr",
+					points: "\t!#%&()*+,-./:;<=>?|[]{}",
+					cases: [{
+						handler: handler,
+						args: {
+							bounds: [{
+								startAfter: "/*",
+								endBefore: "*/"
+							},
+							{
+								startAfter: "--",
+								end: "\n"
+							},
+							{
+								startAfter: "--"
+							}
+							]
+						}
+					},
+					{
+						handler: handler,
+						args: {
+							subs: {
+								content: " ",
+								continued: true
+							}
+						}
+					},
+					{
+						handler: handler,
+						args: {
+							bounds: [{
+								startAfter: "'",
+								endBefore: "'"
+							},
+							{
+								startAfter: "\"",
+								endBefore: "\""
+							}
+							]
+						}
+					}
+					]
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/underscore.js b/stt/underscore.js
new file mode 100644
index 0000000..88199c7
--- /dev/null
+++ b/stt/underscore.js
@@ -0,0 +1,14 @@
+define(["./internal/stext"], function (stext) {
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: "ltr",
+					points: "_"
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/url.js b/stt/url.js
new file mode 100644
index 0000000..337427d
--- /dev/null
+++ b/stt/url.js
@@ -0,0 +1,15 @@
+define(["./internal/stext"], function (stext) {
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+			//text = stext.restore(text, !!isHtml);
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: "ltr",
+					points: ":?#/@.[]="
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/word.js b/stt/word.js
new file mode 100644
index 0000000..bcbb2d2
--- /dev/null
+++ b/stt/word.js
@@ -0,0 +1,15 @@
+define(["./internal/stext"], function (stext) {
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: args.dir ? args.dir : isRtl ? "rtl" : "ltr",
+					points: " ,.!?;:",
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/stt/xpath.js b/stt/xpath.js
new file mode 100644
index 0000000..1a00fd3
--- /dev/null
+++ b/stt/xpath.js
@@ -0,0 +1,31 @@
+define(["./internal/stext", "./handlers/common"], function (stext, handler) {
+
+	return {
+		format: function (text, args, isRtl, isHtml, locale) {
+
+			return stext.displayStructure(text,
+				{
+					guiDir: isRtl ? "rtl" : "ltr",
+					dir: "ltr",
+					points: " /[]<>=!:@.|()+-*",
+					cases: [{
+						handler: handler,
+						args: {
+							bounds: [{
+								startAfter: "\"",
+								endBefore: "\""
+							},
+							{
+								startAfter: "'",
+								endBefore: "'"
+							}
+							],
+							points: ""
+						}
+					}
+					]
+				},
+				!!isHtml, locale);
+		}
+	};
+});
diff --git a/tests/help.txt b/tests/help.txt
new file mode 100644
index 0000000..8bfd0a7
--- /dev/null
+++ b/tests/help.txt
@@ -0,0 +1,54 @@
+<CONFIG> Here should be placed Object, which may have the following properties:
+    "handler":       Module, which performs formatting. If not defined, dbidi/stt/common is used.
+    "points":        Defines delimiters - characters or substrings, which determine the structure.
+                     Value may be written as string or array.
+                     Examples:
+                         "points": "./;"
+                         "points": [".","/",";","/*","*/"]
+    "dir":           Main base direction of the structure. Can be one of "ltr", "rtl" and "auto".
+    "subDir":        Defines direction of subsegments.
+    "bounds":        Defines the boundaries of text segment, within which the search of delimiters will be done.
+                     Boundaries are considered as kind of delimiters.
+                     Value should be written as array of objects.
+                     {bounds[n]}        Object may have the following properties:
+                     "start":           Substring, by which text segment is started. 
+                     "startAfter":      Substring used as delimiter for the start of the segment.
+                                        If defined, property "start" is ignored.
+                                        If both "start" and "startAfter" undefined, segment starts 
+                                        from the beginning of the whole text.
+                     "startPos":        Position, from which text segments is started.
+                                        If defined, properties "start" and "startAfter" are ignored.
+                     "end":             Substring, by which text segment is ended.
+                     "endBefore":       Substring used as delimiter for the end of the segment.
+                                        If defined, property "end" is ignored.
+                                        If both "end" and "endBefore" are undefined, segments ends 
+                                        in the end of the whole text.
+                     "length":          The length of the segment. If defined, properties "end" and 
+                                        "endBefore" are ignored.
+                     "loops":           If set to "false", suitable segment is searched only once.
+                                        If "startPos" is defined, "loops" is set to "false".
+                                        In opposite case by default it is set to "true".
+                     "subDir":          Direction of subsegments, found in the segment in accordance with "bounds".
+                                        Can be one of "ltr", "rtl" and "auto".
+                     Examples:
+                         "bounds": [{"start": "/*", "end": "*/"}]
+                         "bounds": [{"startPos": "10", "length": "5"}, {"start": "//"}]
+    "subs":          Defines segment of the text by its content.
+                     Value should be written as object.
+                     {subs}             Object may have the following properties:
+                     "content":         Content of the segment.
+                     "continued":       If "true", successive segments with the same "content" are concatenated.
+                     "loops":           If "false", segment with given content is searched only once.
+                     "subDir":          Direction of the segment.
+    "cases":         Defines special handling for some parts of the text.
+                     Value should be written as array of objects.
+                     {cases[n]}         Object may have the following properties:
+                     "handler":         Module, which performs formatting. If not defined, dbidi/stt/common is used.
+                     "args":            Object, which may have all of above-mentioned properties.
+					
+    Note the sequence of calls:
+        1) Handle bounds
+        2) Handle segments with given content
+        3) Handle cases
+        4) Handle points
+</CONFIG>
\ No newline at end of file
diff --git a/tests/testFormat.html b/tests/testFormat.html
new file mode 100644
index 0000000..4e9c2ab
--- /dev/null
+++ b/tests/testFormat.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-type" content="text/html; charset=utf-8">
+<title>Test bidi formatting</title>
+
+<script type="text/javascript" src="../../requirejs/require.js"></script>
+	<script type="text/javascript">
+		require.config({
+			baseUrl: "../../",
+			//config: {
+				"requirejs-dplugins/has": {
+					"bidi": true
+				}
+			//}
+		});		
+		require([
+			"dbidi/format",
+			"dbidi/misc",
+			"delite/register",
+			"requirejs-text/text!dbidi/tests/help.txt",
+			"deliteful/Button",
+			"requirejs-domready/domReady!"
+		], function (format, misc, register, helptxt) {
+			register.parse();
+			guiDir = "ltr";
+			mMisc = misc;
+			fFormat = format;
+			local = document.documentElement.lang? document.documentElement.lang : "en";
+			predef = "CUSTOM";
+			errMsg = "Synax Error!!!   Syntax Error!!!";
+			htxt = helptxt.replace(/<.*>/g,"");
+			inpb = document.getElementById("inpb");
+			outb = document.getElementById("outb");
+			inArea = document.getElementById("inp");
+			outArea = document.getElementById("out");
+			confArea = document.getElementById("config");
+			b1 = document.getElementById('b1');
+			b2 = document.getElementById('b2');
+			b3 = document.getElementById('b3');
+			b4 = document.getElementById('b4');
+			s1 = document.getElementById('s1');
+			s2 = document.getElementById('s2');
+			s3 = document.getElementById('s3');
+			bHelp = document.getElementById('help');
+
+			inpb.onclick = function () {
+				if (inpb.innerHTML === "To Logical") {
+					inArea.value = (guiDir === "ltr"? misc.LRO : misc.RLO) + inArea.value + misc.PDF;
+					inpb.innerHTML = "To Visual";
+				} else {
+					inArea.value = misc.removeUcc(inArea.value);
+					inpb.innerHTML = "To Logical";
+				}
+			};
+			outb.onclick = function () {
+				if (outb.innerHTML === "Show UCC") {
+					outArea.value = misc.showMarks(outArea.value);
+					outb.innerHTML = "Hide UCC";
+				} else {
+					outArea.value = misc.hideMarks(outArea.value);
+					outb.innerHTML = "Show UCC";
+				}
+			};
+			b4.onclick = function () {
+				var args = {};
+				if (confArea.value !== "") {
+					try {
+						args = JSON.parse(confArea.value);						
+					} catch (e) {
+						outArea.value = errMsg;
+						return;
+					}
+				}
+				if (outb.innerHTML === 'Show UCC') {
+					outArea.value = format.getString(mMisc.removeUcc(inArea.value), predef, args, guiDir === "rtl", local);
+				} else {
+					outArea.value = misc.showMarks(format.getString(mMisc.removeUcc(inArea.value), predef, args, guiDir === "rtl", local));
+				}
+			};
+			bHelp.onclick = function () {
+				if (this.innerHTML === 'Help') {
+					confArea.origContent = confArea.value; 
+					confArea.value = htxt.replace('   ',' ');
+					[inArea, outArea, b1, b2, b3, b4, s1, s2, s3, inpb, outb].forEach(function (element) {
+						element.disabled = true;
+					});
+					confArea.origDisabled = confArea.disabled;
+					confArea.disabled = false;
+					this.innerHTML = 'Return';
+				} else {
+					[inArea, outArea, b1, b2, b3, b4, s1, s2, s3, inpb, outb].forEach(function (element) {
+						element.disabled = false;
+					})
+					confArea.disabled = confArea.origDisabled;
+					this.innerHTML = 'Help';
+					confArea.value = confArea.origContent;
+					confArea.origContent = '';
+				}
+			};
+			s1.onchange = function () {
+				inArea.dir = this.value.toLowerCase();
+				outArea.dir = this.value.toLowerCase();
+				guiDir = this.value.toLowerCase();
+				var inpVal = misc.removeUcc(inArea.value);
+				var args = {};
+				if (confArea.value !== "") {
+					try {
+						args = JSON.parse(confArea.value);						
+					} catch (e) {
+						outArea.value = errMsg;
+						return;
+					}
+				}
+				if (outb.innerHTML !== 'Show UCC') {
+					outArea.value = misc.showMarks(format.getString(inpVal, predef, args, guiDir === 'rtl', local));
+				} else {
+					outArea.value = format.getString(inpVal, predef, args, guiDir === 'rtl', local);
+				}
+			};
+			s2.onchange = function () {
+				local = this.value.toLowerCase();
+				document.documentElement.lang = local; 
+				var inpVal = misc.removeUcc(inArea.value);
+				var args = {};
+				if (confArea.value !== "") {
+					try {
+						args = JSON.parse(confArea.value);						
+					} catch (e) {
+						outArea.value = errMsg;
+						return;
+					}
+				}				
+				if (outb.innerHTML !== 'Show UCC') {
+					outArea.value = misc.showMarks(format.getString(inpVal, predef, args, guiDir === 'rtl', local));
+				} else {
+					outArea.value = format.getString(inpVal, predef, args, guiDir === 'rtl', local);
+				}
+			};
+			s3.onchange = function () {
+				var inpVal = misc.removeUcc(inArea.value);
+				predef = this.value.toLowerCase();
+				if (predef !== 'custom') {
+					confArea.disabled = true;
+				} else {
+					confArea.disabled = false;
+				}
+				var args = {};
+				if (confArea.value !== "") {
+					try {
+						args = JSON.parse(confArea.value);						
+					} catch (e) {
+						outArea.value = errMsg;
+						return;
+					}
+				}				
+				if (outb.innerHTML !== 'Show UCC') {
+					outArea.value = misc.showMarks(format.getString(inpVal, predef, args, guiDir === 'rtl', local));
+				} else {
+					outArea.value = format.getString(inpVal, predef, args, guiDir === 'rtl', local);
+				}
+			};
+		});
+	</script>
+</head>
+<body>
+<table style="width: 100%; border-collapse: separate; border-spacing: 10px;">
+<tr>
+
+<td style="width: 80%">
+<textarea id="inp" rows=2 style="width: 100%;" spellcheck="false"></textarea>
+<br>
+<textarea id="out" rows=2 style="width: 100%;" spellcheck="false"></textarea>
+<br>
+<textarea id="config" rows="20" style="width: 100%;" spellcheck="false" wrap="off"></textarea>
+<br>
+<button id="b1" onclick="config.value = ''">Clear config</button>
+<button id="b2" onclick="inp.value = ''">Clear input</button>
+<button id="b3" onclick="out.value = ''">Clear output</button>
+<button id="help">Help</button>
+</td>
+
+<td>
+<button id="inpb" style="position: relative; top: -115px;">To Logical</button>
+<br>
+<button id="outb" style="position: relative; top: -80px;">Show UCC</button>
+<br>
+<div style="position: relative; top: -50px;">
+GUI direction:
+<br>
+<select style="width: 100px;" id="s1">
+   <option> LTR </option>
+   <option> RTL </option>
+</select>
+</div>
+<div style="position: relative; top: -10px;">
+Locale:
+<br>
+<select style="width: 100px;" id="s2">
+   <option> EN </option>
+   <option> HE </option>
+   <option> AR </option>
+</select>
+</div>
+<div style="position: relative; top: 30px;">
+Handler:
+<br>
+<select style="width: 100px;" id="s3">
+   <option> CUSTOM </option>
+   <option> BREADCRUMB </option>
+   <option> COMMA </option>
+   <option> EMAIL </option>
+   <option> FILEPATH </option>
+   <option> FORMULA </option>
+   <option> SQL </option>
+   <option> UNDERSCORE </option>
+   <option> URL </option>
+   <option> WORD </option>
+   <option> XPATH </option>
+</select>
+</div>
+<div>
+<button id="b4" style="position: relative; top: 90px; height: 50px;">Format</button>
+</div>
+</td>
+</tr>
+</table>
+</body>
+</html>
\ No newline at end of file