Skip to content

Commit

Permalink
Merge branch 'ashrafg-bidi' into bidi
Browse files Browse the repository at this point in the history
  • Loading branch information
knolleary committed Dec 5, 2016
2 parents b67df2c + e5511ea commit e0ac565
Show file tree
Hide file tree
Showing 52 changed files with 1,107 additions and 212 deletions.
6 changes: 4 additions & 2 deletions Gruntfile.js
Expand Up @@ -104,8 +104,10 @@ module.exports = function(grunt) {
"editor/js/settings.js",
"editor/js/user.js",
"editor/js/comms.js",
"editor/js/text/bidi.js",
"editor/js/text/format.js",
"editor/js/bidi/bidi.js",
"editor/js/bidi/base-text-dir.js",
"editor/js/bidi/format.js",
"editor/js/bidi/numeric-shaping.js",
"editor/js/ui/state.js",
"editor/js/nodes.js",
"editor/js/history.js",
Expand Down
67 changes: 67 additions & 0 deletions editor/js/bidi/base-text-dir.js
@@ -0,0 +1,67 @@
/**
* 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.bidi.baseTextDir = (function() {
var LRE = "\u202A",
RLE = "\u202B",
PDF = "\u202C";

function _isRTLValue(stringValue) {
var length = stringValue.length;
for (var i = 0 ; i < length; i++) {
if (_isBidiChar(stringValue.charCodeAt(i))) {
return true;
} else if (_isLatinChar(stringValue.charCodeAt(i))) {
return false;
}
}
return RED.bidi.isMirroringEnabled();
}

function _isBidiChar(c) {
return (c >= 0x05d0 && c <= 0x05ff)||
(c >= 0x0600 && c <= 0x065f)||
(c >= 0x066a && c <= 0x06ef)||
(c >= 0x06fa && c <= 0x07ff)||
(c >= 0xfb1d && c <= 0xfdff)||
(c >= 0xfe70 && c <= 0xfefc);
}

function _isLatinChar(c) {
return (c > 64 && c < 91) || (c > 96 && c < 123);
}

/**
* Enforces the text direction of a given string by adding
* UCC (Unicode Control Characters)
* @param value - the string
*/
function _enforceTextDirectionWithUCC(value) {
if (value) {
var dir = RED.bidi.resolveBaseTextDir(value);
if (dir == "ltr") {
return LRE + value + PDF;
} else if (dir == "rtl") {
return RLE + value + PDF;
}
}
return value;
}

return {
enforceTextDirectionWithUCC: _enforceTextDirectionWithUCC,
isRTLValue : _isRTLValue
}
})();
189 changes: 189 additions & 0 deletions editor/js/bidi/bidi.js
@@ -0,0 +1,189 @@
/**
* 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.bidi = (function() {
var textDir = "";
var shaperType = "";
var calendarType = "";

/**
* Indicates the type of bidi-support
* BTD : Base text direction
* NS : Numeric Shaping
* CALENDAR : National Calendar
* STT_ATTACH : Structure Text Support, it is using to call attach function located at format.js
* STT_GETHTML : STT_ATTACH : Structure Text Support, it is using to call getHtml function located at format.js
*/
var _flags = {
BTD: 1,
NS: 2,
CALENDAR: 4,
STT_ATTACH: 8,
STT_GETHTML: 16
};

/**
* Reverse component position when mirroring is enabled
*/
var _componentPos = {};

/**
* Check if browser language is RTL language
*/
function _isMirroringEnabled() {
var isRTLLang = new RegExp("^(ar|he)").test(navigator.language);
if (isRTLLang) {
_componentPos.left = "right";
_componentPos.right = "left";
return true;
} else {
_componentPos.left = "left";
_componentPos.right = "right";
return false;
}
}

/**
* @param val - the numeric shaping type: None , National or contextual
*/
function _setNumericShapingType(val) {
shaperType = val;
_refreshView();
}

/**
* Sets the national calendar preference
* @param val - the calendar type hijri, hebrew or gregorian
*/
function _setCalendarType(val) {
calendarType = val;
}

/**
* Formats the date according to the current selected calendar
* @param date - the date object to be formatted
*/
function _getGlobalizedDate(date) {
var options = {};
var lang = navigator.language;
if (calendarType === "hijri") {
options = lang + "-u-ca-islamic";
} else if (calendarType === "hebrew") {
options = lang + "-u-ca-hebrew";
}
return date.toLocaleString(options);
}

/**
* Sets the text direction preference
* @param dir - the text direction preference
*/
function _setTextDirection(dir) {
textDir = dir;
_refreshView();
_enforceTextDirectionOnPage();
}

/**
* Enforces the text direction for all the spans with style bidiAware under
* workspace or sidebar div
*/
function _enforceTextDirectionOnPage() {
$("#workspace").find('span.bidiAware').each(function() {
$(this).attr("dir", _resolveBaseTextDir($(this).html()));
});
$("#sidebar").find('span.bidiAware').each(function() {
$(this).attr("dir", _resolveBaseTextDir($(this).text()));
});
}

/**
* Determines the text direction of a given string.
* @param value - the string
*/
function _resolveBaseTextDir(value) {
if (textDir == "auto") {
if (RED.bidi.baseTextDir.isRTLValue(value)) {
return "rtl";
} else {
return "ltr";
}
} else {
return textDir;
}
}

