import { Assert, UnitTest } from '@ephox/bedrock-client';
import { PlatformDetection } from '@ephox/sand';
import * as Compare from 'ephox/sugar/api/dom/Compare';
import * as Hierarchy from 'ephox/sugar/api/dom/Hierarchy';
import * as Insert from 'ephox/sugar/api/dom/Insert';
import * as Remove from 'ephox/sugar/api/dom/Remove';
import * as SugarBody from 'ephox/sugar/api/node/SugarBody';
import { SugarElement } from 'ephox/sugar/api/node/SugarElement';
import * as Attribute from 'ephox/sugar/api/properties/Attribute';
import * as Class from 'ephox/sugar/api/properties/Class';
import * as Html from 'ephox/sugar/api/properties/Html';
import { SimSelection } from 'ephox/sugar/api/selection/SimSelection';
import { Situ } from 'ephox/sugar/api/selection/Situ';
import * as WindowSelection from 'ephox/sugar/api/selection/WindowSelection';
interface VariantRange {
start: number[];
soffset: number;
finish: number[];
foffset: number;
}
interface Variants {
fallback: VariantRange;
[key: string]: VariantRange;
}
UnitTest.test('WindowSelectionTest', () => {
const container = SugarElement.fromTag('div');
Class.add(container, 'window-selection-test');
Attribute.set(container, 'contenteditable', 'true');
const body = SugarBody.body();
Insert.append(body, container);
Html.set(container, '
This world is not what I
wanted
And even more
');
const find = (path: number[]) => Hierarchy.follow(container, path).getOrDie('invalid path');
const detection = PlatformDetection.detect();
const detector = (variants: Variants): VariantRange => {
if (detection.browser.isFirefox() && variants.firefox !== undefined) {
return variants.firefox;
} else if (detection.browser.isSafari() && variants.safari !== undefined) {
return variants.safari;
} else if (detection.browser.isChromium() && variants.chromium !== undefined) {
return variants.chromium;
} else {
return variants.fallback;
}
};
const checkSelection = (label: string, variants: Variants, start: Situ, finish: Situ) => {
const expected = detector(variants);
WindowSelection.setRelative(window, start, finish);
const actual = WindowSelection.getExact(window).getOrDie('No selection after selection');
const expStart = find(expected.start);
const expFinish = find(expected.finish);
Assert.eq('Start element different', true, Compare.eq(expStart, actual.start));
Assert.eq('Finish element different', true, Compare.eq(expFinish, actual.finish));
Assert.eq('', expected.soffset, actual.soffset);
Assert.eq('', expected.foffset, actual.foffset);
};
const checkUniCodeSelection = (content: string) => {
Remove.empty(container);
Html.set(container, content);
return checkSelection;
};
const checkStringAt = (label: string, expectedStr: string, start: Situ, finish: Situ) => {
// dont need to set a selection range, just extract the Situ.on() element/offset pair
const actual = WindowSelection.getAsString(window, SimSelection.relative(start, finish));
Assert.eq('Actual was not expected [' + expectedStr + '|' + actual + ']', expectedStr, actual);
};
checkSelection(
'LTR selection (o)',
{
// 'This w[o]rld is not what I
wanted
And even more
';
fallback: {
start: [ 0, 1, 0 ],
soffset: 'w'.length,
finish: [ 0, 1, 0 ],
foffset: 'wo'.length
}
},
Situ.on(find( [ 0, 1, 0 ]), 'w'.length),
Situ.on(find( [ 0, 1, 0 ]), 'wo'.length)
);
checkSelection(
'RTL selection: (o)',
{
// 'This w]o[rld is not what I
wanted
And even more
';
fallback: {
start: [ 0, 1, 0 ],
soffset: 'wo'.length,
finish: [ 0, 1, 0 ],
foffset: 'w'.length
}
},
Situ.on(find( [ 0, 1, 0 ]), 'wo'.length),
Situ.on(find( [ 0, 1, 0 ]), 'w'.length)
);
checkSelection(
'RTL selection (orld)',
{
// 'This w]orld[ is not what I
wanted
And even more
';
firefox: {
start: [ 0 ],
soffset: 2,
finish: [ 0, 1, 0 ],
foffset: 'w'.length
},
chromium: {
start: [ 0 ],
soffset: 2,
finish: [ 0, 1, 0 ],
foffset: 'w'.length
},
safari: {
start: [ 0 ],
soffset: 2,
finish: [ 0, 1, 0 ],
foffset: 'w'.length
},
fallback: {
start: [ 0, 1, 0 ],
soffset: 'world'.length,
finish: [ 0, 1, 0 ],
foffset: 'w'.length
}
},
Situ.before(find( [ 0, 2 ])),
Situ.on(find( [ 0, 1, 0 ]), 'w'.length)
);
checkSelection(
'LTR selection (This world is not what I wanted)',
{
// '[This world is not what I
wanted]
And even more
';
fallback: {
start: [ 0 ],
soffset: 0,
finish: [ 0 ],
foffset: 7
}
},
Situ.on(find( [ 0 ]), 0),
Situ.on(find( [ 0 ]), 7)
);
checkSelection(
'RTL SimSelection (This world is not what I wanted)',
{
// ']This world is not what I
wanted[
And even more
';
fallback: {
start: [ 0 ],
soffset: 7,
finish: [ 0 ],
foffset: 0
},
chromium: {
start: [ 0 ],
soffset: 7,
finish: [ 0 ],
foffset: 0
}
},
Situ.on(find( [ 0 ]), 7),
Situ.on(find( [ 0 ]), 0)
);
checkSelection(
'LTR selection (t I)',
{
fallback: {
start: [ 0, 3, 1 ],
soffset: 1,
finish: [ 0 ],
foffset: 6
}
},
Situ.after(find( [ 0, 3, 1, 0 ]) ),
Situ.before(find( [ 0, 6 ]) )
);
checkSelection(
'RTL SimSelection (t I)',
{
// 'This world is not wha]t I
[wanted
And even more
';
fallback: {
finish: [ 0, 3, 2 ],
foffset: ''.length,
start: [ 0 ],
soffset: 6
},
firefox: {
finish: [ 0, 3, 1 ],
foffset: 1,
start: [ 0 ],
soffset: 6
},
chromium: {
finish: [ 0, 3, 1 ],
foffset: 1,
start: [ 0 ],
soffset: 6
},
safari: {
finish: [ 0, 3, 1 ],
foffset: 1,
start: [ 0 ],
soffset: 6
},
spartan: {
finish: [ 0, 3, 1 ],
foffset: 1,
start: [ 0 ],
soffset: 6
}
},
Situ.before(find( [ 0, 6 ]) ),
Situ.after(find( [ 0, 3, 1, 0 ]) )
);
checkStringAt(
'LTR stringAt (This world is not what I)',
// checkSelection above has error in what it thinks 0/7 is:
// expects: '[This world is not what I
wanted]
And even more
';
// but actual: '[This world is not what I
]
wanted
And even more
';
'This world is not what I',
Situ.on(find( [ 0 ]), 0),
Situ.on(find( [ 0 ]), 7)
);
checkStringAt(
'RTL SimSelection (This world is not what I)',
'This world is not what I',
Situ.on(find( [ 0 ]), 7),
Situ.on(find( [ 0 ]), 0)
);
// Test that proves safari will always normalise a selection to the end leaf
// when we set the selection to the span, then get selection, all browsers will return the span
// safari will return the textnode inside the span if one exists.
checkUniCodeSelection('\uFEFF')(
'TBIO-3883: Unicode position',
{
fallback: {
start: [ 0 ],
soffset: 0,
finish: [ 0 ],
foffset: 0
}
},
Situ.on(find( [ 0 ]), 0 ),
Situ.on(find( [ 0 ]), 0 )
);
checkUniCodeSelection('^')(
'TBIO-3883: Any Character',
{
fallback: {
start: [ 0 ],
soffset: 0,
finish: [ 0 ],
foffset: 0
}
},
Situ.on(find( [ 0 ]), 0 ),
Situ.on(find( [ 0 ]), 0 )
);
Remove.remove(container);
});