From 14a787e17279901b3abd90ae475b2bddb68725fb Mon Sep 17 00:00:00 2001 From: Semion Chichelnitsky Date: Wed, 24 Dec 2014 19:20:36 +0200 Subject: [PATCH 1/3] Formatting structured text --- format.js | 51 +++++++ misc.js | 106 +++++++++++++++ stt/breadcrumb.js | 29 ++++ stt/comma.js | 14 ++ stt/custom.js | 14 ++ stt/email.js | 42 ++++++ stt/filepath.js | 17 +++ stt/formula.js | 15 +++ stt/handlers/common.js | 33 +++++ stt/handlers/tools.js | 261 ++++++++++++++++++++++++++++++++++++ stt/internal/TextSegment.js | 18 +++ stt/internal/stext.js | 84 ++++++++++++ stt/sql.js | 59 ++++++++ stt/underscore.js | 14 ++ stt/url.js | 15 +++ stt/word.js | 15 +++ stt/xpath.js | 33 +++++ tests/help.txt | 54 ++++++++ tests/testFormat.html | 229 +++++++++++++++++++++++++++++++ 19 files changed, 1103 insertions(+) create mode 100644 format.js create mode 100644 misc.js create mode 100644 stt/breadcrumb.js create mode 100644 stt/comma.js create mode 100644 stt/custom.js create mode 100644 stt/email.js create mode 100644 stt/filepath.js create mode 100644 stt/formula.js create mode 100644 stt/handlers/common.js create mode 100644 stt/handlers/tools.js create mode 100644 stt/internal/TextSegment.js create mode 100644 stt/internal/stext.js create mode 100644 stt/sql.js create mode 100644 stt/underscore.js create mode 100644 stt/url.js create mode 100644 stt/word.js create mode 100644 stt/xpath.js create mode 100644 tests/help.txt create mode 100644 tests/testFormat.html diff --git a/format.js b/format.js new file mode 100644 index 0000000..ed1321b --- /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..1062fc9 --- /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 += ""; + break; + case RLM: + result += ""; + break; + case LRE: + result += ""; + break; + case RLE: + result += ""; + break; + case LRO: + result += ""; + break; + case RLO: + result += ""; + break; + case PDF: + result += ""; + 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) { + return text.replace(//g, LRM).replace(//g, RLM).replace(//g, LRE). + replace(//g, RLE).replace(//g, LRO).replace(//g, RLO).replace(//g, PDF); + } + }; +}); diff --git a/stt/breadcrumb.js b/stt/breadcrumb.js new file mode 100644 index 0000000..6a46859 --- /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..6d76b09 --- /dev/null +++ b/stt/email.js @@ -0,0 +1,42 @@ +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: [ + { + start: "\"", + end: "\"" + }, + { + start: "(", + end: ")" + } + ], + points: "" + } + } + ] + }, + !!isHtml, locale); + } + }; +}); diff --git a/stt/filepath.js b/stt/filepath.js new file mode 100644 index 0000000..4ab9bee --- /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..74ff3e0 --- /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..ed5b533 --- /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); + if (!isNaN(startPos)) { + bounds.usePos = true; + } else { + bounds.usePos = false; + } + var bLength = parseInt(bounds.length); + 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..59585ed --- /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..8502fc0 --- /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 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?|[]{}", + cases: [ + { + handler: handler, + args: { + bounds: [ + { + start: "/*", + end: "*/" + }, + { + start: "--", + end: "\n" + }, + { + start: "--" + } + ] + } + }, + { + handler: handler, + args: { + subs: { + content: " ", + continued: true + } + } + }, + { + handler: handler, + args: { + bounds: [ + { + start: "'", + end: "'" + }, + { + start: "\"", + end: "\"" + } + ] + } + } + ] + }, + !!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..53e4ed7 --- /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..6daaf26 --- /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..df7168e --- /dev/null +++ b/stt/xpath.js @@ -0,0 +1,33 @@ +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: [ + { + start: "\"", + end: "\"" + }, + { + start: "'", + end: "'" + } + ], + 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 @@ + 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 + \ 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 @@ + + + + +Test bidi formatting + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+ +
+ +
+
+GUI direction: +
+ +
+
+Locale: +
+ +
+
+Handler: +
+ +
+
+ +
+
+ + \ No newline at end of file From efdb74ad9b0905e4c022a684e2f066102754a8d0 Mon Sep 17 00:00:00 2001 From: Semion Chichelnitsky Date: Wed, 24 Dec 2014 21:27:46 +0200 Subject: [PATCH 2/3] Remove jshint errors --- format.js | 6 +-- misc.js | 18 ++++---- stt/breadcrumb.js | 18 ++++---- stt/email.js | 36 ++++++++-------- stt/filepath.js | 2 +- stt/handlers/common.js | 2 +- stt/handlers/tools.js | 16 +++---- stt/internal/TextSegment.js | 2 +- stt/internal/stext.js | 22 +++++----- stt/sql.js | 85 ++++++++++++++++++------------------- stt/url.js | 2 +- stt/word.js | 2 +- stt/xpath.js | 34 +++++++-------- 13 files changed, 119 insertions(+), 126 deletions(-) diff --git a/format.js b/format.js index ed1321b..198e686 100644 --- a/format.js +++ b/format.js @@ -10,9 +10,9 @@ define([ "./stt/url", "./stt/word", "./stt/xpath" - ], function (breadcrumb, comma, custom, email, filepath, formula, sql, underscore, url, word, xpath) { +], function (breadcrumb, comma, custom, email, filepath, formula, sql, underscore, url, word, xpath) { - function getHandler (type) { //jshint maxcomplexity: 15 + function getHandler(type) { //jshint maxcomplexity: 15 switch (type) { case "breadcrumb" : return breadcrumb; @@ -28,7 +28,7 @@ define([ return sql; case "underscore" : return underscore; - case "url" : + case "url" : return url; case "word" : return word; diff --git a/misc.js b/misc.js index 1062fc9..d5259f2 100644 --- a/misc.js +++ b/misc.js @@ -36,7 +36,7 @@ define([], function () { locale = locale.toLowerCase(); if (isBidiLocale(locale)) { var full = locale.split("-"); - return {lang: full[0], country: full[1]? full[1] : ""}; + return {lang: full[0], country: full[1] ? full[1] : ""}; } return {lang: "not-bidi"}; }, @@ -53,7 +53,7 @@ define([], function () { return dir; } guiDir = (/^(rtl|ltr)$/i).test(guiDir) ? guiDir : "ltr"; - var txt = !checkEnd? text : text.split("").reverse().join(""); + 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; }, @@ -68,7 +68,7 @@ define([], function () { for (var i = 0; i < text.length; i++) { var c = "" + text.charAt(i); switch (c) { - case LRM: + case LRM: result += ""; break; case RLM: @@ -76,16 +76,16 @@ define([], function () { break; case LRE: result += ""; - break; + break; case RLE: result += ""; - break; + break; case LRO: result += ""; - break; + break; case RLO: result += ""; - break; + break; case PDF: result += ""; break; @@ -99,8 +99,8 @@ define([], function () { }, hideMarks: function (text) { - return text.replace(//g, LRM).replace(//g, RLM).replace(//g, LRE). - replace(//g, RLE).replace(//g, LRO).replace(//g, RLO).replace(//g, PDF); + var txt = text.replace(//g, LRM).replace(//g, RLM).replace(//g, LRE); + return txt.replace(//g, RLE).replace(//g, LRO).replace(//g, RLO).replace(//g, PDF); } }; }); diff --git a/stt/breadcrumb.js b/stt/breadcrumb.js index 6a46859..92f406c 100644 --- a/stt/breadcrumb.js +++ b/stt/breadcrumb.js @@ -6,21 +6,21 @@ define(["./internal/stext"], function (stext) { return stext.displayStructure(text, { guiDir: isRtl ? "rtl" : "ltr", - dir: args.dir ? args.dir : 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" - } - } - } + args: { + subs: { + content: "<", + continued: true, + subDir: isRtl ? "ltr" : "rtl" + } + } + } ] }, !!isHtml, locale); diff --git a/stt/email.js b/stt/email.js index 6d76b09..9131631 100644 --- a/stt/email.js +++ b/stt/email.js @@ -1,5 +1,5 @@ define(["./internal/stext", "./handlers/common", "../misc"], function (stext, handler, misc) { - function getDir (text, locale) { + function getDir(text, locale) { if (misc.getLocaleDetails(locale).lang !== "ar") { return "ltr"; } @@ -17,24 +17,22 @@ define(["./internal/stext", "./handlers/common", "../misc"], function (stext, ha guiDir: isRtl ? "rtl" : "ltr", dir: getDir(text, locale), points: "<>.:,;@", - cases: [ - { - handler: handler, - args: { - bounds: [ - { - start: "\"", - end: "\"" - }, - { - start: "(", - end: ")" - } - ], - points: "" - } - } - ] + cases: [{ + handler: handler, + args: { + bounds: [{ + start: "\"", + end: "\"" + }, + { + start: "(", + end: ")" + } + ], + points: "" + } + } + ] }, !!isHtml, locale); } diff --git a/stt/filepath.js b/stt/filepath.js index 4ab9bee..e9809c6 100644 --- a/stt/filepath.js +++ b/stt/filepath.js @@ -11,7 +11,7 @@ define(["./internal/stext", "../misc"], function (stext, misc) { dir: "ltr", points: "/\\:." }, - !!isHtml, locale); + !!isHtml, locale); } }; }); diff --git a/stt/handlers/common.js b/stt/handlers/common.js index 74ff3e0..094fb8a 100644 --- a/stt/handlers/common.js +++ b/stt/handlers/common.js @@ -5,7 +5,7 @@ define(["./tools"], function (tools) { var cases = []; if (Array.isArray(args.cases)) { cases = args.cases; - } + } var points = []; if (typeof(args.points) !== "undefined") { if (Array.isArray(args.points)) { diff --git a/stt/handlers/tools.js b/stt/handlers/tools.js index ed5b533..e7ada06 100644 --- a/stt/handlers/tools.js +++ b/stt/handlers/tools.js @@ -1,10 +1,10 @@ define(["../internal/TextSegment"], function (TextSegment) { - function initBounds (bounds) { + function initBounds(bounds) { if (!bounds) { return false; } if (typeof(bounds.start) === "undefined") { - bounds.start = ""; + bounds.start = ""; } if (typeof(bounds.end) === "undefined") { bounds.end = ""; @@ -21,13 +21,13 @@ define(["../internal/TextSegment"], function (TextSegment) { } else { bounds.before = false; } - var startPos = parseInt(bounds.startPos); + var startPos = parseInt(bounds.startPos, 10); if (!isNaN(startPos)) { bounds.usePos = true; } else { bounds.usePos = false; } - var bLength = parseInt(bounds.length); + var bLength = parseInt(bounds.length, 10); if (!isNaN(bLength)) { bounds.useLength = true; } else { @@ -37,7 +37,7 @@ define(["../internal/TextSegment"], function (TextSegment) { return true; } - function getBounds (segment, src) { + function getBounds(segment, src) { var bounds = {}; for (var prop in src) { bounds[prop] = src[prop]; @@ -53,7 +53,7 @@ define(["../internal/TextSegment"], function (TextSegment) { if (useLength) { bounds.end = ""; } - bounds.bEnd = useLength ? bounds.bStart + bounds.length : bounds.end.length > 0 ? + 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 = ""; @@ -225,7 +225,7 @@ define(["../internal/TextSegment"], function (TextSegment) { continue; } var content = segments[j].content; - var pos = content.indexOf(points[i]); + var pos = content.indexOf(points[i]); if (pos >= 0) { segments.splice(j, 1); if (pos > 0) { @@ -243,7 +243,7 @@ define(["../internal/TextSegment"], function (TextSegment) { segments.splice(j + 1, 0, new TextSegment({ content: content.substring(pos + points[i].length), textDirection: args.subDir - })); + })); } } } diff --git a/stt/internal/TextSegment.js b/stt/internal/TextSegment.js index 59585ed..791c8a2 100644 --- a/stt/internal/TextSegment.js +++ b/stt/internal/TextSegment.js @@ -14,5 +14,5 @@ define([ newlyCreated: false } ); - return TextSegment; + return TextSegment; }); \ No newline at end of file diff --git a/stt/internal/stext.js b/stt/internal/stext.js index 8502fc0..b35e17e 100644 --- a/stt/internal/stext.js +++ b/stt/internal/stext.js @@ -1,4 +1,4 @@ -define(["../handlers/common","./TextSegment","../../misc"], function (handler,TextSegment,utils) { +define(["../handlers/common", "./TextSegment", "../../misc"], function (handler, TextSegment, utils) { var stt = {}; // args @@ -10,7 +10,7 @@ define(["../handlers/common","./TextSegment","../../misc"], function (handler,Te // 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) { + function displayStructure(content, args, isHtml, locale) { if (!content || !args) { return content; } @@ -20,7 +20,7 @@ define(["../handlers/common","./TextSegment","../../misc"], function (handler,Te if (!args.dir) { args.dir = args.guiDir; } - if (typeof(args.points) === "undefined" ) { + if (typeof(args.points) === "undefined") { args.points = []; } if (!args.cases) { @@ -30,7 +30,7 @@ define(["../handlers/common","./TextSegment","../../misc"], function (handler,Te args.bounds = []; } args.commonHandler = handler; - var segments = [new TextSegment ( + var segments = [new TextSegment( { content: content, actual: content @@ -41,20 +41,20 @@ define(["../handlers/common","./TextSegment","../../misc"], function (handler,Te } parse(content, segments, args, locale); // Actual work (adding UCC or creating elements - TODO) - return getResult(segments, args, isHtml); + return getResult(segments, args, isHtml); } - function getResult (segments, args, isHtml) { //jshint unused: false, maxcomplexity: 11 + function getResult(segments, args, isHtml) { //jshint unused: false, maxcomplexity: 11 var result = ""; var checkedDir = ""; - for (var i=0; i?|[]{}", - cases: [ - { - handler: handler, - args: { - bounds: [ - { - start: "/*", - end: "*/" - }, - { - start: "--", - end: "\n" - }, - { - start: "--" - } - ] - } - }, - { - handler: handler, - args: { - subs: { - content: " ", - continued: true - } - } - }, - { - handler: handler, - args: { - bounds: [ - { - start: "'", - end: "'" - }, - { - start: "\"", - end: "\"" - } - ] - } - } - ] + cases: [{ + handler: handler, + args: { + bounds: [{ + start: "/*", + end: "*/" + }, + { + start: "--", + end: "\n" + }, + { + start: "--" + } + ] + } + }, + { + handler: handler, + args: { + subs: { + content: " ", + continued: true + } + } + }, + { + handler: handler, + args: { + bounds: [{ + start: "'", + end: "'" + }, + { + start: "\"", + end: "\"" + } + ] + } + } + ] }, !!isHtml, locale); } diff --git a/stt/url.js b/stt/url.js index 53e4ed7..337427d 100644 --- a/stt/url.js +++ b/stt/url.js @@ -9,7 +9,7 @@ define(["./internal/stext"], function (stext) { dir: "ltr", points: ":?#/@.[]=" }, - !!isHtml, locale); + !!isHtml, locale); } }; }); diff --git a/stt/word.js b/stt/word.js index 6daaf26..bcbb2d2 100644 --- a/stt/word.js +++ b/stt/word.js @@ -6,7 +6,7 @@ define(["./internal/stext"], function (stext) { return stext.displayStructure(text, { guiDir: isRtl ? "rtl" : "ltr", - dir: args.dir ? args.dir : isRtl? "rtl" : "ltr", + dir: args.dir ? args.dir : isRtl ? "rtl" : "ltr", points: " ,.!?;:", }, !!isHtml, locale); diff --git a/stt/xpath.js b/stt/xpath.js index df7168e..df3d0fb 100644 --- a/stt/xpath.js +++ b/stt/xpath.js @@ -8,24 +8,22 @@ define(["./internal/stext", "./handlers/common"], function (stext, handler) { guiDir: isRtl ? "rtl" : "ltr", dir: "ltr", points: " /[]<>=!:@.|()+-*", - cases: [ - { - handler: handler, - args: { - bounds: [ - { - start: "\"", - end: "\"" - }, - { - start: "'", - end: "'" - } - ], - points: "" - } - } - ] + cases: [{ + handler: handler, + args: { + bounds: [{ + start: "\"", + end: "\"" + }, + { + start: "'", + end: "'" + } + ], + points: "" + } + } + ] }, !!isHtml, locale); } From 25c629410fe01df1af5619178f109f05a50589c8 Mon Sep 17 00:00:00 2001 From: Semion Chichelnitsky Date: Tue, 30 Dec 2014 14:10:30 +0200 Subject: [PATCH 3/3] Add 'startAfter' and 'endBegin' --- stt/breadcrumb.js | 14 +++++----- stt/email.js | 28 +++++++++---------- stt/sql.js | 68 +++++++++++++++++++++++------------------------ stt/xpath.js | 28 +++++++++---------- 4 files changed, 69 insertions(+), 69 deletions(-) diff --git a/stt/breadcrumb.js b/stt/breadcrumb.js index 92f406c..97ba870 100644 --- a/stt/breadcrumb.js +++ b/stt/breadcrumb.js @@ -13,13 +13,13 @@ define(["./internal/stext"], function (stext) { subDir: isRtl ? "rtl" : "ltr" }, cases: [{ - args: { - subs: { - content: "<", - continued: true, - subDir: isRtl ? "ltr" : "rtl" - } - } + args: { + subs: { + content: "<", + continued: true, + subDir: isRtl ? "ltr" : "rtl" + } + } } ] }, diff --git a/stt/email.js b/stt/email.js index 9131631..db4a588 100644 --- a/stt/email.js +++ b/stt/email.js @@ -18,21 +18,21 @@ define(["./internal/stext", "./handlers/common", "../misc"], function (stext, ha dir: getDir(text, locale), points: "<>.:,;@", cases: [{ - handler: handler, - args: { - bounds: [{ - start: "\"", - end: "\"" - }, - { - start: "(", - end: ")" - } - ], - points: "" - } + handler: handler, + args: { + bounds: [{ + startAfter: "\"", + endBefore: "\"" + }, + { + startAfter: "(", + endBefore: ")" } - ] + ], + points: "" + } + } + ] }, !!isHtml, locale); } diff --git a/stt/sql.js b/stt/sql.js index 6f3b1c4..2823600 100644 --- a/stt/sql.js +++ b/stt/sql.js @@ -9,46 +9,46 @@ define(["./internal/stext", "./handlers/common"], function (stext, handler) { dir: "ltr", points: "\t!#%&()*+,-./:;<=>?|[]{}", cases: [{ - handler: handler, - args: { - bounds: [{ - start: "/*", - end: "*/" - }, - { - start: "--", - end: "\n" - }, - { - start: "--" - } - ] - } + handler: handler, + args: { + bounds: [{ + startAfter: "/*", + endBefore: "*/" }, { - handler: handler, - args: { - subs: { - content: " ", - continued: true - } - } + startAfter: "--", + end: "\n" }, { - handler: handler, - args: { - bounds: [{ - start: "'", - end: "'" - }, - { - start: "\"", - end: "\"" - } - ] - } + startAfter: "--" } ] + } + }, + { + handler: handler, + args: { + subs: { + content: " ", + continued: true + } + } + }, + { + handler: handler, + args: { + bounds: [{ + startAfter: "'", + endBefore: "'" + }, + { + startAfter: "\"", + endBefore: "\"" + } + ] + } + } + ] }, !!isHtml, locale); } diff --git a/stt/xpath.js b/stt/xpath.js index df3d0fb..1a00fd3 100644 --- a/stt/xpath.js +++ b/stt/xpath.js @@ -9,21 +9,21 @@ define(["./internal/stext", "./handlers/common"], function (stext, handler) { dir: "ltr", points: " /[]<>=!:@.|()+-*", cases: [{ - handler: handler, - args: { - bounds: [{ - start: "\"", - end: "\"" - }, - { - start: "'", - end: "'" - } - ], - points: "" - } + handler: handler, + args: { + bounds: [{ + startAfter: "\"", + endBefore: "\"" + }, + { + startAfter: "'", + endBefore: "'" } - ] + ], + points: "" + } + } + ] }, !!isHtml, locale); }