big-moving.ru/api/soft/ajaxorg/lib/ace/layer/font_metrics.js

239 lines
8.3 KiB
JavaScript
Raw Normal View History

2022-06-24 15:29:23 +05:00
/* ***** 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) {
var oop = require("../lib/oop");
var dom = require("../lib/dom");
var lang = require("../lib/lang");
var event = require("../lib/event");
var useragent = require("../lib/useragent");
var EventEmitter = require("../lib/event_emitter").EventEmitter;
var CHAR_COUNT = 256;
var USE_OBSERVER = typeof ResizeObserver == "function";
var L = 200;
var FontMetrics = exports.FontMetrics = function(parentEl) {
this.el = dom.createElement("div");
this.$setMeasureNodeStyles(this.el.style, true);
this.$main = dom.createElement("div");
this.$setMeasureNodeStyles(this.$main.style);
this.$measureNode = dom.createElement("div");
this.$setMeasureNodeStyles(this.$measureNode.style);
this.el.appendChild(this.$main);
this.el.appendChild(this.$measureNode);
parentEl.appendChild(this.el);
this.$measureNode.innerHTML = lang.stringRepeat("X", CHAR_COUNT);
this.$characterSize = {width: 0, height: 0};
if (USE_OBSERVER)
this.$addObserver();
else
this.checkForSizeChanges();
};
(function() {
oop.implement(this, EventEmitter);
this.$characterSize = {width: 0, height: 0};
this.$setMeasureNodeStyles = function(style, isRoot) {
style.width = style.height = "auto";
style.left = style.top = "0px";
style.visibility = "hidden";
style.position = "absolute";
style.whiteSpace = "pre";
if (useragent.isIE < 8) {
style["font-family"] = "inherit";
} else {
style.font = "inherit";
}
style.overflow = isRoot ? "hidden" : "visible";
};
this.checkForSizeChanges = function(size) {
if (size === undefined)
size = this.$measureSizes();
if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) {
this.$measureNode.style.fontWeight = "bold";
var boldSize = this.$measureSizes();
this.$measureNode.style.fontWeight = "";
this.$characterSize = size;
this.charSizes = Object.create(null);
this.allowBoldFonts = boldSize && boldSize.width === size.width && boldSize.height === size.height;
this._emit("changeCharacterSize", {data: size});
}
};
this.$addObserver = function() {
var self = this;
this.$observer = new window.ResizeObserver(function(e) {
var rect = e[0].contentRect;
self.checkForSizeChanges({
height: rect.height,
width: rect.width / CHAR_COUNT
});
});
this.$observer.observe(this.$measureNode);
};
this.$pollSizeChanges = function() {
if (this.$pollSizeChangesTimer || this.$observer)
return this.$pollSizeChangesTimer;
var self = this;
return this.$pollSizeChangesTimer = event.onIdle(function cb() {
self.checkForSizeChanges();
event.onIdle(cb, 500);
}, 500);
};
this.setPolling = function(val) {
if (val) {
this.$pollSizeChanges();
} else if (this.$pollSizeChangesTimer) {
clearInterval(this.$pollSizeChangesTimer);
this.$pollSizeChangesTimer = 0;
}
};
this.$measureSizes = function(node) {
var size = {
height: (node || this.$measureNode).clientHeight,
width: (node || this.$measureNode).clientWidth / CHAR_COUNT
};
// Size and width can be null if the editor is not visible or
// detached from the document
if (size.width === 0 || size.height === 0)
return null;
return size;
};
this.$measureCharWidth = function(ch) {
this.$main.innerHTML = lang.stringRepeat(ch, CHAR_COUNT);
var rect = this.$main.getBoundingClientRect();
return rect.width / CHAR_COUNT;
};
this.getCharacterWidth = function(ch) {
var w = this.charSizes[ch];
if (w === undefined) {
w = this.charSizes[ch] = this.$measureCharWidth(ch) / this.$characterSize.width;
}
return w;
};
this.destroy = function() {
clearInterval(this.$pollSizeChangesTimer);
if (this.$observer)
this.$observer.disconnect();
if (this.el && this.el.parentNode)
this.el.parentNode.removeChild(this.el);
};
this.$getZoom = function getZoom(element) {
if (!element) return 1;
return (window.getComputedStyle(element).zoom || 1) * getZoom(element.parentElement);
};
this.$initTransformMeasureNodes = function() {
var t = function(t, l) {
return ["div", {
style: "position: absolute;top:" + t + "px;left:" + l + "px;"
}];
};
this.els = dom.buildDom([t(0, 0), t(L, 0), t(0, L), t(L, L)], this.el);
};
// general transforms from element coordinates x to screen coordinates u have the form
// | m1[0] m2[0] t[0] | | x | | u |
// | m1[1] m2[1] t[1] | . | y | == k | v |
// | h[0] h[1] 1 | | 1 | | 1 |
// this function finds the coeeficients of the matrix using positions of four points
//
this.transformCoordinates = function(clientPos, elPos) {
if (clientPos) {
var zoom = this.$getZoom(this.el);
clientPos = mul(1 / zoom, clientPos);
}
function solve(l1, l2, r) {
var det = l1[1] * l2[0] - l1[0] * l2[1];
return [
(-l2[1] * r[0] + l2[0] * r[1]) / det,
(+l1[1] * r[0] - l1[0] * r[1]) / det
];
}
function sub(a, b) { return [a[0] - b[0], a[1] - b[1]]; }
function add(a, b) { return [a[0] + b[0], a[1] + b[1]]; }
function mul(a, b) { return [a * b[0], a * b[1]]; }
if (!this.els)
this.$initTransformMeasureNodes();
function p(el) {
var r = el.getBoundingClientRect();
return [r.left, r.top];
}
var a = p(this.els[0]);
var b = p(this.els[1]);
var c = p(this.els[2]);
var d = p(this.els[3]);
var h = solve(sub(d, b), sub(d, c), sub(add(b, c), add(d, a)));
var m1 = mul(1 + h[0], sub(b, a));
var m2 = mul(1 + h[1], sub(c, a));
if (elPos) {
var x = elPos;
var k = h[0] * x[0] / L + h[1] * x[1] / L + 1;
var ut = add(mul(x[0], m1), mul(x[1], m2));
return add(mul(1 / k / L, ut), a);
}
var u = sub(clientPos, a);
var f = solve(sub(m1, mul(h[0], u)), sub(m2, mul(h[1], u)), u);
return mul(L, f);
};
}).call(FontMetrics.prototype);
});