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