33 changed files with 1293 additions and 798 deletions
@ -0,0 +1,445 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { FormattedData, MapProviders, ReplaceInfo } from '@home/components/widget/lib/maps/map-models'; |
||||
|
import { |
||||
|
createLabelFromDatasource, |
||||
|
hashCode, |
||||
|
isDefined, |
||||
|
isDefinedAndNotNull, isFunction, |
||||
|
isNumber, |
||||
|
isUndefined, |
||||
|
padValue |
||||
|
} from '@core/utils'; |
||||
|
import { Observable, Observer, of } from 'rxjs'; |
||||
|
import { map } from 'rxjs/operators'; |
||||
|
import { Datasource, DatasourceData } from '@shared/models/widget.models'; |
||||
|
import _ from 'lodash'; |
||||
|
import { mapProviderSchema, providerSets } from '@home/components/widget/lib/maps/schemes'; |
||||
|
import { addCondition, mergeSchemes } from '@core/schema-utils'; |
||||
|
|
||||
|
export function getProviderSchema(mapProvider: MapProviders, ignoreImageMap = false) { |
||||
|
const providerSchema = _.cloneDeep(mapProviderSchema); |
||||
|
if (mapProvider) { |
||||
|
providerSchema.schema.properties.provider.default = mapProvider; |
||||
|
} |
||||
|
if (ignoreImageMap) { |
||||
|
providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map'); |
||||
|
} |
||||
|
return mergeSchemes([providerSchema, |
||||
|
...Object.keys(providerSets)?.map( |
||||
|
(key: string) => { |
||||
|
const setting = providerSets[key]; |
||||
|
return addCondition(setting?.schema, `model.provider === '${setting.name}'`); |
||||
|
})]); |
||||
|
} |
||||
|
|
||||
|
export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number { |
||||
|
return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); |
||||
|
} |
||||
|
|
||||
|
export function interpolateOnLineSegment( |
||||
|
pointA: FormattedData, |
||||
|
pointB: FormattedData, |
||||
|
latKeyName: string, |
||||
|
lngKeyName: string, |
||||
|
ratio: number |
||||
|
): { [key: string]: number } { |
||||
|
return { |
||||
|
[latKeyName]: (pointA[latKeyName] + (pointB[latKeyName] - pointA[latKeyName]) * ratio), |
||||
|
[lngKeyName]: (pointA[lngKeyName] + (pointB[lngKeyName] - pointA[lngKeyName]) * ratio) |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export function findAngle(startPoint: FormattedData, endPoint: FormattedData, latKeyName: string, lngKeyName: string): number { |
||||
|
if (isUndefined(startPoint) || isUndefined(endPoint)) { |
||||
|
return 0; |
||||
|
} |
||||
|
let angle = -Math.atan2(endPoint[latKeyName] - startPoint[latKeyName], endPoint[lngKeyName] - startPoint[lngKeyName]); |
||||
|
angle = angle * 180 / Math.PI; |
||||
|
return parseInt(angle.toFixed(2), 10); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export function getDefCenterPosition(position) { |
||||
|
if (typeof (position) === 'string') { |
||||
|
return position.split(','); |
||||
|
} |
||||
|
if (typeof (position) === 'object') { |
||||
|
return position; |
||||
|
} |
||||
|
return [0, 0]; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const imageAspectMap = {}; |
||||
|
|
||||
|
function imageLoader(imageUrl: string): Observable<HTMLImageElement> { |
||||
|
return new Observable((observer: Observer<HTMLImageElement>) => { |
||||
|
const image = document.createElement('img'); // support IE
|
||||
|
image.style.position = 'absolute'; |
||||
|
image.style.left = '-99999px'; |
||||
|
image.style.top = '-99999px'; |
||||
|
image.onload = () => { |
||||
|
observer.next(image); |
||||
|
document.body.removeChild(image); |
||||
|
observer.complete(); |
||||
|
}; |
||||
|
image.onerror = err => { |
||||
|
observer.error(err); |
||||
|
document.body.removeChild(image); |
||||
|
observer.complete(); |
||||
|
}; |
||||
|
document.body.appendChild(image); |
||||
|
image.src = imageUrl; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function aspectCache(imageUrl: string): Observable<number> { |
||||
|
if (imageUrl?.length) { |
||||
|
const hash = hashCode(imageUrl); |
||||
|
let aspect = imageAspectMap[hash]; |
||||
|
if (aspect) { |
||||
|
return of(aspect); |
||||
|
} |
||||
|
return imageLoader(imageUrl).pipe(map(image => { |
||||
|
aspect = image.width / image.height; |
||||
|
imageAspectMap[hash] = aspect; |
||||
|
return aspect; |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export type TranslateFunc = (key: string, defaultTranslation?: string) => string; |
||||
|
|
||||
|
const varsRegex = /\${([^}]*)}/g; |
||||
|
const linkActionRegex = /<link-act name=['"]([^['"]*)['"]>([^<]*)<\/link-act>/g; |
||||
|
const buttonActionRegex = /<button-act name=['"]([^['"]*)['"]>([^<]*)<\/button-act>/g; |
||||
|
|
||||
|
function createLinkElement(actionName: string, actionText: string): string { |
||||
|
return `<a href="javascript:void(0);" class="tb-custom-action" data-action-name=${actionName}>${actionText}</a>`; |
||||
|
} |
||||
|
|
||||
|
function createButtonElement(actionName: string, actionText: string) { |
||||
|
return `<button mat-button class="tb-custom-action" data-action-name=${actionName}>${actionText}</button>`; |
||||
|
} |
||||
|
|
||||
|
function parseTemplate(template: string, data: { $datasource?: Datasource, [key: string]: any }, |
||||
|
translateFn?: TranslateFunc) { |
||||
|
let res = ''; |
||||
|
try { |
||||
|
if (translateFn) { |
||||
|
template = translateFn(template); |
||||
|
} |
||||
|
template = createLabelFromDatasource(data.$datasource, template); |
||||
|
|
||||
|
let match = /\${([^}]*)}/g.exec(template); |
||||
|
while (match !== null) { |
||||
|
const variable = match[0]; |
||||
|
let label = match[1]; |
||||
|
let valDec = 2; |
||||
|
const splitValues = label.split(':'); |
||||
|
if (splitValues.length > 1) { |
||||
|
label = splitValues[0]; |
||||
|
valDec = parseFloat(splitValues[1]); |
||||
|
} |
||||
|
|
||||
|
if (label.startsWith('#')) { |
||||
|
const keyIndexStr = label.substring(1); |
||||
|
const n = Math.floor(Number(keyIndexStr)); |
||||
|
if (String(n) === keyIndexStr && n >= 0) { |
||||
|
label = data.$datasource.dataKeys[n].label; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const value = data[label] || ''; |
||||
|
let textValue: string; |
||||
|
if (isNumber(value)) { |
||||
|
textValue = padValue(value, valDec); |
||||
|
} else { |
||||
|
textValue = value; |
||||
|
} |
||||
|
template = template.replace(variable, textValue); |
||||
|
match = /\${([^}]*)}/g.exec(template); |
||||
|
} |
||||
|
|
||||
|
let actionTags: string; |
||||
|
let actionText: string; |
||||
|
let actionName: string; |
||||
|
let action: string; |
||||
|
|
||||
|
match = linkActionRegex.exec(template); |
||||
|
while (match !== null) { |
||||
|
[actionTags, actionName, actionText] = match; |
||||
|
action = createLinkElement(actionName, actionText); |
||||
|
template = template.replace(actionTags, action); |
||||
|
match = linkActionRegex.exec(template); |
||||
|
} |
||||
|
|
||||
|
match = buttonActionRegex.exec(template); |
||||
|
while (match !== null) { |
||||
|
[actionTags, actionName, actionText] = match; |
||||
|
action = createButtonElement(actionName, actionText); |
||||
|
template = template.replace(actionTags, action); |
||||
|
match = buttonActionRegex.exec(template); |
||||
|
} |
||||
|
|
||||
|
// const compiled = _.template(template);
|
||||
|
// res = compiled(data);
|
||||
|
res = template; |
||||
|
} catch (ex) { |
||||
|
console.log(ex, template); |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
export function processPattern(template: string, data: { $datasource?: Datasource, [key: string]: any }): Array<ReplaceInfo> { |
||||
|
const replaceInfo = []; |
||||
|
try { |
||||
|
const reg = /\${([^}]*)}/g; |
||||
|
let match = reg.exec(template); |
||||
|
while (match !== null) { |
||||
|
const variableInfo: ReplaceInfo = { |
||||
|
dataKeyName: '', |
||||
|
valDec: 2, |
||||
|
variable: '' |
||||
|
}; |
||||
|
const variable = match[0]; |
||||
|
let label = match[1]; |
||||
|
let valDec = 2; |
||||
|
const splitValues = label.split(':'); |
||||
|
if (splitValues.length > 1) { |
||||
|
label = splitValues[0]; |
||||
|
valDec = parseFloat(splitValues[1]); |
||||
|
} |
||||
|
|
||||
|
variableInfo.variable = variable; |
||||
|
variableInfo.valDec = valDec; |
||||
|
|
||||
|
if (label.startsWith('#')) { |
||||
|
const keyIndexStr = label.substring(1); |
||||
|
const n = Math.floor(Number(keyIndexStr)); |
||||
|
if (String(n) === keyIndexStr && n >= 0) { |
||||
|
variableInfo.dataKeyName = data.$datasource.dataKeys[n].label; |
||||
|
} |
||||
|
} else { |
||||
|
variableInfo.dataKeyName = label; |
||||
|
} |
||||
|
replaceInfo.push(variableInfo); |
||||
|
|
||||
|
match = reg.exec(template); |
||||
|
} |
||||
|
} catch (ex) { |
||||
|
console.log(ex, template); |
||||
|
} |
||||
|
return replaceInfo; |
||||
|
} |
||||
|
|
||||
|
export function fillPattern(markerLabelText: string, replaceInfoLabelMarker: Array<ReplaceInfo>, data: FormattedData) { |
||||
|
let text = createLabelFromDatasource(data.$datasource, markerLabelText); |
||||
|
if (replaceInfoLabelMarker) { |
||||
|
for (const variableInfo of replaceInfoLabelMarker) { |
||||
|
let txtVal = ''; |
||||
|
if (variableInfo.dataKeyName && isDefinedAndNotNull(data[variableInfo.dataKeyName])) { |
||||
|
const varData = data[variableInfo.dataKeyName]; |
||||
|
if (isNumber(varData)) { |
||||
|
txtVal = padValue(varData, variableInfo.valDec); |
||||
|
} else { |
||||
|
txtVal = varData; |
||||
|
} |
||||
|
} |
||||
|
text = text.replace(variableInfo.variable, txtVal); |
||||
|
} |
||||
|
} |
||||
|
return text; |
||||
|
} |
||||
|
|
||||
|
function prepareProcessPattern(template: string, translateFn?: TranslateFunc): string { |
||||
|
if (translateFn) { |
||||
|
template = translateFn(template); |
||||
|
} |
||||
|
let actionTags: string; |
||||
|
let actionText: string; |
||||
|
let actionName: string; |
||||
|
let action: string; |
||||
|
|
||||
|
let match = linkActionRegex.exec(template); |
||||
|
while (match !== null) { |
||||
|
[actionTags, actionName, actionText] = match; |
||||
|
action = createLinkElement(actionName, actionText); |
||||
|
template = template.replace(actionTags, action); |
||||
|
match = linkActionRegex.exec(template); |
||||
|
} |
||||
|
|
||||
|
match = buttonActionRegex.exec(template); |
||||
|
while (match !== null) { |
||||
|
[actionTags, actionName, actionText] = match; |
||||
|
action = createButtonElement(actionName, actionText); |
||||
|
template = template.replace(actionTags, action); |
||||
|
match = buttonActionRegex.exec(template); |
||||
|
} |
||||
|
return template; |
||||
|
} |
||||
|
|
||||
|
export const parseWithTranslation = { |
||||
|
|
||||
|
translateFn: null, |
||||
|
|
||||
|
translate(key: string, defaultTranslation?: string): string { |
||||
|
if (this.translateFn) { |
||||
|
return this.translateFn(key, defaultTranslation); |
||||
|
} else { |
||||
|
throw console.error('Translate not assigned'); |
||||
|
} |
||||
|
}, |
||||
|
parseTemplate(template: string, data: object, forceTranslate = false): string { |
||||
|
return parseTemplate(forceTranslate ? this.translate(template) : template, data, this.translate.bind(this)); |
||||
|
}, |
||||
|
prepareProcessPattern(template: string, forceTranslate = false): string { |
||||
|
return prepareProcessPattern(forceTranslate ? this.translate(template) : template, this.translate.bind(this)); |
||||
|
}, |
||||
|
setTranslate(translateFn: TranslateFunc) { |
||||
|
this.translateFn = translateFn; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export function parseData(input: DatasourceData[]): FormattedData[] { |
||||
|
return _(input).groupBy(el => el?.datasource?.entityName) |
||||
|
.values().value().map((entityArray, i) => { |
||||
|
const obj: FormattedData = { |
||||
|
entityName: entityArray[0]?.datasource?.entityName, |
||||
|
entityId: entityArray[0]?.datasource?.entityId, |
||||
|
entityType: entityArray[0]?.datasource?.entityType, |
||||
|
$datasource: entityArray[0]?.datasource, |
||||
|
dsIndex: i, |
||||
|
deviceType: null |
||||
|
}; |
||||
|
entityArray.filter(el => el.data.length).forEach(el => { |
||||
|
const indexDate = el?.data?.length ? el.data.length - 1 : 0; |
||||
|
obj[el?.dataKey?.label] = el?.data[indexDate][1]; |
||||
|
obj[el?.dataKey?.label + '|ts'] = el?.data[indexDate][0]; |
||||
|
if (el?.dataKey?.label === 'type') { |
||||
|
obj.deviceType = el?.data[indexDate][1]; |
||||
|
} |
||||
|
}); |
||||
|
return obj; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export function parseArray(input: DatasourceData[]): FormattedData[][] { |
||||
|
return _(input).groupBy(el => el?.datasource?.entityName) |
||||
|
.values().value().map((entityArray) => |
||||
|
entityArray[0].data.map((el, i) => { |
||||
|
const obj: FormattedData = { |
||||
|
entityName: entityArray[0]?.datasource?.entityName, |
||||
|
entityId: entityArray[0]?.datasource?.entityId, |
||||
|
entityType: entityArray[0]?.datasource?.entityType, |
||||
|
$datasource: entityArray[0]?.datasource, |
||||
|
dsIndex: i, |
||||
|
time: el[0], |
||||
|
deviceType: null |
||||
|
}; |
||||
|
entityArray.filter(e => e.data.length && e.data[i]).forEach(entity => { |
||||
|
obj[entity?.dataKey?.label] = entity?.data[i][1]; |
||||
|
obj[entity?.dataKey?.label + '|ts'] = entity?.data[0][0]; |
||||
|
if (entity?.dataKey?.label === 'type') { |
||||
|
obj.deviceType = entity?.data[0][1]; |
||||
|
} |
||||
|
}); |
||||
|
return obj; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export function parseFunction(source: any, params: string[] = ['def']): (...args: any[]) => any { |
||||
|
let res = null; |
||||
|
if (source?.length) { |
||||
|
try { |
||||
|
res = new Function(...params, source); |
||||
|
} |
||||
|
catch (err) { |
||||
|
res = null; |
||||
|
} |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
export function safeExecute(func: (...args: any[]) => any, params = []) { |
||||
|
let res = null; |
||||
|
if (func && typeof (func) === 'function') { |
||||
|
try { |
||||
|
res = func(...params); |
||||
|
} |
||||
|
catch (err) { |
||||
|
console.log('error in external function:', err); |
||||
|
res = null; |
||||
|
} |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
export function functionValueCalculator(useFunction: boolean, func: (...args: any[]) => any, params = [], defaultValue: any) { |
||||
|
let res; |
||||
|
if (useFunction && isDefined(func) && isFunction(func)) { |
||||
|
try { |
||||
|
res = func(...params); |
||||
|
if (!isDefinedAndNotNull(res) || res === '') { |
||||
|
res = defaultValue; |
||||
|
} |
||||
|
} catch (err) { |
||||
|
res = defaultValue; |
||||
|
console.log('error in external function:', err); |
||||
|
} |
||||
|
} else { |
||||
|
res = defaultValue; |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
export function calculateNewPointCoordinate(coordinate: number, imageSize: number): number { |
||||
|
let pointCoordinate = coordinate / imageSize; |
||||
|
if (pointCoordinate < 0) { |
||||
|
pointCoordinate = 0; |
||||
|
} else if (pointCoordinate > 1) { |
||||
|
pointCoordinate = 1; |
||||
|
} |
||||
|
return pointCoordinate; |
||||
|
} |
||||
|
|
||||
|
export function createLoadingDiv(loadingText: string): JQuery<HTMLElement> { |
||||
|
return $(` |
||||
|
<div style=" |
||||
|
z-index: 12; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
flex-direction: column; |
||||
|
align-content: center; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
display: flex; |
||||
|
background: rgba(255,255,255,0.7); |
||||
|
font-size: 16px; |
||||
|
font-family: Roboto; |
||||
|
font-weight: 400; |
||||
|
text-transform: uppercase; |
||||
|
"> |
||||
|
<span>${loadingText}</span> |
||||
|
</div> |
||||
|
`);
|
||||
|
} |
||||
@ -0,0 +1,327 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Ace } from 'ace-builds'; |
||||
|
import { Observable } from 'rxjs/internal/Observable'; |
||||
|
import { forkJoin, from, of } from 'rxjs'; |
||||
|
import { map, mergeMap, tap } from 'rxjs/operators'; |
||||
|
|
||||
|
let aceDependenciesLoaded = false; |
||||
|
let aceModule: any; |
||||
|
|
||||
|
export function loadAceDependencies(): Observable<any> { |
||||
|
if (aceDependenciesLoaded) { |
||||
|
return of(null); |
||||
|
} else { |
||||
|
const aceObservables: Observable<any>[] = []; |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/ext-language_tools'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/ext-searchbox'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/mode-java'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/mode-css'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/mode-json'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/mode-javascript'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/mode-text'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/mode-markdown'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/mode-html'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/snippets/java'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/snippets/css'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/snippets/json'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/snippets/javascript'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/snippets/text'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/snippets/markdown'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/snippets/html'))); |
||||
|
aceObservables.push(from(import('ace-builds/src-noconflict/theme-github'))); |
||||
|
return forkJoin(aceObservables).pipe( |
||||
|
tap(() => { |
||||
|
aceDependenciesLoaded = true; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function getAce(): Observable<any> { |
||||
|
if (aceModule) { |
||||
|
return of(aceModule); |
||||
|
} else { |
||||
|
return from(import('ace')).pipe( |
||||
|
mergeMap((module) => { |
||||
|
return loadAceDependencies().pipe( |
||||
|
map(() => module) |
||||
|
); |
||||
|
}), |
||||
|
tap((module) => { |
||||
|
aceModule = module; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export class Range implements Ace.Range { |
||||
|
|
||||
|
public start: Ace.Point; |
||||
|
public end: Ace.Point; |
||||
|
|
||||
|
constructor(startRow: number, startColumn: number, endRow: number, endColumn: number) { |
||||
|
this.start = { |
||||
|
row: startRow, |
||||
|
column: startColumn |
||||
|
}; |
||||
|
|
||||
|
this.end = { |
||||
|
row: endRow, |
||||
|
column: endColumn |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
static fromPoints(start: Ace.Point, end: Ace.Point): Ace.Range { |
||||
|
return new Range(start.row, start.column, end.row, end.column); |
||||
|
} |
||||
|
|
||||
|
clipRows(firstRow: number, lastRow: number): Ace.Range { |
||||
|
let end: Ace.Point; |
||||
|
let start: Ace.Point; |
||||
|
if (this.end.row > lastRow) { |
||||
|
end = {row: lastRow + 1, column: 0}; |
||||
|
} else if (this.end.row < firstRow) { |
||||
|
end = {row: firstRow, column: 0}; |
||||
|
} |
||||
|
|
||||
|
if (this.start.row > lastRow) { |
||||
|
start = {row: lastRow + 1, column: 0}; |
||||
|
} else if (this.start.row < firstRow) { |
||||
|
start = {row: firstRow, column: 0}; |
||||
|
} |
||||
|
return Range.fromPoints(start || this.start, end || this.end); |
||||
|
} |
||||
|
|
||||
|
clone(): Ace.Range { |
||||
|
return Range.fromPoints(this.start, this.end); |
||||
|
} |
||||
|
|
||||
|
collapseRows(): Ace.Range { |
||||
|
if (this.end.column === 0) { |
||||
|
return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row - 1), 0); |
||||
|
} else { |
||||
|
return new Range(this.start.row, 0, this.end.row, 0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
compare(row: number, column: number): number { |
||||
|
if (!this.isMultiLine()) { |
||||
|
if (row === this.start.row) { |
||||
|
return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (row < this.start.row) { |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (row > this.end.row) { |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
if (this.start.row === row) { |
||||
|
return column >= this.start.column ? 0 : -1; |
||||
|
} |
||||
|
|
||||
|
if (this.end.row === row) { |
||||
|
return column <= this.end.column ? 0 : 1; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
compareEnd(row: number, column: number): number { |
||||
|
if (this.end.row === row && this.end.column === column) { |
||||
|
return 1; |
||||
|
} else { |
||||
|
return this.compare(row, column); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
compareInside(row: number, column: number): number { |
||||
|
if (this.end.row === row && this.end.column === column) { |
||||
|
return 1; |
||||
|
} else if (this.start.row === row && this.start.column === column) { |
||||
|
return -1; |
||||
|
} else { |
||||
|
return this.compare(row, column); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
comparePoint(p: Ace.Point): number { |
||||
|
return this.compare(p.row, p.column); |
||||
|
} |
||||
|
|
||||
|
compareRange(range: Ace.Range): number { |
||||
|
let cmp: number; |
||||
|
const end = range.end; |
||||
|
const start = range.start; |
||||
|
|
||||
|
cmp = this.compare(end.row, end.column); |
||||
|
if (cmp === 1) { |
||||
|
cmp = this.compare(start.row, start.column); |
||||
|
if (cmp === 1) { |
||||
|
return 2; |
||||
|
} else if (cmp === 0) { |
||||
|
return 1; |
||||
|
} else { |
||||
|
return 0; |
||||
|
} |
||||
|
} else if (cmp === -1) { |
||||
|
return -2; |
||||
|
} else { |
||||
|
cmp = this.compare(start.row, start.column); |
||||
|
if (cmp === -1) { |
||||
|
return -1; |
||||
|
} else if (cmp === 1) { |
||||
|
return 42; |
||||
|
} else { |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
compareStart(row: number, column: number): number { |
||||
|
if (this.start.row === row && this.start.column === column) { |
||||
|
return -1; |
||||
|
} else { |
||||
|
return this.compare(row, column); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
contains(row: number, column: number): boolean { |
||||
|
return this.compare(row, column) === 0; |
||||
|
} |
||||
|
|
||||
|
containsRange(range: Ace.Range): boolean { |
||||
|
return this.comparePoint(range.start) === 0 && this.comparePoint(range.end) === 0; |
||||
|
} |
||||
|
|
||||
|
extend(row: number, column: number): Ace.Range { |
||||
|
const cmp = this.compare(row, column); |
||||
|
let end: Ace.Point; |
||||
|
let start: Ace.Point; |
||||
|
if (cmp === 0) { |
||||
|
return this; |
||||
|
} else if (cmp === -1) { |
||||
|
start = {row, column}; |
||||
|
} else { |
||||
|
end = {row, column}; |
||||
|
} |
||||
|
return Range.fromPoints(start || this.start, end || this.end); |
||||
|
} |
||||
|
|
||||
|
inside(row: number, column: number): boolean { |
||||
|
if (this.compare(row, column) === 0) { |
||||
|
if (this.isEnd(row, column) || this.isStart(row, column)) { |
||||
|
return false; |
||||
|
} else { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
insideEnd(row: number, column: number): boolean { |
||||
|
if (this.compare(row, column) === 0) { |
||||
|
if (this.isStart(row, column)) { |
||||
|
return false; |
||||
|
} else { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
insideStart(row: number, column: number): boolean { |
||||
|
if (this.compare(row, column) === 0) { |
||||
|
if (this.isEnd(row, column)) { |
||||
|
return false; |
||||
|
} else { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
intersects(range: Ace.Range): boolean { |
||||
|
const cmp = this.compareRange(range); |
||||
|
return (cmp === -1 || cmp === 0 || cmp === 1); |
||||
|
} |
||||
|
|
||||
|
isEmpty(): boolean { |
||||
|
return (this.start.row === this.end.row && this.start.column === this.end.column); |
||||
|
} |
||||
|
|
||||
|
isEnd(row: number, column: number): boolean { |
||||
|
return this.end.row === row && this.end.column === column; |
||||
|
} |
||||
|
|
||||
|
isEqual(range: Ace.Range): boolean { |
||||
|
return this.start.row === range.start.row && |
||||
|
this.end.row === range.end.row && |
||||
|
this.start.column === range.start.column && |
||||
|
this.end.column === range.end.column; |
||||
|
} |
||||
|
|
||||
|
isMultiLine(): boolean { |
||||
|
return (this.start.row !== this.end.row); |
||||
|
} |
||||
|
|
||||
|
isStart(row: number, column: number): boolean { |
||||
|
return this.start.row === row && this.start.column === column; |
||||
|
} |
||||
|
|
||||
|
moveBy(row: number, column: number): void { |
||||
|
this.start.row += row; |
||||
|
this.start.column += column; |
||||
|
this.end.row += row; |
||||
|
this.end.column += column; |
||||
|
} |
||||
|
|
||||
|
setEnd(row: number, column: number): void { |
||||
|
if (typeof row === 'object') { |
||||
|
this.end.column = (row as Ace.Point).column; |
||||
|
this.end.row = (row as Ace.Point).row; |
||||
|
} else { |
||||
|
this.end.row = row; |
||||
|
this.end.column = column; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setStart(row: number, column: number): void { |
||||
|
if (typeof row === 'object') { |
||||
|
this.start.column = (row as Ace.Point).column; |
||||
|
this.start.row = (row as Ace.Point).row; |
||||
|
} else { |
||||
|
this.start.row = row; |
||||
|
this.start.column = column; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
toScreenRange(session: Ace.EditSession): Ace.Range { |
||||
|
const screenPosStart = session.documentToScreenPosition(this.start); |
||||
|
const screenPosEnd = session.documentToScreenPosition(this.end); |
||||
|
|
||||
|
return new Range( |
||||
|
screenPosStart.row, screenPosStart.column, |
||||
|
screenPosEnd.row, screenPosEnd.column |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue