From 6ad71bd222ba87e3c380800ad68e5b1f981b31f1 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 25 Aug 2016 17:09:56 +0100 Subject: [PATCH] Move bidi code under RED.text --- Gruntfile.js | 4 +- editor/js/format.js | 1329 ---------------------------------- editor/js/history.js | 2 +- editor/js/main.js | 13 +- editor/js/{ => text}/bidi.js | 58 +- editor/js/text/format.js | 1329 ++++++++++++++++++++++++++++++++++ editor/js/ui/editor.js | 20 +- editor/js/ui/palette.js | 6 +- editor/js/ui/tab-info.js | 10 +- editor/js/ui/tabs.js | 4 +- editor/js/ui/view.js | 6 +- editor/js/ui/workspaces.js | 8 +- 12 files changed, 1395 insertions(+), 1394 deletions(-) delete mode 100644 editor/js/format.js rename editor/js/{ => text}/bidi.js (84%) create mode 100644 editor/js/text/format.js diff --git a/Gruntfile.js b/Gruntfile.js index 0825e1b39..847842420 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -104,8 +104,8 @@ module.exports = function(grunt) { "editor/js/settings.js", "editor/js/user.js", "editor/js/comms.js", - "editor/js/bidi.js", - "editor/js/format.js", + "editor/js/text/bidi.js", + "editor/js/text/format.js", "editor/js/ui/state.js", "editor/js/nodes.js", "editor/js/history.js", diff --git a/editor/js/format.js b/editor/js/format.js deleted file mode 100644 index 8e3dd52e6..000000000 --- a/editor/js/format.js +++ /dev/null @@ -1,1329 +0,0 @@ -/** - * Copyright 2016 IBM Corp. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -RED.format = (function() { - - var TextSegment = (function() { - var TextSegment = function (obj) { - this.content = ""; - this.actual = ""; - this.textDirection = ""; - this.localGui = ""; - this.isVisible = true; - this.isSeparator = false; - this.isParsed = false; - this.keep = false; - this.inBounds = false; - this.inPoints = false; - var prop = ""; - for (prop in obj) { - if (obj.hasOwnProperty(prop)) { - this[prop] = obj[prop]; - } - } - }; - return TextSegment; - })(); - - var tools = (function() { - 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) { - if (src.hasOwnProperty(prop)) { - 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 { - 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), - localGui: args.dir, - keep: true - })); - j++; - } - segments.splice(j, 0, new TextSegment({ - content: content.substring(start, end), - textDirection: subs.subDir, - localGui: args.dir - })); - if (end < content.length) { - segments.splice(j + 1, 0, new TextSegment({ - content: content.substring(end, content.length), - localGui: args.dir, - keep: true - })); - } - if (!sLoops) { - break; - } - } - }, - - handleBounds: function (segments, args, aBounds, origContent, locale) { - 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), - localGui: args.dir, - keep: true - })); - j++; - } - if (bounds.start) { - segments.splice(j, 0, new TextSegment({ - content: bounds.start, - localGui: args.dir, - isSeparator: true - })); - j++; - } - segments.splice(j, 0, new TextSegment({ - content: content.substring(start + bounds.start.length, end - bounds.end.length), - textDirection: bounds.subDir, - localGui: args.dir, - inBounds: true - })); - if (bounds.end) { - j++; - segments.splice(j, 0, new TextSegment({ - content: bounds.end, - localGui: args.dir, - 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), - localGui: args.dir, - 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) { - if (cases.length === 0) { - return segments; - } - var hArgs = {}; - for (var prop in args) { - if (args.hasOwnProperty(prop)) { - 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; - }, - - 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, - localGui: args.dir, - inPoints: true - })); - j++; - } - segments.splice(j, 0, new TextSegment({ - content: points[i], - localGui: args.dir, - 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, - localGui: args.dir, - inPoints: true - })); - } - } - } - } - for (i = 0; i < segments.length; i++) { - if (segments[i].keep) { - segments[i].keep = false; - } else if(segments[i].inPoints){ - segments[i].isParsed = true; - segments[i].inPoints = false; - } - } - return segments; - } - }; - })(); - - var common = (function() { - 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; - } - }; - })(); - - var misc = (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; - }, - - removeTags: function (text) { - if (text) { - return text.replace(/<[^<]*>/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) { - 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) { - var txt = text.replace(//g, this.LRM).replace(//g, this.RLM).replace(//g, this.LRE); - return txt.replace(//g, this.RLE).replace(//g, this.LRO).replace(//g, this.RLO).replace(//g, this.PDF); - }, - - showTags: function (text) { - return "" + text + ""; - }, - - hideTags: function (text) { - return text.replace(//g,"").replace(/<\/xmp>/g,""); - } - }; - })(); - - var stext = (function() { - 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 parseAndDisplayStructure(content, fArgs, isHtml, locale) { - if (!content || !fArgs) { - return content; - } - return displayStructure(parseStructure(content, fArgs, locale), fArgs, isHtml); - } - - function checkArguments(fArgs, fullCheck) { - var args = Array.isArray(fArgs)? fArgs[0] : fArgs; - if (!args.guiDir) { - args.guiDir = "ltr"; - } - if (!args.dir) { - args.dir = args.guiDir; - } - if (!fullCheck) { - return args; - } - if (typeof(args.points) === "undefined") { - args.points = []; - } - if (!args.cases) { - args.cases = []; - } - if (!args.bounds) { - args.bounds = []; - } - args.commonHandler = common; - return args; - } - - function parseStructure(content, fArgs, locale) { - if (!content || !fArgs) { - return new TextSegment({content: ""}); - } - var args = checkArguments(fArgs, true); - var segments = [new TextSegment( - { - content: content, - actual: content, - localGui: args.dir - })]; - var parse = common.handle; - if (args.handler && typeof(args.handler) === "function") { - parse = args.handler.handle; - } - parse(content, segments, args, locale); - return segments; - } - - function displayStructure(segments, fArgs, isHtml) { - var args = checkArguments(fArgs, false); - if (isHtml) { - return getResultWithHtml(segments, args); - } - else { - return getResultWithUcc(segments, args); - } - } - - function getResultWithUcc(segments, args, isHtml) { - var result = ""; - var checkedDir = ""; - var prevDir = ""; - var stop = false; - for (var i = 0; i < segments.length; i++) { - if (segments[i].isVisible) { - var dir = segments[i].textDirection; - var lDir = segments[i].localGui; - if (lDir !== "" && prevDir === "") { - result += (lDir === "rtl" ? misc.RLE : misc.LRE); - } - else if(prevDir !== "" && (lDir === "" || lDir !== prevDir || stop)) { - result += misc.PDF + (i == segments.length - 1 && lDir !== ""? "" : args.dir === "rtl" ? misc.RLM : misc.LRM); - if (lDir !== "") { - result += (lDir === "rtl" ? misc.RLE : misc.LRE); - } - } - if (dir === "auto") { - dir = misc.getDirection(segments[i].content, dir, args.guiDir); - } - if ((/^(rtl|ltr)$/i).test(dir)) { - result += (dir === "rtl" ? misc.RLE : misc.LRE) + segments[i].content + misc.PDF; - checkedDir = dir; - } - else { - result += segments[i].content; - checkedDir = misc.getDirection(segments[i].content, dir, args.guiDir, true); - } - if (i < segments.length - 1) { - var locDir = lDir && segments[i+1].localGui? lDir : args.dir; - result += locDir === "rtl" ? misc.RLM : misc.LRM; - } - else if(prevDir !== "") { - result += misc.PDF; - } - prevDir = lDir; - stop = false; - } - else { - stop = true; - } - } - var sttDir = args.dir === "auto" ? misc.getDirection(segments[0].actual, args.dir, args.guiDir) : args.dir; - if (sttDir !== args.guiDir) { - result = (sttDir === "rtl" ? misc.RLE : misc.LRE) + result + misc.PDF; - } - return result; - } - - function getResultWithHtml(segments, args, isHtml) { - var result = ""; - var checkedDir = ""; - var prevDir = ""; - for (var i = 0; i < segments.length; i++) { - if (segments[i].isVisible) { - var dir = segments[i].textDirection; - var lDir = segments[i].localGui; - if (lDir !== "" && prevDir === "") { - result += "<bdi dir='" + (lDir === "rtl" ? "rtl" : "ltr") + "'>"; - } - else if(prevDir !== "" && (lDir === "" || lDir !== prevDir || stop)) { - result += "</bdi>" + (i == segments.length - 1 && lDir !== ""? "" : "<span style='unicode-bidi: embed; direction: " + (args.dir === "rtl" ? "rtl" : "ltr") + ";'></span>"); - if (lDir !== "") { - result += "<bdi dir='" + (lDir === "rtl" ? "rtl" : "ltr") + "'>"; - } - } - - if (dir === "auto") { - dir = misc.getDirection(segments[i].content, dir, args.guiDir); - } - if ((/^(rtl|ltr)$/i).test(dir)) { - //result += "<span style='unicode-bidi: embed; direction: " + (dir === "rtl" ? "rtl" : "ltr") + ";'>" + segments[i].content + "</span>"; - result += "<bdi dir='" + (dir === "rtl" ? "rtl" : "ltr") + "'>" + segments[i].content + "</bdi>"; - checkedDir = dir; - } - else { - result += segments[i].content; - checkedDir = misc.getDirection(segments[i].content, dir, args.guiDir, true); - } - if (i < segments.length - 1) { - var locDir = lDir && segments[i+1].localGui? lDir : args.dir; - result += "<span style='unicode-bidi: embed; direction: " + (locDir === "rtl" ? "rtl" : "ltr") + ";'></span>"; - } - else if(prevDir !== "") { - result += "</bdi>"; - } - prevDir = lDir; - stop = false; - } - else { - stop = true; - } - } - var sttDir = args.dir === "auto" ? misc.getDirection(segments[0].actual, args.dir, args.guiDir) : args.dir; - if (sttDir !== args.guiDir) { - result = "<bdi dir='" + (sttDir === "rtl" ? "rtl" : "ltr") + "'>" + result + "</bdi>"; - } - return result; - } - - //TBD ? - function restore(text, isHtml) { - return text; - } - - stt.parseAndDisplayStructure = parseAndDisplayStructure; - stt.parseStructure = parseStructure; - stt.displayStructure = displayStructure; - stt.restore = restore; - - return stt; - })(); - - var breadcrumb = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - 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" - } - } - }] - }; - - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var comma = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: "ltr", - points: "," - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var email = (function() { - 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, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: getDir(text, locale), - points: "<>.:,;@", - cases: [{ - handler: common, - args: { - bounds: [{ - startAfter: "\"", - endBefore: "\"" - }, - { - startAfter: "(", - endBefore: ")" - } - ], - points: "" - } - }] - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var filepath = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: "ltr", - points: "/\\:." - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var formula = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: "ltr", - points: " /%^&[]<>=!?~:.,|()+-*{}", - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - - var sql = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: "ltr", - points: "\t!#%&()*+,-./:;<=>?|[]{}", - cases: [{ - handler: common, - args: { - bounds: [{ - startAfter: "/*", - endBefore: "*/" - }, - { - startAfter: "--", - end: "\n" - }, - { - startAfter: "--" - } - ] - } - }, - { - handler: common, - args: { - subs: { - content: " ", - continued: true - } - } - }, - { - handler: common, - args: { - bounds: [{ - startAfter: "'", - endBefore: "'" - }, - { - startAfter: "\"", - endBefore: "\"" - } - ] - } - } - ] - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var underscore = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: "ltr", - points: "_" - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var url = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: "ltr", - points: ":?#/@.[]=" - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var word = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: args.dir ? args.dir : isRtl ? "rtl" : "ltr", - points: " ,.!?;:", - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var xpath = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var fArgs = - { - guiDir: isRtl ? "rtl" : "ltr", - dir: "ltr", - points: " /[]<>=!:@.|()+-*", - cases: [{ - handler: common, - args: { - bounds: [{ - startAfter: "\"", - endBefore: "\"" - }, - { - startAfter: "'", - endBefore: "'" - } - ], - points: "" - } - } - ] - }; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, fArgs, !!isHtml, locale); - } - } - }; - })(); - - var custom = (function() { - return { - format: function (text, args, isRtl, isHtml, locale, parseOnly) { - var hArgs = {}; - var prop = ""; - var sArgs = Array.isArray(args)? args[0] : args; - for (prop in sArgs) { - if (sArgs.hasOwnProperty(prop)) { - hArgs[prop] = sArgs[prop]; - } - } - hArgs.guiDir = isRtl ? "rtl" : "ltr"; - hArgs.dir = hArgs.dir ? hArgs.dir : hArgs.guiDir; - if (!parseOnly) { - return stext.parseAndDisplayStructure(text, hArgs, !!isHtml, locale); - } - else { - return stext.parseStructure(text, hArgs, !!isHtml, locale); - } - } - }; - })(); - - var message = (function() { - var params = {msgLang: "en", msgDir: "", phLang: "", phDir: "", phPacking: ["{","}"], phStt: {type: "none", args: {}}, guiDir: ""}; - var parametersChecked = false; - - function getDirectionOfLanguage(lang) { - if (lang === "he" || lang === "iw" || lang === "ar") { - return "rtl"; - } - return "ltr"; - } - - function checkParameters(obj) { - if (obj.msgDir.length === 0) { - obj.msgDir = getDirectionOfLanguage(obj.msgLang); - } - obj.msgDir = obj.msgDir !== "ltr" && obj.msgDir !== "rtl" && obj.msgDir != "auto"? "ltr" : obj.msgDir; - if (obj.guiDir.length === 0) { - obj.guiDir = obj.msgDir; - } - obj.guiDir = obj.guiDir !== "rtl"? "ltr" : "rtl"; - if (obj.phDir.length === 0) { - obj.phDir = obj.phLang.length === 0? obj.msgDir : getDirectionOfLanguage(obj.phLang); - } - obj.phDir = obj.phDir !== "ltr" && obj.phDir !== "rtl" && obj.phDir != "auto"? "ltr" : obj.phDir; - if (typeof (obj.phPacking) === "string") { - obj.phPacking = obj.phPacking.split(""); - } - if (obj.phPacking.length < 2) { - obj.phPacking = ["{","}"]; - } - } - - return { - setDefaults: function (args) { - for (var prop in args) { - if (params.hasOwnProperty(prop)) { - params[prop] = args[prop]; - } - } - checkParameters(params); - parametersChecked = true; - }, - - format: function (text) { - if (!parametersChecked) { - checkParameters(params); - parametersChecked = true; - } - var isHtml = false; - var hasHtmlArg = false; - var spLength = params.phPacking[0].length; - var epLength = params.phPacking[1].length; - if (arguments.length > 0) { - var last = arguments[arguments.length-1]; - if (typeof (last) === "boolean") { - isHtml = last; - hasHtmlArg = true; - } - } - //Message - var re = new RegExp(params.phPacking[0] + "\\d+" + params.phPacking[1]); - var m; - var tSegments = []; - var offset = 0; - var txt = text; - while ((m = re.exec(txt)) != null) { - var lastIndex = txt.indexOf(m[0]) + m[0].length; - if (lastIndex > m[0].length) { - tSegments.push({text: txt.substring(0, lastIndex - m[0].length), ph: false}); - } - tSegments.push({text: m[0], ph: true}); - offset += lastIndex; - txt = txt.substring(lastIndex, txt.length); - } - if (offset < text.length) { - tSegments.push({text: text.substring(offset, text.length), ph: false}); - } - //Parameters - var tArgs = []; - for (var i = 1; i < arguments.length - (hasHtmlArg? 1 : 0); i++) { - var arg = arguments[i]; - var checkArr = arg; - var inLoop = false; - var indArr = 0; - if (Array.isArray(checkArr)) { - arg = checkArr[0]; - if (typeof(arg) === "undefined") { - continue; - } - inLoop = true; - } - do { - if (typeof (arg) === "string") { - tArgs.push({text: arg, dir: params.phDir, stt: params.stt}); - } - else if(typeof (arg) === "boolean") { - isHtml = arg; - } - else if(typeof (arg) === "object") { - tArgs.push(arg); - if (!arg.hasOwnProperty("text")) { - tArgs[tArgs.length-1].text = "{???}"; - } - if (!arg.hasOwnProperty("dir") || arg.dir.length === 0) { - tArgs[tArgs.length-1].dir = params.phDir; - } - if (!arg.hasOwnProperty("stt") || (typeof (arg.stt) === "string" && arg.stt.length === 0) || - (typeof (arg.stt) === "object" && Object.keys(arg.stt).length === 0)) { - tArgs[tArgs.length-1].stt = params.phStt; - } - } - else { - tArgs.push({text: "" + arg, dir: params.phDir, stt: params.phStt}); - } - if (inLoop) { - indArr++; - if (indArr == checkArr.length) { - inLoop = false; - } - else { - arg = checkArr[indArr]; - } - } - } while(inLoop); - } - //Indexing - var segments = []; - for (i = 0; i < tSegments.length; i++) { - var t = tSegments[i]; - if (!t.ph) { - segments.push(new TextSegment({content: t.text, textDirection: params.msgDir})); - } - else { - var ind = parseInt(t.text.substring(spLength, t.text.length - epLength)); - if (isNaN(ind) || ind >= tArgs.length) { - segments.push(new TextSegment({content: t.text, textDirection: params.msgDir})); - continue; - } - var sttType = "none"; - if (!tArgs[ind].stt) { - tArgs[ind].stt = params.phStt; - } - if (tArgs[ind].stt) { - if (typeof (tArgs[ind].stt) === "string") { - sttType = tArgs[ind].stt; - } - else if(tArgs[ind].stt.hasOwnProperty("type")) { - sttType = tArgs[ind].stt.type; - } - } - if (sttType.toLowerCase() !== "none") { - var sttSegs = getHandler(sttType).format(tArgs[ind].text, tArgs[ind].stt.args || {}, - params.msgDir === "rtl", false, params.msgLang, true); - for (var j = 0; j < sttSegs.length; j++) { - segments.push(sttSegs[j]); - } - segments.push(new TextSegment({isVisible: false})); - } - else { - segments.push(new TextSegment({content: tArgs[ind].text, textDirection: (tArgs[ind].dir? tArgs[ind].dir : params.phDir)})); - } - } - } - var result = stext.displayStructure(segments, {guiDir: params.guiDir, dir: params.msgDir}, isHtml); - return result; - } - }; - })(); - - var event = null; - - function getHandler(type) { - 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; - } - } - - function isInputEventSupported(element) { - var agent = window.navigator.userAgent; - if (agent.indexOf("MSIE") >=0 || agent.indexOf("Trident") >=0 || agent.indexOf("Edge") >=0) { - return false; - } - var checked = document.createElement(element.tagName); - checked.contentEditable = true; - var isSupported = ("oninput" in checked); - if (!isSupported) { - checked.setAttribute('oninput', 'return;'); - isSupported = typeof checked['oninput'] == 'function'; - } - checked = null; - return isSupported; - } - - function attachElement(element, type, args, isRtl, locale) { - //if (!element || element.nodeType != 1 || !element.isContentEditable) - if (!element || element.nodeType != 1) { - return false; - } - if (!event) { - event = document.createEvent('Event'); - event.initEvent('TF', true, true); - } - element.setAttribute("data-tf-type", type); - var sArgs = args === "undefined"? "{}" : JSON.stringify(Array.isArray(args)? args[0] : args); - element.setAttribute("data-tf-args", sArgs); - var dir = "ltr"; - if (isRtl === "undefined") { - if (element.dir) { - dir = element.dir; - } - else if(element.style && element.style.direction) { - dir = element.style.direction; - } - isRtl = dir.toLowerCase() === "rtl"; - } - element.setAttribute("data-tf-dir", isRtl); - element.setAttribute("data-tf-locale", misc.getLocaleDetails(locale).lang); - if (isInputEventSupported(element)) { - var ehandler = element.oninput; - element.oninput = function(event) { - displayWithStructure(event.target); - }; - } - else { - element.onkeyup = function(e) { - displayWithStructure(e.target); - element.dispatchEvent(event); - }; - element.onmouseup = function(e) { - displayWithStructure(e.target); - element.dispatchEvent(event); - }; - } - displayWithStructure(element); - - return true; - } - - function detachElement(element) { - if (!element || element.nodeType != 1) { - return; - } - element.removeAttribute("data-tf-type"); - element.removeAttribute("data-tf-args"); - element.removeAttribute("data-tf-dir"); - element.removeAttribute("data-tf-locale"); - element.innerHTML = element.textContent || ""; - } - - function displayWithStructure(element) { - var txt = element.textContent || ""; - if (txt.length === 0) { - element.dispatchEvent(event); - return; - } - var selection = document.getSelection(); - var range = selection.getRangeAt(0); - var tempRange = range.cloneRange(), startNode, startOffset; - startNode = range.startContainer; - startOffset = range.startOffset; - var textOffset = 0; - if (startNode.nodeType === 3) { - textOffset += startOffset; - } - tempRange.setStart(element,0); - tempRange.setEndBefore(startNode); - var div = document.createElement('div'); - div.appendChild(tempRange.cloneContents()); - textOffset += div.textContent.length; - - element.innerHTML = getHandler(element.getAttribute("data-tf-type")). - format(txt, JSON.parse(element.getAttribute("data-tf-args")), (element.getAttribute("data-tf-dir") === "true"? true : false), - true, element.getAttribute("data-tf-locale")); - var parent = element; - var node = element; - var newOffset = 0; - var inEnd = false; - selection.removeAllRanges(); - range.setStart(element,0); - range.setEnd(element,0); - while (node) { - if (node.nodeType === 3) { - if (newOffset + node.nodeValue.length >= textOffset) { - range.setStart(node, textOffset - newOffset); - break; - } - else { - newOffset += node.nodeValue.length; - node = node.nextSibling; - } - } - else if(node.hasChildNodes()) { - parent = node; - node = parent.firstChild; - continue; - } - else { - node = node.nextSibling; - } - while (!node) { - if (parent === element) { - inEnd = true; - break; - } - node = parent.nextSibling; - parent = parent.parentNode; - } - if (inEnd) { - break; - } - } - - selection.addRange(range); - element.dispatchEvent(event); - } - - return { - /** - * Returns the HTML representation of a given structured text - * @param text - the structured text - * @param type - could be one of filepath, url, email - * @param args - pass additional arguments to the handler. generally null. - * @param isRtl - indicates if the GUI is mirrored - * @param locale - the browser locale - */ - getHtml: function (text, type, args, isRtl, locale) { - return getHandler(type).format(text, args, isRtl, true, locale); - }, - /** - * Handle Structured text correct display for a given HTML element. - * @param element - the element : should be of type div contenteditable=true - * @param type - could be one of filepath, url, email - * @param args - pass additional arguments to the handler. generally null. - * @param isRtl - indicates if the GUI is mirrored - * @param locale - the browser locale - */ - attach: function (element, type, args, isRtl, locale) { - return attachElement(element, type, args, isRtl, locale); - } - }; -})(); diff --git a/editor/js/history.js b/editor/js/history.js index 74520beac..43aabc0f1 100644 --- a/editor/js/history.js +++ b/editor/js/history.js @@ -236,7 +236,7 @@ RED.history = (function() { }); if (ev.node.type === 'subflow') { - $("#menu-item-workspace-menu-"+ev.node.id.replace(".","-")).text(RED.bidi.enforceTextDirectionWithUCC(ev.node.name)); + $("#menu-item-workspace-menu-"+ev.node.id.replace(".","-")).text(RED.text.bidi.enforceTextDirectionWithUCC(ev.node.name)); } } else { RED.editor.updateNodeProperties(ev.node); diff --git a/editor/js/main.js b/editor/js/main.js index b51fa9047..0a9a0b5ae 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -176,13 +176,14 @@ var RED = (function() { {id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid}, {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true}, null, - {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}, {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[ - {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.view.toggleTextDir("")}}}, - {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if(s){RED.view.toggleTextDir("ltr")}}}, - {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if(s){RED.view.toggleTextDir("rtl")}}}, - {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.view.toggleTextDir("auto")}}} - ]} + {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.view.toggleTextDir("")}}}, + {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if(s){RED.view.toggleTextDir("ltr")}}}, + {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if(s){RED.view.toggleTextDir("rtl")}}}, + {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.view.toggleTextDir("auto")}}} + ]}, + null, + {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true} ]}, null, {id:"menu-item-import",label:RED._("menu.label.import"),options:[ diff --git a/editor/js/bidi.js b/editor/js/text/bidi.js similarity index 84% rename from editor/js/bidi.js rename to editor/js/text/bidi.js index 11e2ec84a..91393d955 100644 --- a/editor/js/bidi.js +++ b/editor/js/text/bidi.js @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - -RED.bidi = (function() { +RED.text = {}; +RED.text.bidi = (function() { var textDir = ""; var LRE = "\u202A", RLE = "\u202B", PDF = "\u202C"; - + function isRTLValue(stringValue) { for (var ch in stringValue) { if (isBidiChar(stringValue.charCodeAt(ch))) { @@ -27,7 +27,7 @@ RED.bidi = (function() { } else if(isLatinChar(stringValue.charCodeAt(ch))) { return false; - } + } } return false; } @@ -60,14 +60,14 @@ RED.bidi = (function() { if((c > 64 && c < 91)||(c > 96 && c < 123)) { return true; } - else { + else { return false; - } + } } - - /** + + /** * Determines the text direction of a given string. - * @param value - the string + * @param value - the string */ function resolveBaseTextDir(value) { if (textDir == "auto") { @@ -76,27 +76,27 @@ RED.bidi = (function() { } else { return "ltr"; } - } + } else { return textDir; } } - + function onInputChange() { - $(this).attr("dir", resolveBaseTextDir($(this).val())); + $(this).attr("dir", resolveBaseTextDir($(this).val())); } - /** + /** * Listens to keyup, paste and cut events of a given input field. Upon one of these events the text direction is computed again - * @param input - the input field + * @param input - the input field */ - function initInputEvents(input) { + function initInputEvents(input) { input.on("keyup",onInputChange).on("paste",onInputChange).on("cut",onInputChange); } - - /** + + /** * Enforces the text direction of a given string by adding UCC (Unicode Control Characters) - * @param value - the string + * @param value - the string */ function enforceTextDirectionWithUCC(value) { if (value) { @@ -110,27 +110,27 @@ RED.bidi = (function() { } return value; } - - /** + + /** * Enforces the text direction for all the spans with style bidiAware under workpsace or sidebar div */ - function enforceTextDirectionOnPage() { - $("#workspace").find('span.bidiAware').each(function() { + function enforceTextDirectionOnPage() { + $("#workspace").find('span.bidiAware').each(function() { $(this).attr("dir", resolveBaseTextDir($(this).html())); - }); - $("#sidebar").find('span.bidiAware').each(function() { + }); + $("#sidebar").find('span.bidiAware').each(function() { $(this).attr("dir", resolveBaseTextDir($(this).text())); - }); + }); } - - /** + + /** * Sets the text direction preference - * @param dir - the text direction preference + * @param dir - the text direction preference */ function setTextDirection(dir) { textDir = dir; } - + return { setTextDirection: setTextDirection, enforceTextDirectionOnPage: enforceTextDirectionOnPage, diff --git a/editor/js/text/format.js b/editor/js/text/format.js new file mode 100644 index 000000000..f32ed6ec0 --- /dev/null +++ b/editor/js/text/format.js @@ -0,0 +1,1329 @@ +/** + * Copyright 2016 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ +RED.text.format = (function() { + + var TextSegment = (function() { + var TextSegment = function (obj) { + this.content = ""; + this.actual = ""; + this.textDirection = ""; + this.localGui = ""; + this.isVisible = true; + this.isSeparator = false; + this.isParsed = false; + this.keep = false; + this.inBounds = false; + this.inPoints = false; + var prop = ""; + for (prop in obj) { + if (obj.hasOwnProperty(prop)) { + this[prop] = obj[prop]; + } + } + }; + return TextSegment; + })(); + + var tools = (function() { + 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) { + if (src.hasOwnProperty(prop)) { + 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 { + 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), + localGui: args.dir, + keep: true + })); + j++; + } + segments.splice(j, 0, new TextSegment({ + content: content.substring(start, end), + textDirection: subs.subDir, + localGui: args.dir + })); + if (end < content.length) { + segments.splice(j + 1, 0, new TextSegment({ + content: content.substring(end, content.length), + localGui: args.dir, + keep: true + })); + } + if (!sLoops) { + break; + } + } + }, + + handleBounds: function (segments, args, aBounds, origContent, locale) { + 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), + localGui: args.dir, + keep: true + })); + j++; + } + if (bounds.start) { + segments.splice(j, 0, new TextSegment({ + content: bounds.start, + localGui: args.dir, + isSeparator: true + })); + j++; + } + segments.splice(j, 0, new TextSegment({ + content: content.substring(start + bounds.start.length, end - bounds.end.length), + textDirection: bounds.subDir, + localGui: args.dir, + inBounds: true + })); + if (bounds.end) { + j++; + segments.splice(j, 0, new TextSegment({ + content: bounds.end, + localGui: args.dir, + 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), + localGui: args.dir, + 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) { + if (cases.length === 0) { + return segments; + } + var hArgs = {}; + for (var prop in args) { + if (args.hasOwnProperty(prop)) { + 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; + }, + + 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, + localGui: args.dir, + inPoints: true + })); + j++; + } + segments.splice(j, 0, new TextSegment({ + content: points[i], + localGui: args.dir, + 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, + localGui: args.dir, + inPoints: true + })); + } + } + } + } + for (i = 0; i < segments.length; i++) { + if (segments[i].keep) { + segments[i].keep = false; + } else if(segments[i].inPoints){ + segments[i].isParsed = true; + segments[i].inPoints = false; + } + } + return segments; + } + }; + })(); + + var common = (function() { + 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; + } + }; + })(); + + var misc = (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; + }, + + removeTags: function (text) { + if (text) { + return text.replace(/<[^<]*>/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) { + 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, this.LRM).replace(/<RLM>/g, this.RLM).replace(/<LRE>/g, this.LRE); + return txt.replace(/<RLE>/g, this.RLE).replace(/<LRO>/g, this.LRO).replace(/<RLO>/g, this.RLO).replace(/<PDF>/g, this.PDF); + }, + + showTags: function (text) { + return "<xmp>" + text + ""; + }, + + hideTags: function (text) { + return text.replace(//g,"").replace(/<\/xmp>/g,""); + } + }; + })(); + + var stext = (function() { + 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 parseAndDisplayStructure(content, fArgs, isHtml, locale) { + if (!content || !fArgs) { + return content; + } + return displayStructure(parseStructure(content, fArgs, locale), fArgs, isHtml); + } + + function checkArguments(fArgs, fullCheck) { + var args = Array.isArray(fArgs)? fArgs[0] : fArgs; + if (!args.guiDir) { + args.guiDir = "ltr"; + } + if (!args.dir) { + args.dir = args.guiDir; + } + if (!fullCheck) { + return args; + } + if (typeof(args.points) === "undefined") { + args.points = []; + } + if (!args.cases) { + args.cases = []; + } + if (!args.bounds) { + args.bounds = []; + } + args.commonHandler = common; + return args; + } + + function parseStructure(content, fArgs, locale) { + if (!content || !fArgs) { + return new TextSegment({content: ""}); + } + var args = checkArguments(fArgs, true); + var segments = [new TextSegment( + { + content: content, + actual: content, + localGui: args.dir + })]; + var parse = common.handle; + if (args.handler && typeof(args.handler) === "function") { + parse = args.handler.handle; + } + parse(content, segments, args, locale); + return segments; + } + + function displayStructure(segments, fArgs, isHtml) { + var args = checkArguments(fArgs, false); + if (isHtml) { + return getResultWithHtml(segments, args); + } + else { + return getResultWithUcc(segments, args); + } + } + + function getResultWithUcc(segments, args, isHtml) { + var result = ""; + var checkedDir = ""; + var prevDir = ""; + var stop = false; + for (var i = 0; i < segments.length; i++) { + if (segments[i].isVisible) { + var dir = segments[i].textDirection; + var lDir = segments[i].localGui; + if (lDir !== "" && prevDir === "") { + result += (lDir === "rtl" ? misc.RLE : misc.LRE); + } + else if(prevDir !== "" && (lDir === "" || lDir !== prevDir || stop)) { + result += misc.PDF + (i == segments.length - 1 && lDir !== ""? "" : args.dir === "rtl" ? misc.RLM : misc.LRM); + if (lDir !== "") { + result += (lDir === "rtl" ? misc.RLE : misc.LRE); + } + } + if (dir === "auto") { + dir = misc.getDirection(segments[i].content, dir, args.guiDir); + } + if ((/^(rtl|ltr)$/i).test(dir)) { + result += (dir === "rtl" ? misc.RLE : misc.LRE) + segments[i].content + misc.PDF; + checkedDir = dir; + } + else { + result += segments[i].content; + checkedDir = misc.getDirection(segments[i].content, dir, args.guiDir, true); + } + if (i < segments.length - 1) { + var locDir = lDir && segments[i+1].localGui? lDir : args.dir; + result += locDir === "rtl" ? misc.RLM : misc.LRM; + } + else if(prevDir !== "") { + result += misc.PDF; + } + prevDir = lDir; + stop = false; + } + else { + stop = true; + } + } + var sttDir = args.dir === "auto" ? misc.getDirection(segments[0].actual, args.dir, args.guiDir) : args.dir; + if (sttDir !== args.guiDir) { + result = (sttDir === "rtl" ? misc.RLE : misc.LRE) + result + misc.PDF; + } + return result; + } + + function getResultWithHtml(segments, args, isHtml) { + var result = ""; + var checkedDir = ""; + var prevDir = ""; + for (var i = 0; i < segments.length; i++) { + if (segments[i].isVisible) { + var dir = segments[i].textDirection; + var lDir = segments[i].localGui; + if (lDir !== "" && prevDir === "") { + result += "<bdi dir='" + (lDir === "rtl" ? "rtl" : "ltr") + "'>"; + } + else if(prevDir !== "" && (lDir === "" || lDir !== prevDir || stop)) { + result += "</bdi>" + (i == segments.length - 1 && lDir !== ""? "" : "<span style='unicode-bidi: embed; direction: " + (args.dir === "rtl" ? "rtl" : "ltr") + ";'></span>"); + if (lDir !== "") { + result += "<bdi dir='" + (lDir === "rtl" ? "rtl" : "ltr") + "'>"; + } + } + + if (dir === "auto") { + dir = misc.getDirection(segments[i].content, dir, args.guiDir); + } + if ((/^(rtl|ltr)$/i).test(dir)) { + //result += "<span style='unicode-bidi: embed; direction: " + (dir === "rtl" ? "rtl" : "ltr") + ";'>" + segments[i].content + "</span>"; + result += "<bdi dir='" + (dir === "rtl" ? "rtl" : "ltr") + "'>" + segments[i].content + "</bdi>"; + checkedDir = dir; + } + else { + result += segments[i].content; + checkedDir = misc.getDirection(segments[i].content, dir, args.guiDir, true); + } + if (i < segments.length - 1) { + var locDir = lDir && segments[i+1].localGui? lDir : args.dir; + result += "<span style='unicode-bidi: embed; direction: " + (locDir === "rtl" ? "rtl" : "ltr") + ";'></span>"; + } + else if(prevDir !== "") { + result += "</bdi>"; + } + prevDir = lDir; + stop = false; + } + else { + stop = true; + } + } + var sttDir = args.dir === "auto" ? misc.getDirection(segments[0].actual, args.dir, args.guiDir) : args.dir; + if (sttDir !== args.guiDir) { + result = "<bdi dir='" + (sttDir === "rtl" ? "rtl" : "ltr") + "'>" + result + "</bdi>"; + } + return result; + } + + //TBD ? + function restore(text, isHtml) { + return text; + } + + stt.parseAndDisplayStructure = parseAndDisplayStructure; + stt.parseStructure = parseStructure; + stt.displayStructure = displayStructure; + stt.restore = restore; + + return stt; + })(); + + var breadcrumb = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + 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" + } + } + }] + }; + + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var comma = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: "ltr", + points: "," + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var email = (function() { + 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, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: getDir(text, locale), + points: "<>.:,;@", + cases: [{ + handler: common, + args: { + bounds: [{ + startAfter: "\"", + endBefore: "\"" + }, + { + startAfter: "(", + endBefore: ")" + } + ], + points: "" + } + }] + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var filepath = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: "ltr", + points: "/\\:." + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var formula = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: "ltr", + points: " /%^&[]<>=!?~:.,|()+-*{}", + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + + var sql = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: "ltr", + points: "\t!#%&()*+,-./:;<=>?|[]{}", + cases: [{ + handler: common, + args: { + bounds: [{ + startAfter: "/*", + endBefore: "*/" + }, + { + startAfter: "--", + end: "\n" + }, + { + startAfter: "--" + } + ] + } + }, + { + handler: common, + args: { + subs: { + content: " ", + continued: true + } + } + }, + { + handler: common, + args: { + bounds: [{ + startAfter: "'", + endBefore: "'" + }, + { + startAfter: "\"", + endBefore: "\"" + } + ] + } + } + ] + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var underscore = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: "ltr", + points: "_" + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var url = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: "ltr", + points: ":?#/@.[]=" + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var word = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: args.dir ? args.dir : isRtl ? "rtl" : "ltr", + points: " ,.!?;:", + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var xpath = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var fArgs = + { + guiDir: isRtl ? "rtl" : "ltr", + dir: "ltr", + points: " /[]<>=!:@.|()+-*", + cases: [{ + handler: common, + args: { + bounds: [{ + startAfter: "\"", + endBefore: "\"" + }, + { + startAfter: "'", + endBefore: "'" + } + ], + points: "" + } + } + ] + }; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, fArgs, !!isHtml, locale); + } + } + }; + })(); + + var custom = (function() { + return { + format: function (text, args, isRtl, isHtml, locale, parseOnly) { + var hArgs = {}; + var prop = ""; + var sArgs = Array.isArray(args)? args[0] : args; + for (prop in sArgs) { + if (sArgs.hasOwnProperty(prop)) { + hArgs[prop] = sArgs[prop]; + } + } + hArgs.guiDir = isRtl ? "rtl" : "ltr"; + hArgs.dir = hArgs.dir ? hArgs.dir : hArgs.guiDir; + if (!parseOnly) { + return stext.parseAndDisplayStructure(text, hArgs, !!isHtml, locale); + } + else { + return stext.parseStructure(text, hArgs, !!isHtml, locale); + } + } + }; + })(); + + var message = (function() { + var params = {msgLang: "en", msgDir: "", phLang: "", phDir: "", phPacking: ["{","}"], phStt: {type: "none", args: {}}, guiDir: ""}; + var parametersChecked = false; + + function getDirectionOfLanguage(lang) { + if (lang === "he" || lang === "iw" || lang === "ar") { + return "rtl"; + } + return "ltr"; + } + + function checkParameters(obj) { + if (obj.msgDir.length === 0) { + obj.msgDir = getDirectionOfLanguage(obj.msgLang); + } + obj.msgDir = obj.msgDir !== "ltr" && obj.msgDir !== "rtl" && obj.msgDir != "auto"? "ltr" : obj.msgDir; + if (obj.guiDir.length === 0) { + obj.guiDir = obj.msgDir; + } + obj.guiDir = obj.guiDir !== "rtl"? "ltr" : "rtl"; + if (obj.phDir.length === 0) { + obj.phDir = obj.phLang.length === 0? obj.msgDir : getDirectionOfLanguage(obj.phLang); + } + obj.phDir = obj.phDir !== "ltr" && obj.phDir !== "rtl" && obj.phDir != "auto"? "ltr" : obj.phDir; + if (typeof (obj.phPacking) === "string") { + obj.phPacking = obj.phPacking.split(""); + } + if (obj.phPacking.length < 2) { + obj.phPacking = ["{","}"]; + } + } + + return { + setDefaults: function (args) { + for (var prop in args) { + if (params.hasOwnProperty(prop)) { + params[prop] = args[prop]; + } + } + checkParameters(params); + parametersChecked = true; + }, + + format: function (text) { + if (!parametersChecked) { + checkParameters(params); + parametersChecked = true; + } + var isHtml = false; + var hasHtmlArg = false; + var spLength = params.phPacking[0].length; + var epLength = params.phPacking[1].length; + if (arguments.length > 0) { + var last = arguments[arguments.length-1]; + if (typeof (last) === "boolean") { + isHtml = last; + hasHtmlArg = true; + } + } + //Message + var re = new RegExp(params.phPacking[0] + "\\d+" + params.phPacking[1]); + var m; + var tSegments = []; + var offset = 0; + var txt = text; + while ((m = re.exec(txt)) != null) { + var lastIndex = txt.indexOf(m[0]) + m[0].length; + if (lastIndex > m[0].length) { + tSegments.push({text: txt.substring(0, lastIndex - m[0].length), ph: false}); + } + tSegments.push({text: m[0], ph: true}); + offset += lastIndex; + txt = txt.substring(lastIndex, txt.length); + } + if (offset < text.length) { + tSegments.push({text: text.substring(offset, text.length), ph: false}); + } + //Parameters + var tArgs = []; + for (var i = 1; i < arguments.length - (hasHtmlArg? 1 : 0); i++) { + var arg = arguments[i]; + var checkArr = arg; + var inLoop = false; + var indArr = 0; + if (Array.isArray(checkArr)) { + arg = checkArr[0]; + if (typeof(arg) === "undefined") { + continue; + } + inLoop = true; + } + do { + if (typeof (arg) === "string") { + tArgs.push({text: arg, dir: params.phDir, stt: params.stt}); + } + else if(typeof (arg) === "boolean") { + isHtml = arg; + } + else if(typeof (arg) === "object") { + tArgs.push(arg); + if (!arg.hasOwnProperty("text")) { + tArgs[tArgs.length-1].text = "{???}"; + } + if (!arg.hasOwnProperty("dir") || arg.dir.length === 0) { + tArgs[tArgs.length-1].dir = params.phDir; + } + if (!arg.hasOwnProperty("stt") || (typeof (arg.stt) === "string" && arg.stt.length === 0) || + (typeof (arg.stt) === "object" && Object.keys(arg.stt).length === 0)) { + tArgs[tArgs.length-1].stt = params.phStt; + } + } + else { + tArgs.push({text: "" + arg, dir: params.phDir, stt: params.phStt}); + } + if (inLoop) { + indArr++; + if (indArr == checkArr.length) { + inLoop = false; + } + else { + arg = checkArr[indArr]; + } + } + } while(inLoop); + } + //Indexing + var segments = []; + for (i = 0; i < tSegments.length; i++) { + var t = tSegments[i]; + if (!t.ph) { + segments.push(new TextSegment({content: t.text, textDirection: params.msgDir})); + } + else { + var ind = parseInt(t.text.substring(spLength, t.text.length - epLength)); + if (isNaN(ind) || ind >= tArgs.length) { + segments.push(new TextSegment({content: t.text, textDirection: params.msgDir})); + continue; + } + var sttType = "none"; + if (!tArgs[ind].stt) { + tArgs[ind].stt = params.phStt; + } + if (tArgs[ind].stt) { + if (typeof (tArgs[ind].stt) === "string") { + sttType = tArgs[ind].stt; + } + else if(tArgs[ind].stt.hasOwnProperty("type")) { + sttType = tArgs[ind].stt.type; + } + } + if (sttType.toLowerCase() !== "none") { + var sttSegs = getHandler(sttType).format(tArgs[ind].text, tArgs[ind].stt.args || {}, + params.msgDir === "rtl", false, params.msgLang, true); + for (var j = 0; j < sttSegs.length; j++) { + segments.push(sttSegs[j]); + } + segments.push(new TextSegment({isVisible: false})); + } + else { + segments.push(new TextSegment({content: tArgs[ind].text, textDirection: (tArgs[ind].dir? tArgs[ind].dir : params.phDir)})); + } + } + } + var result = stext.displayStructure(segments, {guiDir: params.guiDir, dir: params.msgDir}, isHtml); + return result; + } + }; + })(); + + var event = null; + + function getHandler(type) { + 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; + } + } + + function isInputEventSupported(element) { + var agent = window.navigator.userAgent; + if (agent.indexOf("MSIE") >=0 || agent.indexOf("Trident") >=0 || agent.indexOf("Edge") >=0) { + return false; + } + var checked = document.createElement(element.tagName); + checked.contentEditable = true; + var isSupported = ("oninput" in checked); + if (!isSupported) { + checked.setAttribute('oninput', 'return;'); + isSupported = typeof checked['oninput'] == 'function'; + } + checked = null; + return isSupported; + } + + function attachElement(element, type, args, isRtl, locale) { + //if (!element || element.nodeType != 1 || !element.isContentEditable) + if (!element || element.nodeType != 1) { + return false; + } + if (!event) { + event = document.createEvent('Event'); + event.initEvent('TF', true, true); + } + element.setAttribute("data-tf-type", type); + var sArgs = args === "undefined"? "{}" : JSON.stringify(Array.isArray(args)? args[0] : args); + element.setAttribute("data-tf-args", sArgs); + var dir = "ltr"; + if (isRtl === "undefined") { + if (element.dir) { + dir = element.dir; + } + else if(element.style && element.style.direction) { + dir = element.style.direction; + } + isRtl = dir.toLowerCase() === "rtl"; + } + element.setAttribute("data-tf-dir", isRtl); + element.setAttribute("data-tf-locale", misc.getLocaleDetails(locale).lang); + if (isInputEventSupported(element)) { + var ehandler = element.oninput; + element.oninput = function(event) { + displayWithStructure(event.target); + }; + } + else { + element.onkeyup = function(e) { + displayWithStructure(e.target); + element.dispatchEvent(event); + }; + element.onmouseup = function(e) { + displayWithStructure(e.target); + element.dispatchEvent(event); + }; + } + displayWithStructure(element); + + return true; + } + + function detachElement(element) { + if (!element || element.nodeType != 1) { + return; + } + element.removeAttribute("data-tf-type"); + element.removeAttribute("data-tf-args"); + element.removeAttribute("data-tf-dir"); + element.removeAttribute("data-tf-locale"); + element.innerHTML = element.textContent || ""; + } + + function displayWithStructure(element) { + var txt = element.textContent || ""; + if (txt.length === 0) { + element.dispatchEvent(event); + return; + } + var selection = document.getSelection(); + var range = selection.getRangeAt(0); + var tempRange = range.cloneRange(), startNode, startOffset; + startNode = range.startContainer; + startOffset = range.startOffset; + var textOffset = 0; + if (startNode.nodeType === 3) { + textOffset += startOffset; + } + tempRange.setStart(element,0); + tempRange.setEndBefore(startNode); + var div = document.createElement('div'); + div.appendChild(tempRange.cloneContents()); + textOffset += div.textContent.length; + + element.innerHTML = getHandler(element.getAttribute("data-tf-type")). + format(txt, JSON.parse(element.getAttribute("data-tf-args")), (element.getAttribute("data-tf-dir") === "true"? true : false), + true, element.getAttribute("data-tf-locale")); + var parent = element; + var node = element; + var newOffset = 0; + var inEnd = false; + selection.removeAllRanges(); + range.setStart(element,0); + range.setEnd(element,0); + while (node) { + if (node.nodeType === 3) { + if (newOffset + node.nodeValue.length >= textOffset) { + range.setStart(node, textOffset - newOffset); + break; + } + else { + newOffset += node.nodeValue.length; + node = node.nextSibling; + } + } + else if(node.hasChildNodes()) { + parent = node; + node = parent.firstChild; + continue; + } + else { + node = node.nextSibling; + } + while (!node) { + if (parent === element) { + inEnd = true; + break; + } + node = parent.nextSibling; + parent = parent.parentNode; + } + if (inEnd) { + break; + } + } + + selection.addRange(range); + element.dispatchEvent(event); + } + + return { + /** + * Returns the HTML representation of a given structured text + * @param text - the structured text + * @param type - could be one of filepath, url, email + * @param args - pass additional arguments to the handler. generally null. + * @param isRtl - indicates if the GUI is mirrored + * @param locale - the browser locale + */ + getHtml: function (text, type, args, isRtl, locale) { + return getHandler(type).format(text, args, isRtl, true, locale); + }, + /** + * Handle Structured text correct display for a given HTML element. + * @param element - the element : should be of type div contenteditable=true + * @param type - could be one of filepath, url, email + * @param args - pass additional arguments to the handler. generally null. + * @param isRtl - indicates if the GUI is mirrored + * @param locale - the browser locale + */ + attach: function (element, type, args, isRtl, locale) { + return attachElement(element, type, args, isRtl, locale); + } + }; +})(); diff --git a/editor/js/ui/editor.js b/editor/js/ui/editor.js index fc638dde4..82bb63e16 100644 --- a/editor/js/ui/editor.js +++ b/editor/js/ui/editor.js @@ -301,19 +301,19 @@ RED.editor = (function() { var input = $("#"+prefix+"-"+property); if (input.attr('type') === "checkbox") { input.prop('checked',node[property]); - } + } else { var val = node[property]; if (val == null) { val = ""; } - if ("format" in definition[property] && definition[property].format !== "" && input[0].nodeName === "DIV") { - input.html(RED.format.getHtml(val, definition[property].format, {}, false, "en")); - RED.format.attach(input[0], definition[property].format, {}, false, "en"); + if (definition[property].hasOwnProperty("format") && definition[property].format !== "" && input[0].nodeName === "DIV") { + input.html(RED.text.format.getHtml(val, definition[property].format, {}, false, "en")); + RED.text.format.attach(input[0], definition[property].format, {}, false, "en"); } else { - input.val(val).attr("dir", RED.bidi.resolveBaseTextDir(val)); - RED.bidi.initInputEvents(input); - } + input.val(val).attr("dir", RED.text.bidi.resolveBaseTextDir(val)); + RED.text.bidi.initInputEvents(input); + } } } @@ -1205,7 +1205,7 @@ RED.editor = (function() { changes['name'] = editing_node.name; editing_node.name = newName; changed = true; - $("#menu-item-workspace-menu-"+editing_node.id.replace(".","-")).text(RED.bidi.enforceTextDirectionWithUCC(newName)); + $("#menu-item-workspace-menu-"+editing_node.id.replace(".","-")).text(RED.text.bidi.enforceTextDirectionWithUCC(newName)); } var newDescription = subflowEditor.getValue(); @@ -1297,8 +1297,8 @@ RED.editor = (function() { value: "" }); - $("#subflow-input-name").val(subflow.name).attr("dir", RED.bidi.resolveBaseTextDir(subflow.name)); - RED.bidi.initInputEvents($("#subflow-input-name")); + $("#subflow-input-name").val(subflow.name).attr("dir", RED.text.bidi.resolveBaseTextDir(subflow.name)); + RED.text.bidi.initInputEvents($("#subflow-input-name")); subflowEditor.getSession().setValue(subflow.info||"",-1); var userCount = 0; var subflowType = "subflow:"+editing_node.id; diff --git a/editor/js/ui/palette.js b/editor/js/ui/palette.js index 236a0c612..27f317fea 100644 --- a/editor/js/ui/palette.js +++ b/editor/js/ui/palette.js @@ -91,15 +91,15 @@ RED.palette = (function() { el.css({height:multiLineNodeHeight+"px"}); var labelElement = el.find(".palette_label"); - labelElement.html(lines).attr('dir', RED.bidi.resolveBaseTextDir(lines)); + labelElement.html(lines).attr('dir', RED.text.bidi.resolveBaseTextDir(lines)); el.find(".palette_port").css({top:(multiLineNodeHeight/2-5)+"px"}); var popOverContent; try { - var l = "<p><b>"+RED.bidi.enforceTextDirectionWithUCC(label)+"</b></p>"; + var l = "<p><b>"+RED.text.bidi.enforceTextDirectionWithUCC(label)+"</b></p>"; if (label != type) { - l = "<p><b>"+RED.bidi.enforceTextDirectionWithUCC(label)+"</b><br/><i>"+type+"</i></p>"; + l = "<p><b>"+RED.text.bidi.enforceTextDirectionWithUCC(label)+"</b><br/><i>"+type+"</i></p>"; } popOverContent = $(l+(info?info:$("script[data-help-name|='"+type+"']").html()||"<p>"+RED._("palette.noInfo")+"</p>").trim()) .filter(function(n) { diff --git a/editor/js/ui/tab-info.js b/editor/js/ui/tab-info.js index 11702e74d..0e983957b 100644 --- a/editor/js/ui/tab-info.js +++ b/editor/js/ui/tab-info.js @@ -70,7 +70,7 @@ RED.sidebar.info = (function() { var table = '<table class="node-info"><tbody>'; table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.node")+'</td></tr>'; if (node.type != "subflow" && node.name) { - table += '<tr><td>'+RED._("common.label.name")+'</td><td>&nbsp;<span class="bidiAware" dir="'+RED.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>'; + table += '<tr><td>'+RED._("common.label.name")+'</td><td>&nbsp;<span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>'; } table += "<tr><td>"+RED._("sidebar.info.type")+"</td><td>&nbsp;"+node.type+"</td></tr>"; table += "<tr><td>"+RED._("sidebar.info.id")+"</td><td>&nbsp;"+node.id+"</td></tr>"; @@ -93,7 +93,7 @@ RED.sidebar.info = (function() { userCount++; } }); - table += '<tr><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>'; + table += '<tr><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>'; table += "<tr><td>"+RED._("sidebar.info.instances")+"</td><td>"+userCount+"</td></tr>"; } @@ -140,14 +140,14 @@ RED.sidebar.info = (function() { table += "</tbody></table><hr/>"; if (!subflowNode && node.type != "comment") { var helpText = $("script[data-help-name|='"+node.type+"']").html()||""; - table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.bidi.resolveBaseTextDir(helpText)+'">'+helpText+'</span></div>'; + table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(helpText)+'">'+helpText+'</span></div>'; } if (subflowNode) { - table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.bidi.resolveBaseTextDir(subflowNode.info||"")+'">'+marked(subflowNode.info||"")+'</span></div>'; + table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.info||"")+'">'+marked(subflowNode.info||"")+'</span></div>'; } else if (node._def && node._def.info) { var info = node._def.info; var textInfo = (typeof info === "function" ? info.call(node) : info); - table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.bidi.resolveBaseTextDir(textInfo)+'">'+marked(textInfo)+'</span></div>'; + table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(textInfo)+'">'+marked(textInfo)+'</span></div>'; //table += '<div class="node-help">'+(typeof info === "function" ? info.call(node) : info)+'</div>'; } diff --git a/editor/js/ui/tabs.js b/editor/js/ui/tabs.js index 2a7a6c8d9..e0ba8ef88 100644 --- a/editor/js/ui/tabs.js +++ b/editor/js/ui/tabs.js @@ -127,7 +127,7 @@ RED.tabs = (function() { $('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link); } var span = $('<span/>',{class:"bidiAware"}).text(tab.label).appendTo(link); - span.attr('dir', RED.bidi.resolveBaseTextDir(tab.label)); + span.attr('dir', RED.text.bidi.resolveBaseTextDir(tab.label)); link.on("click",onTabClick); link.on("dblclick",onTabDblClick); @@ -240,7 +240,7 @@ RED.tabs = (function() { tabs[id].label = label; var tab = ul.find("a[href='#"+id+"']"); tab.attr("title",label); - tab.find("span").text(label).attr('dir', RED.bidi.resolveBaseTextDir(label)); + tab.find("span").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label)); updateTabWidths(); }, order: function(order) { diff --git a/editor/js/ui/view.js b/editor/js/ui/view.js index 920d77feb..a56b082d0 100644 --- a/editor/js/ui/view.js +++ b/editor/js/ui/view.js @@ -1792,7 +1792,7 @@ RED.view = (function() { l = d._def.label; try { l = (typeof l === "function" ? l.call(d) : l)||""; - l = RED.bidi.enforceTextDirectionWithUCC(l); + l = RED.text.bidi.enforceTextDirectionWithUCC(l); } catch(err) { console.log("Definition error: "+d.type+".label",err); l = d.type; @@ -2155,7 +2155,7 @@ RED.view = (function() { ).classed("link_selected", false); } - RED.bidi.enforceTextDirectionOnPage(); + RED.text.bidi.enforceTextDirectionOnPage(); if (d3.event) { d3.event.preventDefault(); @@ -2361,7 +2361,7 @@ RED.view = (function() { redraw(); }, toggleTextDir: function(value) { - RED.bidi.setTextDirection(value); + RED.text.bidi.setTextDirection(value); RED.nodes.eachNode(function(n) { n.dirty = true;}); redraw(); RED.palette.refresh(); diff --git a/editor/js/ui/workspaces.js b/editor/js/ui/workspaces.js index f41393232..a1cc00355 100644 --- a/editor/js/ui/workspaces.js +++ b/editor/js/ui/workspaces.js @@ -94,7 +94,7 @@ RED.workspaces = (function() { workspace_tabs.renameTab(workspace.id,label); RED.nodes.dirty(true); RED.sidebar.config.refresh(); - $("#menu-item-workspace-menu-"+workspace.id.replace(".","-")).text(RED.bidi.enforceTextDirectionWithUCC(label)); + $("#menu-item-workspace-menu-"+workspace.id.replace(".","-")).text(RED.text.bidi.enforceTextDirectionWithUCC(label)); } RED.tray.close(); } @@ -109,8 +109,8 @@ RED.workspaces = (function() { '</div>').appendTo(dialogForm); $('<input type="text" style="display: none;" />').prependTo(dialogForm); dialogForm.submit(function(e) { e.preventDefault();}); - $("#node-input-name").val(workspace.label).attr("dir", RED.bidi.resolveBaseTextDir(workspace.label)); - RED.bidi.initInputEvents($("#node-input-name")); + $("#node-input-name").val(workspace.label).attr("dir", RED.text.bidi.resolveBaseTextDir(workspace.label)); + RED.text.bidi.initInputEvents($("#node-input-name")); dialogForm.i18n(); }, close: function() { @@ -226,7 +226,7 @@ RED.workspaces = (function() { refresh: function() { RED.nodes.eachWorkspace(function(ws) { workspace_tabs.renameTab(ws.id,ws.label); - $("#menu-item-workspace-menu-"+ws.id.replace(".","-")).text(RED.bidi.enforceTextDirectionWithUCC(ws.label)); + $("#menu-item-workspace-menu-"+ws.id.replace(".","-")).text(RED.text.bidi.enforceTextDirectionWithUCC(ws.label)); }) RED.nodes.eachSubflow(function(sf) {