/* * (c) Copyright Ascensio System SIA 2010-2024 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ "use strict"; (function(window, undefined) { window["AscInputMethod"] = window["AscInputMethod"] || {}; /// // такие методы нужны в апи // baseEditorsApi.prototype.Begin_CompositeInput = function() // baseEditorsApi.prototype.Replace_CompositeText = function(arrCharCodes) // baseEditorsApi.prototype.Set_CursorPosInCompositeText = function(nPos) // baseEditorsApi.prototype.Get_CursorPosInCompositeText = function() // baseEditorsApi.prototype.End_CompositeInput = function() // baseEditorsApi.prototype.Get_MaxCursorPosInCompositeText = function() // baseEditorsApi.prototype.onKeyDown = function(e) // baseEditorsApi.prototype.onKeyPress = function(e) // baseEditorsApi.prototype.onKeyUp = function(e) /// function CKeyboardEventWrapper(e) { // emulate this.altKey = e.altKey; this.ctrlKey = e.ctrlKey; this.metaKey = e.metaKey; this.shiftKey = e.shiftKey; this.charCode = e.charCode; this.keyCode = e.keyCode; this.which = e.which; this.code = e.code; this.key = e.key; this.srcElement = e.srcElement; this.target = e.target; // work this._isDefaultPrevented = false; } CKeyboardEventWrapper.prototype = { preventDefault : function() { this._isDefaultPrevented = true; }, stopPropagation : function() { // nothing } }; function CTextInput(api) { this.Api = api; this.TargetId = null; // id caret this.HtmlDiv = null; // для незаметной реализации одной textarea недостаточно this.TextArea_Not_ContentEditableDiv = true; this.HtmlArea = null; this.HtmlAreaOffset = 50; // height in pix this.HtmlAreaWidth = 200; this.LockerTargetTimer = -1; this.KeyDownFlag = false; this.KeyPressFlag = false; this.IsLockTargetMode = false; this.nativeFocusElement = null; this.nativeFocusElementNoRemoveOnElementFocus = false; this.InterfaceEnableKeyEvents = true; this.debugTexBoxMaxW = 100; this.debugTexBoxMaxH = 20; this.isDebug = false; this.isSystem = false; this.isShow = false; // chrome element for left/top this.FixedPosCheckElementX = 0; this.FixedPosCheckElementY = 0; this.virtualKeyboardClickTimeout = -1; this.virtualKeyboardClickPrevent = false; // current text info this.TextBeforeComposition = ""; this.Text = ""; this.Target = 0; this.CompositionStart = 0; this.CompositionEnd = 0; this.IsComposition = false; this.ApiIsComposition = false; // Notes offset for slides this.TargetOffsetY = 0; // editor_sdk div sizes (for visible textarea) this.editorSdkW = 0; this.editorSdkH = 0; this.ReadOnlyCounter = 0; this.LastReplaceText = []; this.IsLastReplaceFlag = false; this.isNoClearOnFocus = false; this.keyPressInput = ""; this.isInputHelpersPresent = false; this.isInputHelpers = {}; this.isKeyPressOnUp = AscCommon.AscBrowser.isAppleDevices; // keyPress может приходить ДО oncompositionstart, а это проблема. this.keyPressOnUpCodes = []; this.isKeyPressOnUpStackedMode = this.isKeyPressOnUp; this.isHardCheckKeyboard = AscCommon.AscBrowser.isSailfish; } CTextInput.prototype = { log : function(_val) { //console.log(_val); }, init : function(target_id, parent_id) { this.TargetId = target_id; this.HtmlDiv = document.createElement("div"); this.HtmlDiv.id = "area_id_parent"; this.HtmlDiv.style.background = "transparent"; this.HtmlDiv.style.border = "none"; // в хроме скроллируется редактор, когда курсор текстового поля выходит за пределы окна if (AscCommon.AscBrowser.isChrome) this.HtmlDiv.style.position = "fixed"; else this.HtmlDiv.style.position = "absolute"; this.HtmlDiv.style.zIndex = 10; this.HtmlDiv.style.width = "20px"; this.HtmlDiv.style.height = "50px"; this.HtmlDiv.style.overflow = "hidden"; this.HtmlDiv.style.boxSizing = "content-box"; this.HtmlDiv.style.webkitBoxSizing = "content-box"; this.HtmlDiv.style.MozBoxSizing = "content-box"; if (this.TextArea_Not_ContentEditableDiv) { this.HtmlArea = document.createElement("textarea"); } else { this.HtmlArea = document.createElement("div"); this.HtmlArea.setAttribute("contentEditable", true); } this.HtmlArea.id = "area_id"; if (this.Api.isViewMode && this.Api.isMobileVersion) this.setReadOnlyWrapper(true); var _style = ("left:-" + (this.HtmlAreaWidth >> 1) + "px;top:" + (-this.HtmlAreaOffset) + "px;"); _style += ("background:transparent;border:none;position:absolute;text-shadow:0 0 0 #000;outline:none;color:transparent;width:" + this.HtmlAreaWidth + "px;height:50px;"); _style += "overflow:hidden;padding:0px;margin:0px;font-family:arial;resize:none;font-weight:normal;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;"; _style += "touch-action: none;-webkit-touch-callout: none;color:transparent;caret-color:transparent;"; _style += AscCommon.AscBrowser.isAppleDevices ? "font-size:0px;" : "font-size:8px;" this.HtmlArea.setAttribute("style", _style); this.HtmlArea.setAttribute("spellcheck", false); this.HtmlArea.setAttribute("autocapitalize", "none"); this.HtmlArea.setAttribute("autocomplete", "off"); this.HtmlArea.setAttribute("autocorrect", "off"); this.HtmlDiv.appendChild(this.HtmlArea); this.appendInputToCanvas(parent_id); // events: var oThis = this; this.HtmlArea["onkeydown"] = function(e) { if (AscCommon.AscBrowser.isSafariMacOs) { var cmdButton = (e.ctrlKey || e.metaKey) ? true : false; var buttonCode = ((e.keyCode == 67) || (e.keyCode == 88) || (e.keyCode == 86)); if (cmdButton && buttonCode) oThis.IsDisableKeyPress = true; else oThis.IsDisableKeyPress = false; } return oThis.onKeyDown(e); }; this.HtmlArea["onkeypress"] = function(e) { if (oThis.IsDisableKeyPress == true) { // macOS Sierra send keypress before copy event oThis.IsDisableKeyPress = false; var cmdButton = (e.ctrlKey || e.metaKey) ? true : false; if (cmdButton) return; } return oThis.onKeyPress(e); }; this.HtmlArea["onkeyup"] = function(e) { oThis.IsDisableKeyPress = false; return oThis.onKeyUp(e); }; this.HtmlArea.addEventListener("input", function(e) { return oThis.onInput(e); }, false); this.HtmlArea.addEventListener("compositionstart", function(e) { return oThis.onCompositionStart(e); }, false); this.HtmlArea.addEventListener("compositionupdate", function(e) { return oThis.onCompositionUpdate(e); }, false); this.HtmlArea.addEventListener("compositionend", function(e) { return oThis.onCompositionEnd(e); }, false); this.show(); this.Api.Input_UpdatePos(); if (AscCommon.AscBrowser.isAndroid) { this.HtmlArea.onclick = function (e) { var _this = AscCommon.g_inputContext; if (-1 != _this.virtualKeyboardClickTimeout) { clearTimeout(_this.virtualKeyboardClickTimeout); _this.virtualKeyboardClickTimeout = -1; } _this.apiCompositeEnd(); if (!_this.virtualKeyboardClickPrevent) return; _this.setReadOnlyWrapper(true); _this.virtualKeyboardClickPrevent = false; AscCommon.stopEvent(e); _this.virtualKeyboardClickTimeout = setTimeout(function () { _this.setReadOnlyWrapper(false); _this.virtualKeyboardClickTimeout = -1; }, 1); return false; }; } }, appendInputToCanvas: function (parent_id) { var oHtmlParent = null; var oHtmlTarget = document.getElementById(this.TargetId); if (undefined == parent_id) oHtmlParent = oHtmlTarget.parentNode; else oHtmlParent = document.getElementById(parent_id); // нужен еще один родитель. чтобы скроллился он, а не oHtmlParent var oHtmlDivScrollable = document.createElement("div"); oHtmlDivScrollable.id = "area_id_main"; oHtmlDivScrollable.setAttribute("style", "background:transparent;border:none;position:absolute;padding:0px;margin:0px;z-index:0;pointer-events:none;"); var parentStyle = getComputedStyle(oHtmlParent); oHtmlDivScrollable.style.left = parentStyle.left; oHtmlDivScrollable.style.top = parentStyle.top; oHtmlDivScrollable.style.width = parentStyle.width; oHtmlDivScrollable.style.height = parentStyle.height; oHtmlDivScrollable.style.overflow = "hidden"; oHtmlDivScrollable.appendChild(this.HtmlDiv); oHtmlParent.parentNode.appendChild(oHtmlDivScrollable); }, onResize : function(_editorContainerId) { var _elem = document.getElementById("area_id_main"); var _elemSrc = document.getElementById(_editorContainerId); if (!_elem || !_elemSrc) return; if (AscCommon.AscBrowser.isChrome) { var rectObject = _elemSrc.getBoundingClientRect(); this.FixedPosCheckElementX = rectObject.left; this.FixedPosCheckElementY = rectObject.top; } var _width = _elemSrc.style.width; if ((null == _width || "" == _width) && window.getComputedStyle) { var _s = window.getComputedStyle(_elemSrc); _elem.style.left = _s.left; _elem.style.top = _s.top; _elem.style.width = _s.width; _elem.style.height = _s.height; } else { _elem.style.left = _elemSrc.style.left; _elem.style.top = _elemSrc.style.top; _elem.style.width = _width; _elem.style.height = _elemSrc.style.height; } if (this.Api.isMobileVersion) { var _elem1 = document.getElementById("area_id_parent"); var _elem2 = document.getElementById("area_id"); _elem1.parentNode.style.pointerEvents = ""; _elem1.style.left = "0px"; _elem1.style.top = "-1000px"; _elem1.style.right = "0px"; _elem1.style.bottom = "-100px"; _elem1.style.width = "auto"; _elem1.style.height = "auto"; _elem2.style.left = "0px"; _elem2.style.top = "0px"; _elem2.style.right = "0px"; _elem2.style.bottom = "0px"; _elem2.style.width = "100%"; _elem2.style.height = "100%"; if (AscCommon.AscBrowser.isIE) { document.body.style["msTouchAction"] = "none"; document.body.style["touchAction"] = "none"; } } var _editorSdk = document.getElementById("editor_sdk"); this.editorSdkW = _editorSdk.clientWidth; this.editorSdkH = _editorSdk.clientHeight; }, checkFocus : function() { if (this.Api.asc_IsFocus() && !AscCommon.g_clipboardBase.IsFocus() && !AscCommon.g_clipboardBase.IsWorking()) { if (document.activeElement != this.HtmlArea) focusHtmlElement(this.HtmlArea); } }, move : function(x, y) { if (this.Api.isMobileVersion) return; var oTarget = document.getElementById(this.TargetId); if (!oTarget) return; var xPos = x ? x : parseInt(oTarget.style.left); var yPos = (y ? y : parseInt(oTarget.style.top)) + parseInt(oTarget.style.height); if (AscCommon.AscBrowser.isSafari && AscCommon.AscBrowser.isMobile) xPos = -100; if (!this.isDebug && !this.isSystem) { this.HtmlDiv.style.left = xPos + this.FixedPosCheckElementX + "px"; this.HtmlDiv.style.top = yPos + this.FixedPosCheckElementY + this.TargetOffsetY + this.HtmlAreaOffset + "px"; this.HtmlArea.scrollTop = this.HtmlArea.scrollHeight; //this.log("" + this.HtmlArea.scrollTop + ", " + this.HtmlArea.scrollHeight); } else { // this.HtmlAreaOffset - не сдвигаем, курсор должен быть виден this.debugCalculatePlace(xPos + this.FixedPosCheckElementX, yPos + this.FixedPosCheckElementY + this.TargetOffsetY); } if (window.g_asc_plugins) window.g_asc_plugins.onPluginEvent("onTargetPositionChanged"); }, emulateKeyDownApi : function(code) { var _e = { altKey : false, ctrlKey : false, shiftKey : false, target : null, charCode : 0, which : code, keyCode : code, code : "", emulated: true, preventDefault : function() {}, stopPropagation : function() {} }; this.Api.onKeyDown(_e); this.Api.onKeyUp(_e); }, clear : function(isFromFocus) { if (!this.TextArea_Not_ContentEditableDiv) { this.HtmlArea.innerHTML = ""; } else { this.HtmlArea.value = ""; } if (isFromFocus !== true) focusHtmlElement(this.HtmlArea); this.TextBeforeComposition = ""; this.Text = ""; this.Target = 0; this.CompositionStart = 0; this.CompositionEnd = 0; this.IsComposition = false; this.keyPressInput = ""; if (window.g_asc_plugins) window.g_asc_plugins.onPluginEvent("onInputHelperClear"); }, getAreaValue : function() { return this.TextArea_Not_ContentEditableDiv ? this.HtmlArea.value : this.HtmlArea.innerText; }, setReadOnly : function(isLock) { if (isLock) this.ReadOnlyCounter++; else this.ReadOnlyCounter--; // при синхронной загрузке шрифтов (десктоп) // может вызываться и в обратном порядке (setReadOnly(false), setReadOnly(true)) // поэтому сравнение с нулем неверно. отрицательные значение могут быть. this.setReadOnlyWrapper((0 >= this.ReadOnlyCounter) ? false : true); }, setReadOnlyWrapper : function(val) { this.HtmlArea.readOnly = this.Api.isViewMode ? true : val; }, show : function() { if (this.isDebug || this.isSystem) { this.log("ti: show"); document.getElementById("area_id_main").style.zIndex = 10; this.HtmlArea.style.top = "0px"; this.HtmlArea.style.width = "100%"; this.HtmlArea.style.height = "100%"; this.HtmlArea.style.background = "#FFFFFF"; this.HtmlArea.style.color = "black"; this.HtmlDiv.style.zIndex = 90; this.HtmlDiv.style.border = "2px solid #4363A4"; this.isShow = true; } }, unshow : function(isAttack) { if (this.isDebug || this.isSystem || (true == isAttack)) { this.log("ti: unshow"); document.getElementById("area_id_main").style.zIndex = 0; this.HtmlArea.style.top = ((-this.HtmlAreaOffset) + "px"); this.HtmlArea.style.width = "1000px"; this.HtmlArea.style.height = "50px"; this.HtmlArea.style.background = "transparent"; this.HtmlArea.style.color = "transparent"; this.HtmlDiv.style.zIndex = 0; this.HtmlDiv.style.border = "none"; this.isShow = false; } }, debugCalculatePlace : function(x, y) { var _left = x; var _top = y; if (undefined == _left) _left = parseInt(this.HtmlDiv.style.left); if (undefined == _top) _top = parseInt(this.HtmlDiv.style.top); var _r_max = this.editorSdkW; var _b_max = this.editorSdkH; _r_max -= 60; if ((_r_max - _left) > 50) { this.debugTexBoxMaxW = _r_max - _left; } else { _left = _r_max - 50; this.debugTexBoxMaxW = 50; } _b_max -= 40; if ((_b_max - _top) > 50) { this.debugTexBoxMaxH = _b_max - _top; } else { _top = _b_max - 50; this.debugTexBoxMaxH = 50; } if (AscCommon.AscBrowser.isSafari && AscCommon.AscBrowser.isMobile) _left = -100; this.HtmlDiv.style.left = _left + "px"; this.HtmlDiv.style.top = _top + "px"; var _height = 22; var _t = this.getAreaValue(); if (0 != _t.length) { var _editorSdk = document.getElementById("editor_sdk"); // теперь нужно расчитать ширину/высоту текстбокса var _p = document.createElement('p'); _p.style.zIndex = "-1"; _p.style.position = "absolute"; _p.style.fontFamily = "arial"; _p.style.fontSize = "12pt"; _p.style.left = "0px"; _p.style.width = this.debugTexBoxMaxW + "px"; _editorSdk.appendChild(_p); _t = _t.replace(/ /g, " "); _p.innerHTML = "" + _t + ""; var _width = _p.firstChild.offsetWidth; _width = Math.min(_width + 20, this.debugTexBoxMaxW); if (AscCommon.AscBrowser.isIE) _width += 10; var area = document.createElement('textarea'); area.style.zIndex = "-1"; area.id = "area2_id"; area.rows = 1; area.setAttribute("style", "font-family:arial;font-size:12pt;position:absolute;resize:none;padding:0px;margin:0px;font-weight:normal;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;"); area.style.overflow = "hidden"; area.style.width = _width + "px"; _editorSdk.appendChild(area); area.value = this.getAreaValue(); _height = area.clientHeight; if (area.scrollHeight > _height) _height = area.scrollHeight; _editorSdk.removeChild(_p); _editorSdk.removeChild(area); } if (_height > this.debugTexBoxMaxH) _height = this.debugTexBoxMaxH; this.HtmlDiv.style.width = _width + "px"; this.HtmlDiv.style.height = _height + "px"; // вот такая заглушка под firefox если этого не делать, то будет плохо перерисовываться border) var oldZindex = parseInt(this.HtmlDiv.style.zIndex); var newZindex = (oldZindex == 90) ? "89" : "90"; this.HtmlDiv.style.zIndex = newZindex; }, onInput : function(e, isFromCompositionUpdate) { if (this.Api.isLongAction() || this.Api.isViewMode) { AscCommon.stopEvent(e); return false; } if (this.isSystem) { if (!this.isShow) this.show(); this.debugCalculatePlace(undefined, undefined); return; } if (this.isKeyPressOnUp && this.keyPressOnUpCodes.length > 0) { // clear light if (!this.TextArea_Not_ContentEditableDiv) { this.HtmlArea.innerHTML = ""; } else { this.HtmlArea.value = ""; } this.TextBeforeComposition = ""; this.Text = ""; AscCommon.stopEvent(e); return false; } this.log("ti: onInput"); // current text value this.Text = this.getAreaValue(); this.Text = this.Text.split(" ").join(" "); var codes = []; if (this.IsComposition || this.ApiIsComposition) { var ieStart = -1; var ieEnd = -1; if (true) { var target = e.target; if (target["msGetInputContext"]) { var ctx = target["msGetInputContext"](); if (ctx) { ieStart = ctx["compositionStartOffset"]; ieEnd = ctx["compositionEndOffset"]; } } } this.CompositionEnd = this.Text.length; this.CompositionStart = this.TextBeforeComposition.length; var textReplace = this.Text.substr(this.CompositionStart); var iter; for (iter = textReplace.getUnicodeIterator(); iter.check(); iter.next()) { codes.push(iter.value()); } var isAsync = AscFonts.FontPickerByCharacter.checkTextLight(codes, true); if (!isAsync) { // ie/edge могут не присылать onCompositeEnd. И тогда ориентир - дополнительный селект if (ieStart > this.CompositionStart) { textReplace = textReplace.substr(0, ieStart - this.CompositionStart); codes = []; for (iter = textReplace.getUnicodeIterator(); iter.check(); iter.next()) { codes.push(iter.value()); } this.apiCompositeReplace(codes); this.apiCompositeEnd(); this.TextBeforeComposition = this.Text.substr(0, ieStart); this.apiCompositeStart(); this.CompositionStart = ieStart; codes = []; textReplace = this.Text.substr(this.CompositionStart); for (iter = textReplace.getUnicodeIterator(); iter.check(); iter.next()) { codes.push(iter.value()); } this.apiCompositeReplace(codes); } else { this.apiCompositeReplace(codes); } if (!this.IsComposition) { this.apiCompositeEnd(); this.TextBeforeComposition = this.Text; } } else { AscFonts.FontPickerByCharacter.loadFonts(this, function () { this.apiCompositeReplace(codes); this.apiCompositeEnd(); this.clear(); this.setReadOnly(false); }); AscCommon.stopEvent(e); this.setReadOnly(true); return false; } } else { var textToApi = this.Text.substr(this.TextBeforeComposition.length); for (var iter = textToApi.getUnicodeIterator(); iter.check(); iter.next()) { codes.push(iter.value()); } if (codes.length > 0) { this.apiInputText(codes); } this.TextBeforeComposition = this.Text; } if (!this.IsComposition) { if (this.Text.length > 0) { var _lastCode = this.Text.charCodeAt(this.Text.length - 1); if (_lastCode == 12290 || _lastCode == 46) { // китайская точка AscCommon.stopEvent(e); if (AscCommon.AscBrowser.isIE && !AscCommon.AscBrowser.isIeEdge) { // ie тепряет фокус setTimeout(function(){ window['AscCommon'].g_inputContext.clear(); focusHtmlElement(window['AscCommon'].g_inputContext.HtmlArea); }, 0); } else { this.clear(); } return false; } } } }, emulateNativeKeyDown : function(e, target) { var oEvent = document.createEvent('KeyboardEvent'); /* var _event = new KeyboardEvent("keydown", { bubbles : true, cancelable : true, char : e.charCode, shiftKey : e.shiftKey, ctrlKey : e.ctrlKey, metaKey : e.metaKey, altKey : e.altKey, keyCode : e.keyCode, which : e.which, key : e.key }); */ // Chromium Hack Object.defineProperty(oEvent, 'keyCode', { get : function() { return this.keyCodeVal; } }); Object.defineProperty(oEvent, 'which', { get : function() { return this.keyCodeVal; } }); Object.defineProperty(oEvent, 'shiftKey', { get : function() { return this.shiftKeyVal; } }); Object.defineProperty(oEvent, 'altKey', { get : function() { return this.altKeyVal; } }); Object.defineProperty(oEvent, 'metaKey', { get : function() { return this.metaKeyVal; } }); Object.defineProperty(oEvent, 'ctrlKey', { get : function() { return this.ctrlKeyVal; } }); if (AscCommon.AscBrowser.isIE) { oEvent.preventDefault = function () { try { Object.defineProperty(this, "defaultPrevented", { get: function () { return true; } }); } catch(err) { } }; } var k = e.keyCode; if (oEvent.initKeyboardEvent) { oEvent.initKeyboardEvent("keydown", true, true, window, false, false, false, false, k, k); } else { oEvent.initKeyEvent("keydown", true, true, window, false, false, false, false, k, 0); } oEvent.keyCodeVal = k; oEvent.shiftKeyVal = e.shiftKey; oEvent.altKeyVal = e.altKey; oEvent.metaKeyVal = e.metaKey; oEvent.ctrlKeyVal = e.ctrlKey; var _elem = target ? target : _getElementKeyboardDown(this.nativeFocusElement, 3); _elem.dispatchEvent(oEvent); return oEvent.defaultPrevented; }, isSpaceSymbol : function(e) { if (e.keyCode == 32) return true; if ((e.keyCode == 229) && ((e.code == "space") || (e.code == "Space") || (e.key == "Spacebar"))) return true; return false; }, systemInputEnable : function(isEnabled) { if (this.isSystem == isEnabled) return; this.isSystem = isEnabled; this.HtmlArea.style.left = this.isSystem ? "0px" : ("-" + (this.HtmlAreaWidth >> 1) + "px"); this.clear(); if (this.isShow) this.unshow(true); if (this.Api.WordControl && this.Api.WordControl.m_oLogicDocument && this.Api.WordControl.m_oLogicDocument.Document_UpdateSelectionState) this.Api.WordControl.m_oLogicDocument.Document_UpdateSelectionState(); }, debugInputEnable : function(isEnabled) { if (this.isDebug == isEnabled) return; this.isDebug = isEnabled; this.HtmlArea.style.left = this.isDebug ? "0px" : ("-" + (this.HtmlAreaWidth >> 1) + "px"); }, apiInputText : function(codes) { var isAsync = AscFonts.FontPickerByCharacter.checkTextLight(codes, true); if (!isAsync) { this.apiCompositeStart(); this.apiCompositeReplace(codes); this.apiCompositeEnd(); } else { AscFonts.FontPickerByCharacter.loadFonts(this, function () { this.apiCompositeStart(); this.apiCompositeReplace(codes); this.apiCompositeEnd(); this.setReadOnly(false); }); this.setReadOnly(true); return false; } }, onKeyDown : function(e) { if (this.Api.isLongAction()) { AscCommon.stopEvent(e); return false; } if (this.isInputHelpersPresent) { switch (e.keyCode) { case 9: // tab case 13: // enter case 38: // top case 40: // bottom case 33: // pageup case 34: // pagedown case 35: // end case 36: // home case 27: // escape { window.g_asc_plugins.onPluginEvent2("onKeyDown", { "keyCode" : e.keyCode }, this.isInputHelpers); AscCommon.stopEvent(e); return false; } case 32: { // send, but not prevent //window.g_asc_plugins.onPluginEvent2("onKeyDown", { "keyCode" : e.keyCode }, this.isInputHelpers); //теперь пробел - на keyPress - и добавится там //this.keyPressInput += " "; if (window.g_asc_plugins) window.g_asc_plugins.onPluginEvent("onInputHelperInput", { "text" : this.keyPressInput }); } default: break; } } else if (32 == e.keyCode) { //теперь пробел - на keyPress - и добавится там //this.keyPressInput += " "; } if (this.isSystem && this.isShow) { // нужно проверить на enter // вся остальная обработка - в текстбоксе if (e.keyCode == 13) { var text = this.getAreaValue(); var codes = []; for (var iter = text.getUnicodeIterator(); iter.check(); iter.next()) { codes.push(iter.value()); } this.apiInputText(codes); this.clear(); this.unshow(); AscCommon.stopEvent(e); return false; } else if (e.keyCode == 27) { this.clear(); this.unshow(); AscCommon.stopEvent(e); return false; } // вся обработка - в текстбоксе return; } if (null != this.nativeFocusElement) { if (this.emulateNativeKeyDown(e)) { e.preventDefault(); return false; } } // некоторые рукописные вводы не присылают keyUp var _code = e.keyCode; if (_code != 8 && _code != 46) this.KeyDownFlag = true; AscCommon.check_KeyboardEvent(e); var arrCodes = this.Api.getAddedTextOnKeyDown(AscCommon.global_keyboardEvent); var isAsync = AscFonts.FontPickerByCharacter.checkTextLight(arrCodes, true); if (isAsync) { AscFonts.FontPickerByCharacter.loadFonts(this, function () { this.onKeyDown(e); this.onKeyUp(e); this.setReadOnly(false); }); AscCommon.stopEvent(e); this.setReadOnly(true); return false; } var ret = this.Api.onKeyDown(e); switch (e.keyCode) { case 8: // backspace { var oldKeyPressInput = this.keyPressInput; this.clear(); if (oldKeyPressInput.length > 1) { this.keyPressInput = oldKeyPressInput.substr(0, oldKeyPressInput.length - 1); if (window.g_asc_plugins) window.g_asc_plugins.onPluginEvent("onInputHelperInput", { "text" : this.keyPressInput }); } return false; } case 9: // tab case 13: // enter case 37: // left case 38: // top case 39: // right case 40: // bottom case 33: // pageup case 34: // pagedown case 35: // end case 36: // home { this.clear(); return false; } case 46: // delete case 45: // insert { if (!AscCommon.global_keyboardEvent.CtrlKey && !AscCommon.global_keyboardEvent.ShiftKey) // copy/cut/paste { // заканчиваем "непрерывный" ввод => очищаем текстбокс this.clear(); return false; } } default: break; } if (e.keyCode == 32 && AscCommon.global_keyboardEvent.CtrlKey && !AscCommon.global_keyboardEvent.ShiftKey) { if (window.g_asc_plugins) window.g_asc_plugins.onPluginEvent("onClick"); } return ret; }, onKeyPress : function(e) { if (this.Api.isLongAction() || !this.Api.asc_IsFocus() || this.Api.isViewMode) { AscCommon.stopEvent(e); return false; } if (this.isSystem) return; if (this.KeyDownFlag) this.KeyPressFlag = true; if (this.IsComposition) return; if ((e.which == 13 && e.keyCode == 13) || (e.which == 10 && e.keyCode == 10)) { AscCommon.stopEvent(e); return false; } var c = e.which || e.keyCode; var isAsync = (c >= 0x20) ? AscFonts.FontPickerByCharacter.checkTextLight([c], true) : false; if (isAsync) { AscFonts.FontPickerByCharacter.loadFonts(this, function () { this.apiCompositeStart(); this.apiCompositeReplace([c]); this.apiCompositeEnd(); this.setReadOnly(false); }); AscCommon.stopEvent(e); this.setReadOnly(true); return false; } if (this.isKeyPressOnUp) { var isSaveCode = true; switch (e.which) { case 46: // delete { isSaveCode = false; break; } default: break; } if (isSaveCode) { if (this.isKeyPressOnUpStackedMode) { this.keyPressOnUpCodes.push({ which: e.which, charCode: e.charCode, keyCode: e.keyCode, shiftKey: e.shiftKey, ctrlKey: e.ctrlKey, metaKey: e.metaKey, altKey: e.altKey, preventDefault: function () { } }); } return; } } var ret = this.Api.onKeyPress(e); if (e.key === "Delete" || e.code === "Delete") { switch (e.which) { case 46: // delete { AscCommon.stopEvent(e); this.clear(); return false; } default: break; } } this.keyPressInput += String.fromCharCode(e.which); if (window.g_asc_plugins) window.g_asc_plugins.onPluginEvent("onInputHelperInput", { "text" : this.keyPressInput }); AscCommon.stopEvent(e); return ret; }, onKeyUp : function(e) { if (this.Api.isLongAction()) { AscCommon.stopEvent(e); return false; } if (this.isSystem && this.isShow) return; if (this.isKeyPressOnUp && this.keyPressOnUpCodes.length > 0) { this.isKeyPressOnUp = false; for (var i = 0; i < this.keyPressOnUpCodes.length; i++) { this.onKeyPress(this.keyPressOnUpCodes[i]); } this.isKeyPressOnUp = true; this.keyPressOnUpCodes = []; } this.KeyDownFlag = false; this.KeyPressFlag = false; AscCommon.global_keyboardEvent.Up(); this.Api.onKeyUp(e); }, getAreaPos : function() { var _offset = 0; if (this.TextArea_Not_ContentEditableDiv) { _offset = this.HtmlArea.selectionEnd; } else { var sel = window.getSelection(); if (sel.rangeCount > 0) { var range = sel.getRangeAt(0); _offset = range.endOffset; } } return _offset; }, checkTargetPosition : function(isCorrect) { var _offset = this.getAreaPos(); if (false !== isCorrect) { var _value = this.getAreaValue(); _offset -= (_value.length - this.compositionValue.length); } if (!this.IsLockTargetMode) { // никакого смысла прыгать курсором туда-сюда if (_offset == 0 && this.compositionValue.length == 1) _offset = 1; } this.Api.Set_CursorPosInCompositeText(_offset); this.unlockTarget(); }, lockTarget : function() { if (!this.IsLockTargetMode) return; if (-1 != this.LockerTargetTimer) clearTimeout(this.LockerTargetTimer); this.Api.asc_LockTargetUpdate(true); var oThis = this; this.LockerTargetTimer = setTimeout(function() { oThis.unlockTarget(); }, 1000); }, unlockTarget : function() { if (!this.IsLockTargetMode) return; if (-1 != this.LockerTargetTimer) clearTimeout(this.LockerTargetTimer); this.LockerTargetTimer = -1; this.Api.asc_LockTargetUpdate(false); }, clearLastCompositeText : function() { this.LastReplaceText = []; this.IsLastReplaceFlag = false; }, apiCompositeStart : function() { }, apiCompositeReplace : function(_value) { if (this.Api.isLongAction()) return false; if (!this.ApiIsComposition) { this.Api.Begin_CompositeInput(); this.clearLastCompositeText(); } this.ApiIsComposition = true; if (this.IsLastReplaceFlag) { // check _value == this.LastReplaceText if (_value.length == this.LastReplaceText.length) { var isEqual = true; for (var nC = 0; nC < _value.length; nC++) { if (_value[nC] != this.LastReplaceText[nC]) { isEqual = false; break; } } if (isEqual) return; // не посылаем одинаковые замены! } } this.Api.Replace_CompositeText(_value); if (window.g_asc_plugins) { this.keyPressInput = String.fromCodePoint.apply(this, _value); window.g_asc_plugins.onPluginEvent("onInputHelperInput", {"text": this.keyPressInput}); } this.LastReplaceText = _value.slice(); this.IsLastReplaceFlag = true; }, apiCompositeEnd : function() { if (!this.ApiIsComposition) return; this.ApiIsComposition = false; this.Api.End_CompositeInput(); this.clearLastCompositeText(); }, onCompositionStart : function(e) { if (this.isSystem) return; this.IsComposition = true; this.keyPressOnUpCodes = []; }, onCompositionUpdate : function(e) { if (this.isSystem) return; this.IsComposition = true; this.keyPressOnUpCodes = []; this.onInput(e, true); }, onCompositionEnd : function(e) { if (this.isSystem) return; this.IsComposition = false; this.onInput(e, true); }, setInterfaceEnableKeyEvents : function(value) { this.InterfaceEnableKeyEvents = value; if (true == this.InterfaceEnableKeyEvents) { if (document.activeElement) { var _id = document.activeElement.id; if (_id == "area_id" || (window.g_asc_plugins && window.g_asc_plugins.checkRunnedFrameId(_id))) return; } focusHtmlElement(this.HtmlArea); } }, externalEndCompositeInput : function() { this.clear(); }, externalChangeFocus : function() { if (!this.IsComposition) return false; setTimeout(function() { window['AscCommon'].g_inputContext.clear(); }, 10); return true; }, isCompositionProcess : function() { return this.IsComposition; }, preventVirtualKeyboard : function(e) { if (this.isHardCheckKeyboard) return; //AscCommon.stopEvent(e); if (AscCommon.AscBrowser.isAndroid) { this.setReadOnlyWrapper(true); this.virtualKeyboardClickPrevent = true; this.virtualKeyboardClickTimeout = setTimeout(function () { window['AscCommon'].g_inputContext.setReadOnlyWrapper(false); window['AscCommon'].g_inputContext.virtualKeyboardClickTimeout = -1; }, 1); } }, enableVirtualKeyboard : function() { if (this.isHardCheckKeyboard) return; if (AscCommon.AscBrowser.isAndroid) { if (-1 != this.virtualKeyboardClickTimeout) { clearTimeout(this.virtualKeyboardClickTimeout); this.virtualKeyboardClickTimeout = -1; } this.setReadOnlyWrapper(false); this.virtualKeyboardClickPrevent = false; } }, preventVirtualKeyboard_Hard : function() { this.setReadOnlyWrapper(true); }, enableVirtualKeyboard_Hard : function() { this.setReadOnlyWrapper(false); } }; function _getAttirbute(_elem, _attr, _depth) { var _elemTest = _elem; for (var _level = 0; _elemTest && (_level < _depth); ++_level, _elemTest = _elemTest.parentNode) { var _res = _elemTest.getAttribute ? _elemTest.getAttribute(_attr) : null; if (null != _res) return _res; } return null; } function _getElementKeyboardDown(_elem, _depth) { var _elemTest = _elem; for (var _level = 0; _elemTest && (_level < _depth); ++_level, _elemTest = _elemTest.parentNode) { var _res = _elemTest.getAttribute ? _elemTest.getAttribute("oo_editor_keyboard") : null; if (null != _res) return _elemTest; } return null; } function _getDefaultKeyboardInput(_elem, _depth) { var _elemTest = _elem; for (var _level = 0; _elemTest && (_level < _depth); ++_level, _elemTest = _elemTest.parentNode) { var _name = " " + _elemTest.className + " "; if (_name.indexOf(" dropdown-menu" ) > -1 || _name.indexOf(" dropdown-toggle ") > -1 || _name.indexOf(" dropdown-submenu ") > -1 || _name.indexOf(" canfocused ") > -1) { return "true"; } } return null; } window['AscCommon'] = window['AscCommon'] || {}; window['AscCommon'].CTextInput = CTextInput; window['AscCommon'].InitBrowserInputContext = function(api, target_id, parent_id) { if (window['AscCommon'].g_inputContext) return; window['AscCommon'].g_inputContext = new CTextInput(api); window['AscCommon'].g_inputContext.init(target_id, parent_id); window['AscCommon'].g_clipboardBase.Init(api); window['AscCommon'].g_clipboardBase.inputContext = window['AscCommon'].g_inputContext; if (window['AscCommon'].TextBoxInputMode === true) { window['AscCommon'].g_inputContext.systemInputEnable(true); } //window["SetInputDebugMode"](); document.addEventListener("focus", function(e) { var t = window['AscCommon'].g_inputContext; var _oldNativeFE = t.nativeFocusElement; t.nativeFocusElement = e.target; if (t.IsComposition) { t.apiCompositeEnd(); t.externalEndCompositeInput(); } if (!t.isSystem && !t.isNoClearOnFocus) t.clear(true); t.isNoClearOnFocus = false; t.Api.isBlurEditor = false; var _nativeFocusElementNoRemoveOnElementFocus = t.nativeFocusElementNoRemoveOnElementFocus; t.nativeFocusElementNoRemoveOnElementFocus = false; if (t.InterfaceEnableKeyEvents == false) { t.nativeFocusElement = null; return; } if (t.nativeFocusElement && (t.nativeFocusElement.id == t.HtmlArea.id)) { t.Api.asc_enableKeyEvents(true, true); if (_nativeFocusElementNoRemoveOnElementFocus) t.nativeFocusElement = _oldNativeFE; else t.nativeFocusElement = null; return; } if (t.nativeFocusElement && (t.nativeFocusElement.id == window['AscCommon'].g_clipboardBase.CommonDivId)) { t.nativeFocusElement = null; return; } t.nativeFocusElementNoRemoveOnElementFocus = false; var _isElementEditable = false; if (t.nativeFocusElement) { // detect _isElementEditable var _name = t.nativeFocusElement.nodeName; if (_name) _name = _name.toUpperCase(); if ("INPUT" == _name || "TEXTAREA" == _name) _isElementEditable = true; else if ("DIV" == _name) { if (t.nativeFocusElement.getAttribute("contenteditable") == "true") _isElementEditable = true; } } if ("IFRAME" == _name) { // перехват клавиатуры t.Api.asc_enableKeyEvents(false, true); t.nativeFocusElement = null; return; } // перехватывает ли элемент ввод var _oo_editor_input = _getAttirbute(t.nativeFocusElement, "oo_editor_input", 3); // нужно ли прокидывать нажатие клавиш элементу (ТОЛЬКО keyDown) var _oo_editor_keyboard = _getAttirbute(t.nativeFocusElement, "oo_editor_keyboard", 3); if (!_oo_editor_input && !_oo_editor_keyboard) _oo_editor_input = _getDefaultKeyboardInput(t.nativeFocusElement, 3); if (_oo_editor_keyboard == "true") _oo_editor_input = undefined; if (_oo_editor_input == "true") { // перехват клавиатуры t.Api.asc_enableKeyEvents(false, true); t.nativeFocusElement = null; return; } if (_isElementEditable && (_oo_editor_input != "false")) { // перехват клавиатуры t.Api.asc_enableKeyEvents(false, true); t.nativeFocusElement = null; return; } // итак, ввод у нас. теперь определяем, нужна ли клавиатура элементу if (_oo_editor_keyboard != "true") t.nativeFocusElement = null; var _elem = t.nativeFocusElement; t.nativeFocusElementNoRemoveOnElementFocus = true; // ie focus async AscCommon.AscBrowser.isMozilla ? setTimeout(function(){ focusHtmlElement(t.HtmlArea); }, 0) : focusHtmlElement(t.HtmlArea); t.nativeFocusElement = _elem; t.Api.asc_enableKeyEvents(true, true); }, true); // send focus if (!api.isMobileVersion && !api.isEmbedVersion) focusHtmlElement(window['AscCommon'].g_inputContext.HtmlArea); }; function focusHtmlElement(element) { element.focus(); /* var api = window['AscCommon'].g_inputContext.Api; if (api.isMobileVersion) element.focus(); else element.focus({ "preventScroll" : true }); */ }; window["SetInputDebugMode"] = function() { if (!window['AscCommon'].g_inputContext) return; window['AscCommon'].g_inputContext.debugInputEnable(true); window['AscCommon'].g_inputContext.show(); }; })(window);