123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- "use strict";
- module.metadata = {
- "stability": "stable",
- "engines": {
- "Firefox": "*"
- }
- };
- const { Ci, Cc } = require("chrome"),
- { setTimeout } = require("./timers"),
- { emit, off } = require("./event/core"),
- { Class, obscure } = require("./core/heritage"),
- { EventTarget } = require("./event/target"),
- { ns } = require("./core/namespace"),
- { when: unload } = require("./system/unload"),
- { ignoreWindow } = require('./private-browsing/utils'),
- { getTabs, getTabContentWindow, getTabForContentWindow,
- getAllTabContentWindows } = require('./tabs/utils'),
- winUtils = require("./window/utils"),
- events = require("./system/events"),
- { iteratorSymbol, forInIterator } = require("./util/iteration");
- const HTML = 0x01,
- TEXT = 0x02,
- DOM = 0x03;
- const ERR_CANNOT_CHANGE_SELECTION =
- "It isn't possible to change the selection, as there isn't currently a selection";
- const selections = ns();
- const Selection = Class({
-
- initialize: function initialize(rangeNumber) {
-
-
-
-
-
- Object.defineProperties(this, {
- rangeNumber: {
- enumerable: false,
- configurable: false,
- value: rangeNumber
- }
- });
- },
- get text() { return getSelection(TEXT, this.rangeNumber); },
- set text(value) { setSelection(TEXT, value, this.rangeNumber); },
- get html() { return getSelection(HTML, this.rangeNumber); },
- set html(value) { setSelection(HTML, value, this.rangeNumber); },
- get isContiguous() {
-
-
- let count = 0;
- for (let sel in selectionIterator)
- if (++count > 1)
- break;
- return count === 1;
- }
- });
- const selectionListener = {
- notifySelectionChanged: function (document, selection, reason) {
- if (!["SELECTALL", "KEYPRESS", "MOUSEUP"].some(function(type) reason &
- Ci.nsISelectionListener[type + "_REASON"]) || selection.toString() == "")
- return;
- this.onSelect();
- },
- onSelect: function() {
- emit(module.exports, "select");
- }
- }
- function* forOfIterator() {
- let selection = getSelection(DOM);
- let count = 0;
- if (selection)
- count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
- for (let i = 0; i < count; i++) {
- let sel = Selection(i);
- if (sel.text)
- yield Selection(i);
- }
- }
- const selectionIteratorOptions = {
- __iterator__: forInIterator
- }
- selectionIteratorOptions[iteratorSymbol] = forOfIterator;
- const selectionIterator = obscure(selectionIteratorOptions);
- function getFocusedWindow() {
- let window = winUtils.getFocusedWindow();
- return ignoreWindow(window) ? null : window;
- }
- function getFocusedElement() {
- let element = winUtils.getFocusedElement();
- if (!element || ignoreWindow(element.ownerDocument.defaultView))
- return null;
- return element;
- }
- function getSelection(type, rangeNumber) {
- let window, selection;
- try {
- window = getFocusedWindow();
- selection = window.getSelection();
- }
- catch (e) {
- return null;
- }
-
- if (type == DOM) {
- return selection;
- }
- else if (type == TEXT) {
- let range = safeGetRange(selection, rangeNumber);
- if (range)
- return range.toString();
- let node = getElementWithSelection();
- if (!node)
- return null;
- return node.value.substring(node.selectionStart, node.selectionEnd);
- }
- else if (type == HTML) {
- let range = safeGetRange(selection, rangeNumber);
-
-
-
-
-
- if (!range)
- return null;
- let node = window.document.createElement("span");
- node.appendChild(range.cloneContents());
- return node.innerHTML;
- }
- throw new Error("Type " + type + " is unrecognized.");
- }
- function setSelection(type, val, rangeNumber) {
-
-
- let window, selection;
- try {
- window = getFocusedWindow();
- selection = window.getSelection();
- }
- catch (e) {
- throw new Error(ERR_CANNOT_CHANGE_SELECTION);
- }
- let range = safeGetRange(selection, rangeNumber);
- if (range) {
- let fragment;
- if (type === HTML)
- fragment = range.createContextualFragment(val);
- else {
- fragment = range.createContextualFragment("");
- fragment.textContent = val;
- }
- range.deleteContents();
- range.insertNode(fragment);
- }
- else {
- let node = getElementWithSelection();
- if (!node)
- throw new Error(ERR_CANNOT_CHANGE_SELECTION);
- let { value, selectionStart, selectionEnd } = node;
- let newSelectionEnd = selectionStart + val.length;
- node.value = value.substring(0, selectionStart) +
- val +
- value.substring(selectionEnd, value.length);
- node.setSelectionRange(selectionStart, newSelectionEnd);
- }
- }
- function safeGetRange(selection, rangeNumber) {
- try {
- let { rangeCount } = selection;
- let range = null;
- if (typeof rangeNumber === "undefined")
- rangeNumber = 0;
- else
- rangeCount = rangeNumber + 1;
- for (; rangeNumber < rangeCount; rangeNumber++ ) {
- range = selection.getRangeAt(rangeNumber);
- if (range && range.toString())
- break;
- range = null;
- }
- return range;
- }
- catch (e) {
- return null;
- }
- }
- function getElementWithSelection() {
- let element = getFocusedElement();
- if (!element)
- return null;
- try {
-
-
-
- let { value, selectionStart, selectionEnd } = element;
- let hasSelection = typeof value === "string" &&
- !isNaN(selectionStart) &&
- !isNaN(selectionEnd) &&
- selectionStart !== selectionEnd;
- return hasSelection ? element : null;
- }
- catch (err) {
- return null;
- }
- }
- function addSelectionListener(window) {
- let selection = window.getSelection();
-
-
- if ("selection" in selections(window) && selections(window).selection === selection)
- return;
-
-
-
-
-
- if (selection instanceof Ci.nsISelectionPrivate)
- selection.addSelectionListener(selectionListener);
-
-
-
-
-
-
- window.addEventListener("select", selectionListener.onSelect, true);
- selections(window).selection = selection;
- };
- function removeSelectionListener(window) {
-
- if (!("selection" in selections(window)))
- return;
- let selection = window.getSelection();
- let isSameSelection = selection === selections(window).selection;
-
-
-
- if (selection instanceof Ci.nsISelectionPrivate && isSameSelection)
- selection.removeSelectionListener(selectionListener);
- window.removeEventListener("select", selectionListener.onSelect, true);
- delete selections(window).selection;
- };
- function onContent(event) {
- let window = event.subject.defaultView;
-
-
- if (window && getTabForContentWindow(window) && !ignoreWindow(window)) {
- addSelectionListener(window);
- }
- }
- events.on("document-element-inserted", onContent, true);
- getAllTabContentWindows().forEach(addSelectionListener);
- function onShown(event) {
- let window = event.subject.defaultView;
-
-
- if (!window)
- return;
-
- if ("selection" in selections(window)) {
- let currentSelection = window.getSelection();
- let { selection } = selections(window);
-
-
-
-
-
-
-
-
-
-
- if (currentSelection instanceof Ci.nsISelectionPrivate &&
- currentSelection !== selection) {
- window.addEventListener("select", selectionListener.onSelect, true);
- currentSelection.addSelectionListener(selectionListener);
- selections(window).selection = currentSelection;
- }
- }
- }
- events.on("document-shown", onShown, true);
- unload(function(){
- getAllTabContentWindows().forEach(removeSelectionListener);
- events.off("document-element-inserted", onContent);
- events.off("document-shown", onShown);
- off(exports);
- });
- const selection = Class({
- extends: EventTarget,
- implements: [ Selection, selectionIterator ]
- })();
- module.exports = selection;
|