<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

var bidiUtil = require("./lib/bidiutil");
var lang = require("./lib/lang");
var useragent = require("./lib/useragent");

/**
 * This object is used to ensure Bi-Directional support (for languages with text flowing from right to left, like Arabic or Hebrew)
 * including correct caret positioning, text selection mouse and keyboard arrows functioning
 * @class BidiHandler
 **/

/**
 * Creates a new `BidiHandler` object
 * @param {EditSession} session The session to use
 *
 * @constructor
 **/
var BidiHandler = function(session) {
    this.session = session;
    this.bidiMap = {};
    /* current screen row */
    this.currentRow = null;
    this.bidiUtil = bidiUtil;
    /* Arabic/Hebrew character width differs from regular character width */
    this.charWidths = [];
    this.EOL;
    this.showInvisibles;
    this.isRtlDir = false;
    this.line = "";
    this.wrapIndent = 0;
    /* font face has been set to Courier to ensure monospaced Bidi characters in Chrome/Opera */
    this.isCourierFont = false;
    this.isLastRow = false;
    this.EOF = "\xB6";
};

(function() {
    /**
     * Returns 'true' if row contains Bidi characters, in such case
     * creates Bidi map to be used in operations related to selection
     * (keyboard arrays, mouse click, select)
     * @param {Number} the screen row to be checked
     * @param {Number} the document row to be checked [optional]
     * @param {Number} the wrapped screen line index [ optional]
     **/
	this.isBidiRow = function(screenRow, docRow, splitIndex) {
		if (screenRow !== this.currentRow) {
			this.currentRow = screenRow;
			this.updateRowLine(docRow, splitIndex);
			this.updateBidiMap();
		}
		return this.bidiMap.bidiLevels;
	};

	this.hasBidiCharacters = function(str) {
		return /[\u0591-\u06ff\ufb1d-\ufefc]/.test(str);
	};

	this.getDocumentRow = function() {
		var docRow = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var index = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			if (index >= 0)
				docRow = this.session.$docRowCache[index];
		}

		return docRow;
	};

	this.getSplitIndex = function() {
		var splitIndex = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var currentIndex, prevIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			while (this.currentRow - splitIndex > 0) {
				currentIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow - splitIndex - 1);
				if (currentIndex !== prevIndex)
					break;

				prevIndex = currentIndex;
				splitIndex++;
			}
		}

		return splitIndex;
	};

	this.updateRowLine = function(docRow, splitIndex) {
		if (docRow === undefined)
			docRow = this.getDocumentRow();
			
		this.wrapIndent = 0;
		this.isLastRow = (docRow === this.session.getLength() - 1);
		this.line = this.session.getLine(docRow);
		if (this.session.$useWrapMode) {
			var splits = this.session.$wrapData[docRow];
			if (splits) {
				if (splitIndex === undefined)
					splitIndex = this.getSplitIndex();

				if(splitIndex > 0 && splits.length) {
					this.wrapIndent = splits.indent;
					this.line = (splitIndex < splits.length) ?
						this.line.substring(splits[splitIndex - 1], splits[splits.length - 1]) :
							this.line.substring(splits[splits.length - 1]);
				} else {
					this.line = this.line.substring(0, splits[splitIndex]);
				}
			}
		}
			
		/* replace tab and wide characters by commensurate spaces */
		var session = this.session, shift = 0, size;
		this.line = this.line.replace(/\t|[\u1100-\u2029, \u202F-\uFFE6]/g, function(ch, i){
			if (ch === '\t' || session.isFullWidth(ch.charCodeAt(0))) {
				size = (ch === '\t') ? session.getScreenTabSize(i + shift) : 2;
				shift += size - 1;
				return lang.stringRepeat(bidiUtil.DOT, size);		
			}
		});
	};
	
	this.updateBidiMap = function() {
		var textCharTypes = [], endOfLine = this.isLastRow ? this.EOF : this.EOL;
		var line = this.line + (this.showInvisibles ? endOfLine : bidiUtil.DOT);
		if (bidiUtil.hasBidiCharacters(line, textCharTypes)) {
			this.bidiMap = bidiUtil.doBidiReorder(line, textCharTypes, this.isRtlDir);
		} else {
			this.bidiMap = {};
		}
	};

    /**
     * Resets stored info related to current screen row
     **/
	this.markAsDirty = function() {	
		this.currentRow = null;
	};

    /**
     * Updates array of character widths
     * @param {Object} font metrics
     *
     **/
	this.updateCharacterWidths = function($fontMetrics) {
		var _regularCharWidth = $fontMetrics.$measureCharWidth('a'), _bidiCharWidth = $fontMetrics.$measureCharWidth('\u05d4');
		/* on some Windows7 OS not 'Monaco' but 'Courier' family is true monospaced font for Bidi characters in Chrome based browsers */<commit_msg>fix width of EOL and more<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

var bidiUtil = require("./lib/bidiutil");
var lang = require("./lib/lang");
var useragent = require("./lib/useragent");

/**
 * This object is used to ensure Bi-Directional support (for languages with text flowing from right to left, like Arabic or Hebrew)
 * including correct caret positioning, text selection mouse and keyboard arrows functioning
 * @class BidiHandler
 **/

/**
 * Creates a new `BidiHandler` object
 * @param {EditSession} session The session to use
 *
 * @constructor
 **/
var BidiHandler = function(session) {
    this.session = session;
    this.bidiMap = {};
    /* current screen row */
    this.currentRow = null;
    this.bidiUtil = bidiUtil;
    /* Arabic/Hebrew character width differs from regular character width */
    this.charWidths = [];
    this.EOL;
    this.showInvisibles;
    this.isRtlDir = false;
    this.line = "";
    this.wrapIndent = 0;
    /* font face has been set to Courier to ensure monospaced Bidi characters in Chrome/Opera */
    this.isCourierFont = false;
    this.isLastRow = false;
    this.EOF = "\xB6";
};

(function() {
    /**
     * Returns 'true' if row contains Bidi characters, in such case
     * creates Bidi map to be used in operations related to selection
     * (keyboard arrays, mouse click, select)
     * @param {Number} the screen row to be checked
     * @param {Number} the document row to be checked [optional]
     * @param {Number} the wrapped screen line index [ optional]
     **/
	this.isBidiRow = function(screenRow, docRow, splitIndex) {
		if (screenRow !== this.currentRow) {
			this.currentRow = screenRow;
			this.updateRowLine(docRow, splitIndex);
			this.updateBidiMap();
		}
		return this.bidiMap.bidiLevels;
	};

	this.hasBidiCharacters = function(str) {
		return /[\u0591-\u06ff\ufb1d-\ufefc]/.test(str);
	};

	this.getDocumentRow = function() {
		var docRow = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var index = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			if (index >= 0)
				docRow = this.session.$docRowCache[index];
		}

		return docRow;
	};

	this.getSplitIndex = function() {
		var splitIndex = 0;
		var rowCache = this.session.$screenRowCache;
		if (rowCache.length) {
			var currentIndex, prevIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow);
			while (this.currentRow - splitIndex > 0) {
				currentIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow - splitIndex - 1);
				if (currentIndex !== prevIndex)
					break;

				prevIndex = currentIndex;
				splitIndex++;
			}
		}

		return splitIndex;
	};

	this.updateRowLine = function(docRow, splitIndex) {
		if (docRow === undefined)
			docRow = this.getDocumentRow();
			
		this.wrapIndent = 0;
		this.isLastRow = (docRow === this.session.getLength() - 1);
		this.line = this.session.getLine(docRow);
		if (this.session.$useWrapMode) {
			var splits = this.session.$wrapData[docRow];
			if (splits) {
				if (splitIndex === undefined)
					splitIndex = this.getSplitIndex();

				if(splitIndex > 0 && splits.length) {
					this.wrapIndent = splits.indent;
					this.line = (splitIndex < splits.length) ?
						this.line.substring(splits[splitIndex - 1], splits[splits.length - 1]) :
							this.line.substring(splits[splits.length - 1]);
				} else {
					this.line = this.line.substring(0, splits[splitIndex]);
				}
			}
		}
			
		/* replace tab and wide characters by commensurate spaces */
		var session = this.session, shift = 0, size;
		this.line = this.line.replace(/\t|[\u1100-\u2029, \u202F-\uFFE6]/g, function(ch, i){
			if (ch === '\t' || session.isFullWidth(ch.charCodeAt(0))) {
				size = (ch === '\t') ? session.getScreenTabSize(i + shift) : 2;
				shift += size - 1;
				return lang.stringRepeat(bidiUtil.DOT, size);		
			}
			return ch;
		});
	};
	
	this.updateBidiMap = function() {
		var textCharTypes = [], endOfLine = this.isLastRow ? this.EOF : this.EOL;
		var line = this.line + (this.showInvisibles ? endOfLine : bidiUtil.DOT);
		if (bidiUtil.hasBidiCharacters(line, textCharTypes)) {
			this.bidiMap = bidiUtil.doBidiReorder(line, textCharTypes, this.isRtlDir);
		} else {
			this.bidiMap = {};
		}
	};

    /**
     * Resets stored info related to current screen row
     **/
	this.markAsDirty = function() {	
		this.currentRow = null;
	};

    /**
     * Updates array of character widths
     * @param {Object} font metrics
     *
     **/
	this.updateCharacterWidths = function($fontMetrics) {
		var _regularCharWidth = $fontMetrics.$measureCharWidth('a'), _bidiCharWidth = $fontMetrics.$measureCharWidth('\u05d4');
		/* on some Windows7 OS not 'Monaco' but 'Courier' family is true monospaced font for Bidi characters in Chrome based browsers */
{'fraction_non_alphanumeric': 0.11494519067469508, 'fraction_numerical': 0.006947660954145438, 'mean_word_length': 3.429059829059829, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7449543', 'n_tokens_mistral': 4366, 'n_tokens_neox': 3855, 'n_words': 1237}