/**
* Adds event listeners to the Input to ensure its text-direction attribute
* is properly set based on its content.
* @param input - the input field
*/
function _prepareInput(input) {
input.on("keyup",_onInputChange).on("paste",_onInputChange).on("cut",_onInputChange);
// Set the initial text direction
_onInputChange.call(input);
}

function _onInputChange() {
$(this).attr("dir", _resolveBaseTextDir($(this).val()));
}

/**
* Refreshes the view whenever changing the user preferences
*/
function _refreshView() {
RED.nodes.eachNode(function(n) { n.dirty = true;});
RED.view.redraw();
RED.palette.refresh();
}

/**
* Applying bidi support for these features: base-text-dir ,Numeric-shaping or both, STT ,Calendar which is controlled by the flag value.
* @param value- the string to apply the bidi-support on it.
* @param flag - indicates the type of bidi-support (Base-text-dir ,Numeric-shaping or both, STT , Calendar)
* @param type - could be one of filepath, url, email
* @param args - pass additional arguments to the handler. generally null.
*/
function _applyBidiSupport(value, flag, type, args) {
switch (flag) {
case 0:
value = RED.bidi.baseTextDir.enforceTextDirectionWithUCC(value);
return RED.bidi.numericShaping.shape(value, shaperType, textDir);
case 1:
return RED.bidi.baseTextDir.enforceTextDirectionWithUCC(value);
case 2:
return RED.bidi.numericShaping.shape(value, shaperType, textDir);
case 4:
return _getGlobalizedDate(value);
case 8:
return RED.bidi.format.attach(value, type, args, _isMirroringEnabled(), navigator.language);
case 16:
return RED.bidi.format.getHtml(value, type, args, _isMirroringEnabled(), navigator.language);
default:
return value;
}
}
return {
isMirroringEnabled: _isMirroringEnabled,
setNumericShapingType : _setNumericShapingType,
setCalendarType: _setCalendarType,
setTextDirection : _setTextDirection,
applyBidiSupport : _applyBidiSupport,
resolveBaseTextDir : _resolveBaseTextDir,
prepareInput: _prepareInput,
flags: _flags,
componentPos: _componentPos
}
})();
3 changes: 2 additions & 1 deletion editor/js/text/format.js → editor/js/bidi/format.js
Expand Up @@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.text.format = (function() {

RED.bidi.format = (function() {

var TextSegment = (function() {
var TextSegment = function (obj) {
Expand Down
86 changes: 86 additions & 0 deletions editor/js/bidi/numeric-shaping.js
@@ -0,0 +1,86 @@
/**
* 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.bidi.numericShaping = (function(){
var regex = /([0-9])|([\u0660-\u0669])|([\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE]+)|([^0-9\u0660-\u0669\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE\u0600-\u0607\u0609-\u060A\u060C\u060E-\u061A\u064B-\u066C\u0670\u06D6-\u06E4\u06E7-\u06ED\u06F0-\u06F9\u08E4-\u08FF\uFD3E-\uFD3F\uFDD0-\uFDEF\uFDFD\uFEFF\u0000-\u0040\u005B-\u0060\u007B-\u007F\u0080-\u00A9\u00AB-\u00B4\u00B6-\u00B9\u00BB-\u00BF\u00D7\u00F7\u02B9-\u02BA\u02C2-\u02CF\u02D2-\u02DF\u02E5-\u02ED\u02EF-\u02FF\u2070\u2074-\u207E\u2080-\u208E\u2100-\u2101\u2103-\u2106\u2108-\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A-\u213B\u2140-\u2144\u214A-\u214D\u2150-\u215F\u2189\uA720-\uA721\uA788\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFFE0-\uFFE6\uFFE8-\uFFEE]+)/g;

/**
* Converts the digits in the text to European or Arabic digits According to
* the shaperType & the textDir.
*/
function _shape(text, shaperType, textDir) {
text = text.toString();
if(textDir === "auto"){
textDir = RED.bidi.resolveBaseTextDir(text);
}
if (!text) {
return text;
}
switch (shaperType) {
case "defaultNumeral":
return _shapeEuropean(text);
case "national":
return _shapeArabic(text);
case "contextual":
return _shapeContextual(text, textDir === "rtl" ? 2 : 1);
default:
return text;
}
}

/**
* Converts the digits in the text to European digits.
*/
function _shapeEuropean(text) {
return text.replace(/[\u0660-\u0669]/g, function(c) {
return c.charCodeAt(0) - 1632;
});
}

/**
* Converts the digits in the text to Arabic digits.
*/
function _shapeArabic(text) {
return text.replace(/[0-9]/g, function(c) {
return String.fromCharCode(parseInt(c) + 1632);
});
}

/**
* Converts the digits in the text to European or Arabic digits
* According to the type of the preceding strong character.
* @param context:The current effective context.
* Allowed values:
* '1': European context
* '2': Arabic context
*/
function _shapeContextual(text, context) {
return text.replace(regex, function(match, latinDigit, arabicDigit, strongArabic, strongLatin){
if (latinDigit) {
return (context === 2) ? String.fromCharCode(parseInt(latinDigit) + 1632) : latinDigit;
} else if (arabicDigit) {
return (context === 1) ? arabicDigit.charCodeAt(0) - 1632 : arabicDigit;
} else if (strongArabic) {
context = 2;
} else if (strongLatin) {
context = 1;
}
return match;
});
}
return {
shape: _shape
}
})();

0 comments on commit e0ac565

Please sign in to comment.