Полезная информация

В мире Mozilla происходит много интересных событий. Но вам не нужно постоянно посещать новостные сайты, чтобы быть в курсе всех изменений. Зайдите на ленту новостей Mozilla Россия.

№1592625-09-2021 21:41:12

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
UA: Firefox 68.0

Re: Custom Buttons

ВВП пишет

может скрипт какой?

Так у тебя под спойлером и есть скрипт.
Если его в custom_script_win.js добавить, то будет
вылезать подтверждение при закрытии (каждого) browser.xhtml окна.


Но только при закрытии лисьей кнопкой закрытия окна,
а не кнопкой в Windows-заголовке окна, Alt+F4 и прочим.


А если «вечно хлопнешь и браузер закрылся» дофантазировать как вывод
подтверждения перед закрытием последнего нормального браузерного окна,
то можно попробовать в инициализацию CB-кнопки добавить

скрытый текст

Выделить код

Код:

window.toolbar.visible && (lst => ["close", "DOMWindowClose"].forEach(
	type => addEventListener(type, lst, true)
))(e => 
	e.target == window &&
	!Services.startup.restarting &&
	Array.from(browserWindows())
		.filter(w => !w.closed && w.toolbar.visible).length == 1 &&
	!Services.prompt.confirm(null, "ВНИМАНИЕ !", "Закрыть?") &&
	e.preventDefault()
);

Senflex пишет

Можно ли дополнить ваш код, моментальным сворачиванием cmd

Уж не знаю можно или нет, но, скажем так:
в самом браузере, напрямую, такая возможность не предусмотрена.
Хорошо бы покопаться, возможно ли через ctypes, но это долго и сложно.

Отредактировано Dumby (26-09-2021 18:41:22)

Отсутствует

 

№1592725-09-2021 22:42:51

Senflex
Участник
 
Группа: Members
Зарегистрирован: 18-07-2021
Сообщений: 29
UA: Chrome 93.0

Re: Custom Buttons

Благодарю за то что объяснили! Возник небольшой вопрос, можно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false. Типа команды ping в cmd.

Отсутствует

 

№1592826-09-2021 02:11:27

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 537
UA: Firefox 52.0

Re: Custom Buttons

Senflex пишет

можно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false.

Подтянутся гуру и может что-то получше посоветуют.
А мне только такой "костыль" в голову приходит:

скрытый текст

Выделить код

Код:

/*Initialization Code*/

this.onmousedown =e=> {
  if (e.button == 0) {
    network();
  }
  if ( e.button == 2 ) {
    gShowPopup(this);
  }
}
self.onclick =e=> e.preventDefault();


function network() {
  if (navigator.onLine) {
    alert('TRUE');
  }
  else {
    alert('FALSE');
  }
}


«The Truth Is Out There»

Отсутствует

 

№1592926-09-2021 11:49:09

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 92.0

Re: Custom Buttons

Dumby
Ставил это ,вроде норм.
//
function BrowserTryToCloseWindow() {
if (Services.prompt.confirm(null, "ВНИМАНИЕ !", "Закрыть Браузер ?"))
  if (WindowIsClosing()) {
    goQuitApplication({});
  } // WindowIsClosing does all the necessary checks
}

Отсутствует

 

№1593026-09-2021 17:36:08

Senflex
Участник
 
Группа: Members
Зарегистрирован: 18-07-2021
Сообщений: 29
UA: Chrome 93.0

Re: Custom Buttons

unter_officer пишет
Senflex пишет

можно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false.

Подтянутся гуру и может что-то получше посоветуют.
А мне только такой "костыль" в голову приходит:

скрытый текст

Выделить код

Код:

/*Initialization Code*/

this.onmousedown =e=> {
  if (e.button == 0) {
    network();
  }
  if ( e.button == 2 ) {
    gShowPopup(this);
  }
}
self.onclick =e=> e.preventDefault();


function network() {
  if (navigator.onLine) {
    alert('TRUE');
  }
  else {
    alert('FALSE');
  }
}

Этот вариант, подходит когда у роутера не горят огоньки, а когда начинаются загораться (ещё огонёк интернета не горит), уже показывает true.

Отсутствует

 

№1593126-09-2021 18:41:40

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
UA: Firefox 68.0

Re: Custom Buttons

Senflex пишет

можно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false. Типа команды ping в cmd.

Наверно можно HEAD-запрос на какой-нибудь адрес отправить

скрытый текст

Выделить код

Код:

var url = "http://example.com";

var xhr = new XMLHttpRequest();
xhr.open("HEAD", url, true);
xhr.timeout = 2000;

var {channel} = xhr;
channel.loadFlags |= channel.LOAD_BYPASS_CACHE;

xhr.onreadystatechange = () => {

	if (xhr.readyState == xhr.HEADERS_RECEIVED) {
		xhr.onreadystatechange = null;
		alert(true);
	}
	else if (xhr.readyState == xhr.DONE)
		alert(false);
}
xhr.send();

Отсутствует

 

№1593226-09-2021 20:30:38

Senflex
Участник
 
Группа: Members
Зарегистрирован: 18-07-2021
Сообщений: 29
UA: Chrome 93.0

Re: Custom Buttons

Dumby пишет
Senflex пишет

можно ли узнать через CB, есть интернет или нет. И вывести в alert, true или false. Типа команды ping в cmd.

Наверно можно HEAD-запрос на какой-нибудь адрес отправить

скрытый текст

Выделить код

Код:

var url = "http://example.com";

var xhr = new XMLHttpRequest();
xhr.open("HEAD", url, true);
xhr.timeout = 2000;

var {channel} = xhr;
channel.loadFlags |= channel.LOAD_BYPASS_CACHE;

xhr.onreadystatechange = () => {

	if (xhr.readyState == xhr.HEADERS_RECEIVED) {
		xhr.onreadystatechange = null;
		alert(true);
	}
	else if (xhr.readyState == xhr.DONE)
		alert(false);
}
xhr.send();

Код попробовал. Бывают ложные срабатывания.
Версия лисы 56.0.2

Отредактировано Senflex (27-09-2021 10:48:50)

Отсутствует

 

№1593329-09-2021 17:14:48

Senflex
Участник
 
Группа: Members
Зарегистрирован: 18-07-2021
Сообщений: 29
UA: Chrome 94.0

Re: Custom Buttons

Подскажите пожалуйста, какую версию CB выбрать для версии firefox 56.0.2. Тут скачал https://addons.thunderbird.net/Ru/firefox/addon/custom-buttons/?src=search появляется полоса с надписью (см. скин) как её убрать. И ещё не работает кнопка, "Добавить новую кнопку".
dfcc13e6e8c7t.jpg

Отредактировано Senflex (29-09-2021 17:23:10)

Отсутствует

 

№1593429-09-2021 17:58:07

kokoss
Участник
 
Группа: Members
Зарегистрирован: 15-02-2018
Сообщений: 1728
UA: Firefox 52.0

Re: Custom Buttons

Senflex пишет

Подскажите пожалуйста, какую версию CB выбрать для версии firefox 56.0.2. Тут скачал https://addons.thunderbird.net/Ru/firefox/addon/custom-buttons/?src=search появляется полоса с надписью (см. скин)

https://forum.mozilla-russia.org/viewto … 43#p744943


Win7

Отсутствует

 

№1593502-10-2021 21:06:00

Senflex
Участник
 
Группа: Members
Зарегистрирован: 18-07-2021
Сообщений: 29
UA: Chrome 94.0

Re: Custom Buttons

kokoss пишет
Senflex пишет

Подскажите пожалуйста, какую версию CB выбрать для версии firefox 56.0.2. Тут скачал https://addons.thunderbird.net/Ru/firefox/addon/custom-buttons/?src=search появляется полоса с надписью (см. скин)

https://forum.mozilla-russia.org/viewto … 43#p744943

Благодарю за ответ. Не получилось.

Отсутствует

 

№1593603-10-2021 22:34:40

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 242
UA: Google 2.1

Re: Custom Buttons

Войдите или зарегистрируйтесь, чтобы увидеть скрытый текст.


Жизнь иногда такое выкидывает, что хочется подобрать...

На форуме

 

№1593704-10-2021 04:09:08

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 92.0

Re: Custom Buttons

Dumby
После каждого редактирования кнопки Информация о странице пункты в контекстном меню клонируются. Можешь подправить?

Отсутствует

 

№1593804-10-2021 17:15:15

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
UA: Firefox 68.0

Re: Custom Buttons

Farby пишет

фишка "optionsType = 1" в manifest.json

Это где такое?
Есть <em:optionsType>1</em:optionsType> в install.rdf,
но там несовместимо с моим антиподписячим кодом.
В смысле разве что пришлось бы их объединить,
а это отдельный и неозвученный вопрос.


Вобщем, тяп-ляп скопировал файло,
но без добавления свойства "OPTIONS_TYPE_DIALOG" в AddonManager.
Могут быть проблемы, если какой-нибудь код будет написан так,
как если бы это свойство в нём есть, то есть ссылаться на него.

скрытый текст

Выделить код

Код:

// bootstrap-loader.js
try {(ios => {
	var subst = "bootstrap-loader-config-script";
	ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(subst, ios.newURI(String.raw`
		data:,/%2A%2A%2A RDFDataSource.jsm %2A%2A%2A/%0A%0Aconst NS_XML = "http://www.w3.org/XML/1998/namespace";%0Aconst NS_XMLNS = "http://www.w3.org/2000/xmlns/";%0Aconst NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns%23";%0Aconst NS_NC = "http://home.netscape.com/NC-rdf%23";%0A%0Afunction isElement(obj) {%0A  return Element.isInstance(obj);%0A}%0Afunction isText(obj) {%0A  return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text";%0A}%0A%0A/%2A%2A%0A %2A Returns either an rdf namespaced attribute or an un-namespaced attribute%0A %2A value. Returns null if neither exists,%0A %2A/%0Afunction getRDFAttribute(element, name) {%0A  if (element.hasAttributeNS(NS_RDF, name))%0A    return element.getAttributeNS(NS_RDF, name);%0A  if (element.hasAttribute(name))%0A    return element.getAttribute(name);%0A  return undefined;%0A}%0A%0A/%2A%2A%0A %2A Represents an assertion in the datasource%0A %2A/%0Aclass RDFAssertion {%0A  constructor(subject, predicate, object) {%0A    // The subject on this assertion, an RDFSubject%0A    this._subject = subject;%0A    // The predicate, a string%0A    this._predicate = predicate;%0A    // The object, an RDFNode%0A    this._object = object;%0A    // The datasource this assertion exists in%0A    this._ds = this._subject._ds;%0A    // Marks that _DOMnode is the subject's element%0A    this._isSubjectElement = false;%0A    // The DOM node that represents this assertion. Could be a property element,%0A    // a property attribute or the subject's element for rdf:type%0A    this._DOMNode = null;%0A  }%0A%0A  getPredicate() {%0A    return this._predicate;%0A  }%0A%0A  getObject() {%0A    return this._object;%0A  }%0A}%0A%0Aclass RDFNode {%0A  equals(rdfnode) {%0A    return (rdfnode.constructor === this.constructor &&%0A            rdfnode._value == this._value);%0A  }%0A}%0A%0A/%2A%2A%0A %2A A simple literal value%0A %2A/%0Aclass RDFLiteral extends RDFNode {%0A  constructor(value) {%0A    super();%0A    this._value = value;%0A  }%0A%0A  getValue() {%0A    return this._value;%0A  }%0A}%0A%0A/%2A%2A%0A %2A This is an RDF node that can be a subject so a resource or a blank node%0A %2A/%0Aclass RDFSubject extends RDFNode {%0A  constructor(ds) {%0A    super();%0A    // A lookup of the assertions with this as the subject. Keyed on predicate%0A    this._assertions = {};%0A    // A lookup of the assertions with this as the object. Keyed on predicate%0A    this._backwards = {};%0A    // The datasource this subject belongs to%0A    this._ds = ds;%0A    // The DOM elements in the document that represent this subject. Array of Element%0A    this._elements = [];%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the given Element from the DOM document%0A   %2A/%0A  /%2A eslint-disable complexity %2A/%0A  _parseElement(element) {%0A    this._elements.push(element);%0A%0A    // There might be an inferred rdf:type assertion in the element name%0A    if (element.namespaceURI != NS_RDF ||%0A        element.localName != "Description") {%0A      var assertion = new RDFAssertion(this, RDF_R("type"),%0A                                       this._ds.getResource(element.namespaceURI + element.localName));%0A      assertion._DOMnode = element;%0A      assertion._isSubjectElement = true;%0A      this._addAssertion(assertion);%0A    }%0A%0A    // Certain attributes can be literal properties%0A    for (let attr of element.attributes) {%0A      if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS ||%0A          attr.nodeName == "xmlns")%0A        continue;%0A      if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) &&%0A          (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName)))%0A        continue;%0A      var object = null;%0A      if (attr.namespaceURI == NS_RDF) {%0A        if (attr.localName == "type")%0A          object = this._ds.getResource(attr.nodeValue);%0A      }%0A      if (!object)%0A        object = new RDFLiteral(attr.nodeValue);%0A      assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object);%0A      assertion._DOMnode = attr;%0A      this._addAssertion(assertion);%0A    }%0A%0A    var child = element.firstChild;%0A    element.listCounter = 1;%0A    while (child) {%0A      if (isElement(child)) {%0A        object = null;%0A        var predicate = child.namespaceURI + child.localName;%0A        if (child.namespaceURI == NS_RDF) {%0A          if (child.localName == "li") {%0A            predicate = RDF_R(%60_%24{element.listCounter}%60);%0A            element.listCounter++;%0A          }%0A        }%0A%0A        // Check for and bail out on unknown attributes on the property element%0A        for (let attr of child.attributes) {%0A          // Ignore XML namespaced attributes%0A          if (attr.namespaceURI == NS_XML)%0A            continue;%0A          // These are reserved by XML for future use%0A          if (attr.localName.substring(0, 3).toLowerCase() == "xml")%0A            continue;%0A          // We can handle these RDF attributes%0A          if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) &&%0A              ["resource", "nodeID"].includes(attr.localName))%0A            continue;%0A          // This is a special attribute we handle for compatibility with Mozilla RDF%0A          if (attr.namespaceURI == NS_NC &&%0A              attr.localName == "parseType")%0A            continue;%0A        }%0A%0A        var parseType = child.getAttributeNS(NS_NC, "parseType");%0A%0A        var resource = getRDFAttribute(child, "resource");%0A        var nodeID = getRDFAttribute(child, "nodeID");%0A%0A        if (resource !== undefined) {%0A          var base = Services.io.newURI(element.baseURI);%0A          object = this._ds.getResource(base.resolve(resource));%0A        } else if (nodeID !== undefined) {%0A          object = this._ds.getBlankNode(nodeID);%0A        } else {%0A          var hasText = false;%0A          var childElement = null;%0A          var subchild = child.firstChild;%0A          while (subchild) {%0A            if (isText(subchild) && /\S/.test(subchild.nodeValue)) {%0A              hasText = true;%0A            } else if (isElement(subchild)) {%0A              childElement = subchild;%0A            }%0A            subchild = subchild.nextSibling;%0A          }%0A%0A          if (childElement) {%0A            object = this._ds._getSubjectForElement(childElement);%0A            object._parseElement(childElement);%0A          } else%0A            object = new RDFLiteral(child.textContent);%0A        }%0A%0A        assertion = new RDFAssertion(this, predicate, object);%0A        this._addAssertion(assertion);%0A        assertion._DOMnode = child;%0A      }%0A      child = child.nextSibling;%0A    }%0A  }%0A  /%2A eslint-enable complexity %2A/%0A%0A  /%2A%2A%0A   %2A Adds a new assertion to the internal hashes. Should be called for every%0A   %2A new assertion parsed or created programmatically.%0A   %2A/%0A  _addAssertion(assertion) {%0A    var predicate = assertion.getPredicate();%0A    if (predicate in this._assertions)%0A      this._assertions[predicate].push(assertion);%0A    else%0A      this._assertions[predicate] = [ assertion ];%0A%0A    var object = assertion.getObject();%0A    if (object instanceof RDFSubject) {%0A      // Create reverse assertion%0A      if (predicate in object._backwards)%0A        object._backwards[predicate].push(assertion);%0A      else%0A        object._backwards[predicate] = [ assertion ];%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Returns all objects in assertions with this subject and the given predicate.%0A   %2A/%0A  getObjects(predicate) {%0A    if (predicate in this._assertions)%0A      return Array.from(this._assertions[predicate],%0A                        i => i.getObject());%0A%0A    return [];%0A  }%0A%0A  /%2A%2A%0A   %2A Retrieves the first property value for the given predicate.%0A   %2A/%0A  getProperty(predicate) {%0A    if (predicate in this._assertions)%0A      return this._assertions[predicate][0].getObject();%0A    return null;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFResource for the datasource. Private.%0A %2A/%0Aclass RDFResource extends RDFSubject {%0A  constructor(ds, uri) {%0A    super(ds);%0A    // This is the uri that the resource represents.%0A    this._uri = uri;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new blank node. Private.%0A %2A/%0Aclass RDFBlankNode extends RDFSubject {%0A  constructor(ds, nodeID) {%0A    super(ds);%0A    // The nodeID of this node. May be null if there is no ID.%0A    this._nodeID = nodeID;%0A  }%0A%0A  /%2A%2A%0A   %2A Sets attributes on the DOM element to mark it as representing this node%0A   %2A/%0A  _applyToElement(element) {%0A    if (!this._nodeID)%0A      return;%0A    if (USE_RDFNS_ATTR) {%0A      var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A      element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A    } else {%0A      element.setAttribute("nodeID", this._nodeID);%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Creates a new Element in the document for holding assertions about this%0A   %2A subject. The URI controls what tagname to use.%0A   %2A/%0A  _createNewElement(uri) {%0A    // If there are already nodes representing this in the document then we need%0A    // a nodeID to match them%0A    if (!this._nodeID && this._elements.length > 0) {%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    return super._createNewElement.call(uri);%0A  }%0A%0A  /%2A%2A%0A   %2A Adds a reference to this node to the given property Element.%0A   %2A/%0A  _addReferenceToElement(element) {%0A    if (this._elements.length > 0 && !this._nodeID) {%0A      // In document elsewhere already%0A      // Create a node ID and update the other nodes referencing%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    if (this._nodeID) {%0A      if (USE_RDFNS_ATTR) {%0A        let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A        element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A      } else {%0A        element.setAttribute("nodeID", this._nodeID);%0A      }%0A    } else {%0A      // Add the empty blank node, this is generally right since further%0A      // assertions will be added to fill this out%0A      var newelement = this._ds._addElement(element, RDF_R("Description"));%0A      newelement.listCounter = 1;%0A      this._elements.push(newelement);%0A    }%0A  }%0A%0A    /%2A%2A%0A     %2A Removes any reference to this node from the given property Element.%0A     %2A/%0A    _removeReferenceFromElement(element) {%0A      if (element.hasAttributeNS(NS_RDF, "nodeID"))%0A        element.removeAttributeNS(NS_RDF, "nodeID");%0A      if (element.hasAttribute("nodeID"))%0A        element.removeAttribute("nodeID");%0A    }%0A%0A  getNodeID() {%0A    return this._nodeID;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFDataSource from the given document. The document will be%0A %2A changed as assertions are added and removed to the RDF. Pass a null document%0A %2A to start with an empty graph.%0A %2A/%0Aclass RDFDataSource {%0A  constructor(document) {%0A    // All known resources, indexed on URI%0A    this._resources = {};%0A    // All blank nodes%0A    this._allBlankNodes = [];%0A%0A    // The underlying DOM document for this datasource%0A    this._document = document;%0A    this._parseDocument();%0A  }%0A%0A  static loadFromString(text) {%0A    let parser = new DOMParser();%0A    let document = parser.parseFromString(text, "application/xml");%0A%0A    return new this(document);%0A  }%0A%0A  /%2A%2A%0A   %2A Returns an rdf subject for the given DOM Element. If the subject has not%0A   %2A been seen before a new one is created.%0A   %2A/%0A  _getSubjectForElement(element) {%0A    var about = getRDFAttribute(element, "about");%0A%0A    if (about !== undefined) {%0A      let base = Services.io.newURI(element.baseURI);%0A      return this.getResource(base.resolve(about));%0A    }%0A    return this.getBlankNode(null);%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the document for subjects at the top level.%0A   %2A/%0A  _parseDocument() {%0A    var domnode = this._document.documentElement.firstChild;%0A    while (domnode) {%0A      if (isElement(domnode)) {%0A        var subject = this._getSubjectForElement(domnode);%0A        subject._parseElement(domnode);%0A      }%0A      domnode = domnode.nextSibling;%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Gets a blank node. nodeID may be null and if so a new blank node is created.%0A   %2A If a nodeID is given then the blank node with that ID is returned or created.%0A   %2A/%0A  getBlankNode(nodeID) {%0A    var rdfnode = new RDFBlankNode(this, nodeID);%0A    this._allBlankNodes.push(rdfnode);%0A    return rdfnode;%0A  }%0A%0A  /%2A%2A%0A   %2A Gets the resource for the URI. The resource is created if it has not been%0A   %2A used already.%0A   %2A/%0A  getResource(uri) {%0A    if (uri in this._resources)%0A      return this._resources[uri];%0A%0A    var resource = new RDFResource(this, uri);%0A    this._resources[uri] = resource;%0A    return resource;%0A  }%0A}%0A%0A%0A/%2A%2A%2A RDFManifestConverter.jsm %2A%2A%2A/%0A%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A  return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0A%0Afunction getValue(literal) {%0A  return literal && literal.getValue();%0A}%0A%0Afunction getProperty(resource, property) {%0A  return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A  constructor(ds) {%0A    this.ds = ds;%0A  }%0A%0A  static loadFromString(text) {%0A    return new this(RDFDataSource.loadFromString(text));%0A  }%0A}%0A%0Aclass InstallRDF extends Manifest {%0A  _readProps(source, obj, props) {%0A    for (let prop of props) {%0A      let val = getProperty(source, prop);%0A      if (val != null) {%0A        obj[prop] = val;%0A      }%0A    }%0A  }%0A%0A  _readArrayProp(source, obj, prop, target, decode = getValue) {%0A    let result = Array.from(source.getObjects(EM_R(prop)),%0A                            target => decode(target));%0A    if (result.length) {%0A      obj[target] = result;%0A    }%0A  }%0A%0A  _readArrayProps(source, obj, props, decode = getValue) {%0A    for (let [prop, target] of Object.entries(props)) {%0A      this._readArrayProp(source, obj, prop, target, decode);%0A    }%0A  }%0A%0A  _readLocaleStrings(source, obj) {%0A    this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A    this._readArrayProps(source, obj, {%0A      locale: "locales",%0A      developer: "developers",%0A      translator: "translators",%0A      contributor: "contributors",%0A    });%0A  }%0A%0A  decode() {%0A    let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A    let result = {};%0A%0A    let props = ["id", "version", "type", "updateURL", "optionsURL",%0A                 "optionsType", "aboutURL", "iconURL",%0A                 "bootstrap", "unpack", "strictCompatibility"];%0A    this._readProps(root, result, props);%0A%0A    let decodeTargetApplication = source => {%0A      let app = {};%0A      this._readProps(source, app, ["id", "minVersion", "maxVersion"]);%0A      return app;%0A    };%0A%0A    let decodeLocale = source => {%0A      let localized = {};%0A      this._readLocaleStrings(source, localized);%0A      return localized;%0A    };%0A%0A    this._readLocaleStrings(root, result);%0A%0A    this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"});%0A    this._readArrayProps(root, result, {"targetApplication": "targetApplications"},%0A                         decodeTargetApplication);%0A    this._readArrayProps(root, result, {"localized": "localized"},%0A                         decodeLocale);%0A    this._readArrayProps(root, result, {"dependency": "dependencies"},%0A                         source => getProperty(source, "id"));%0A%0A    return result;%0A  }%0A}%0A%0A%0A/%2A%2A%2A BootstrapLoader.jsm %2A%2A%2A/%0A%0Avar {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");%0Avar {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");%0A%0AXPCOMUtils.defineLazyModuleGetters(this, {%0A  ConsoleAPI: "resource://gre/modules/Console.jsm",%0A  Blocklist: "resource://gre/modules/Blocklist.jsm",%0A  XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm"%0A});%0A%0Avar OPTIONS_TYPE_DIALOG = 1;%0A%0AServices.obs.addObserver(doc => {%0A  if (doc.location.protocol + doc.location.pathname === 'about:addons' ||%0A      doc.location.protocol + doc.location.pathname === 'chrome:/content/extensions/aboutaddons.html') {%0A    const win = doc.defaultView;%0A    let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent;%0A    win.customElements.get('addon-card').prototype.handleEvent = function (e) {%0A      if (e.type === 'click' &&%0A          e.target.getAttribute('action') === 'preferences' &&%0A          this.addon.optionsType == OPTIONS_TYPE_DIALOG) {%0A        var windows = Services.wm.getEnumerator(null);%0A        while (windows.hasMoreElements()) {%0A          var win2 = windows.getNext();%0A          if (win2.closed) {%0A            continue;%0A          }%0A          if (win2.document.documentURI == this.addon.optionsURL) {%0A            win2.focus();%0A            return;%0A          }%0A        }%0A        var features = 'chrome,titlebar,toolbar,centerscreen';%0A        var instantApply = Services.prefs.getBoolPref('browser.preferences.instantApply');%0A        features += instantApply ? ',dialog=no' : '';%0A        win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features); %0A      } else {%0A        handleEvent_orig.apply(this, arguments);%0A      }%0A    }%0A    let update_orig = win.customElements.get('addon-options').prototype.update;%0A    win.customElements.get('addon-options').prototype.update = function (card, addon) {%0A      update_orig.apply(this, arguments);%0A      if (addon.optionsType == OPTIONS_TYPE_DIALOG)%0A        this.querySelector('panel-item[action="preferences"]').hidden = false;%0A    }%0A  }%0A}, 'chrome-document-loaded');%0A%0Aconst {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0Aconst {XPIDatabase, AddonInternal} = ChromeUtils.import("resource://gre/modules/addons/XPIDatabase.jsm");%0A%0ACu.import("resource://gre/modules/addons/XPIDatabase.jsm", {}).defineAddonWrapperProperty("optionsType", function optionsType() {%0A  if (!this.isActive) {%0A    return null;%0A  }%0A%0A  let addon = this.__AddonInternal__;%0A  let hasOptionsURL = !!this.optionsURL;%0A%0A  if (addon.optionsType) {%0A    switch (parseInt(addon.optionsType, 10)) {%0A      case OPTIONS_TYPE_DIALOG:%0A      case AddonManager.OPTIONS_TYPE_TAB:%0A      case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:%0A        return hasOptionsURL ? addon.optionsType : null;%0A    }%0A    return null;%0A  }%0A%0A  return null;%0A});%0A%0AXPIDatabase.isDisabledLegacy = () => false;%0A%0AXPCOMUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => XPIProvider.BOOTSTRAP_REASONS);%0A%0Aconst {Log} = ChromeUtils.import('resource://gre/modules/Log.jsm');%0Avar logger = Log.repository.getLogger('addons.bootstrap');%0A%0A/%2A%2A%0A %2A Valid IDs fit this pattern.%0A %2A/%0Avar gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]%2A\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA      = ['id', 'version', 'type', 'internalName', 'updateURL',%0A                            'optionsURL', 'optionsType', 'aboutURL', 'iconURL'];%0Aconst PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL'];%0Aconst PROP_LOCALE_MULTI  = ['developers', 'translators', 'contributors'];%0A%0A// Map new string type identifiers to old style nsIUpdateItem types.%0A// Retired values:%0A// 32 = multipackage xpi file%0A// 8 = locale%0A// 256 = apiextension%0A// 128 = experiment%0A// theme = 4%0Aconst TYPES = {%0A  extension: 2,%0A  dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A  extension: true,%0A  dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A  let ext = filename.slice(-4).toLowerCase();%0A  return ext === '.xpi' || ext === '.zip';%0A}%0A%0A/%2A%2A%0A %2A Gets an nsIURI for a file within another file, either a directory or an XPI%0A %2A file. If aFile is a directory then this will return a file: URI, if it is an%0A %2A XPI file then it will return a jar: URI.%0A %2A%0A %2A @param {nsIFile} aFile%0A %2A        The file containing the resources, must be either a directory or an%0A %2A        XPI file%0A %2A @param {string} aPath%0A %2A        The path to find the resource at, '/' separated. If aPath is empty%0A %2A        then the uri to the root of the contained files will be returned%0A %2A @returns {nsIURI}%0A %2A        An nsIURI pointing at the resource%0A %2A/%0Afunction getURIForResourceInFile(aFile, aPath) {%0A  if (!isXPI(aFile.leafName)) {%0A    let resource = aFile.clone();%0A    if (aPath)%0A      aPath.split('/').forEach(part => resource.append(part));%0A%0A    return Services.io.newFileURI(resource);%0A  }%0A%0A  return buildJarURI(aFile, aPath);%0A}%0A%0A/%2A%2A%0A %2A Creates a jar: URI for a file inside a ZIP file.%0A %2A%0A %2A @param {nsIFile} aJarfile%0A %2A        The ZIP file as an nsIFile%0A %2A @param {string} aPath%0A %2A        The path inside the ZIP file%0A %2A @returns {nsIURI}%0A %2A        An nsIURI for the file%0A %2A/%0Afunction buildJarURI(aJarfile, aPath) {%0A  let uri = Services.io.newFileURI(aJarfile);%0A  uri = 'jar:' + uri.spec + '!/' + aPath;%0A  return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A  name: 'bootstrap',%0A  manifestFile: 'install.rdf',%0A  async loadManifest(pkg) {%0A    /%2A%2A%0A     %2A Reads locale properties from either the main install manifest root or%0A     %2A an em:localized section in the install manifest.%0A     %2A%0A     %2A @param {Object} aSource%0A     %2A        The resource to read the properties from.%0A     %2A @param {boolean} isDefault%0A     %2A        True if the locale is to be read from the main install manifest%0A     %2A        root%0A     %2A @param {string[]} aSeenLocales%0A     %2A        An array of locale names already seen for this install manifest.%0A     %2A        Any locale names seen as a part of this function will be added to%0A     %2A        this array%0A     %2A @returns {Object}%0A     %2A        an object containing the locale properties%0A     %2A/%0A    function readLocale(aSource, isDefault, aSeenLocales) {%0A      let locale = {};%0A      if (!isDefault) {%0A        locale.locales = [];%0A        for (let localeName of aSource.locales || []) {%0A          if (!localeName) {%0A            logger.warn('Ignoring empty locale in localized properties');%0A            continue;%0A          }%0A          if (aSeenLocales.includes(localeName)) {%0A            logger.warn('Ignoring duplicate locale in localized properties');%0A            continue;%0A          }%0A          aSeenLocales.push(localeName);%0A          locale.locales.push(localeName);%0A        }%0A%0A        if (locale.locales.length == 0) {%0A          logger.warn('Ignoring localized properties with no listed locales');%0A          return null;%0A        }%0A      }%0A%0A      for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A        if (hasOwnProperty(aSource, prop)) {%0A          locale[prop] = aSource[prop];%0A        }%0A      }%0A%0A      return locale;%0A    }%0A%0A    let manifestData = await pkg.readString('install.rdf');%0A    let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A    let addon = new AddonInternal();%0A    for (let prop of PROP_METADATA) {%0A      if (hasOwnProperty(manifest, prop)) {%0A        addon[prop] = manifest[prop];%0A      }%0A    }%0A%0A    if (!addon.type) {%0A      addon.type = 'extension';%0A    } else {%0A      let type = addon.type;%0A      addon.type = null;%0A      for (let name in TYPES) {%0A        if (TYPES[name] == type) {%0A          addon.type = name;%0A          break;%0A        }%0A      }%0A    }%0A%0A    if (!(addon.type in TYPES))%0A      throw new Error('Install manifest specifies unknown type: ' + addon.type);%0A%0A    if (!addon.id)%0A      throw new Error('No ID in install manifest');%0A    if (!gIDTest.test(addon.id))%0A      throw new Error('Illegal add-on ID ' + addon.id);%0A    if (!addon.version)%0A      throw new Error('No version in install manifest');%0A%0A    addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A                                 manifest.strictCompatibility == 'true');%0A%0A    // Only read these properties for extensions.%0A    if (addon.type == 'extension') {%0A      if (manifest.bootstrap != 'true') {%0A        throw new Error('Non-restartless extensions no longer supported');%0A      }%0A%0A      if (addon.optionsType &&%0A          addon.optionsType != OPTIONS_TYPE_DIALOG &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) {%0A            throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType);%0A      }%0A%0A      if (addon.optionsType)%0A        addon.optionsType = parseInt(addon.optionsType);%0A    }%0A%0A    addon.defaultLocale = readLocale(manifest, true);%0A%0A    let seenLocales = [];%0A    addon.locales = [];%0A    for (let localeData of manifest.localized || []) {%0A      let locale = readLocale(localeData, false, seenLocales);%0A      if (locale)%0A        addon.locales.push(locale);%0A    }%0A%0A    let dependencies = new Set(manifest.dependencies);%0A    addon.dependencies = Object.freeze(Array.from(dependencies));%0A%0A    let seenApplications = [];%0A    addon.targetApplications = [];%0A    for (let targetApp of manifest.targetApplications || []) {%0A      if (!targetApp.id || !targetApp.minVersion ||%0A          !targetApp.maxVersion) {%0A            logger.warn('Ignoring invalid targetApplication entry in install manifest');%0A            continue;%0A      }%0A      if (seenApplications.includes(targetApp.id)) {%0A        logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id +%0A                    ' in install manifest');%0A        continue;%0A      }%0A      seenApplications.push(targetApp.id);%0A      addon.targetApplications.push(targetApp);%0A    }%0A%0A    // Note that we don't need to check for duplicate targetPlatform entries since%0A    // the RDF service coalesces them for us.%0A    addon.targetPlatforms = [];%0A    for (let targetPlatform of manifest.targetPlatforms || []) {%0A      let platform = {%0A        os: null,%0A        abi: null,%0A      };%0A%0A      let pos = targetPlatform.indexOf('_');%0A      if (pos != -1) {%0A        platform.os = targetPlatform.substring(0, pos);%0A        platform.abi = targetPlatform.substring(pos + 1);%0A      } else {%0A        platform.os = targetPlatform;%0A      }%0A%0A      addon.targetPlatforms.push(platform);%0A    }%0A%0A    addon.userDisabled = false;%0A    addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A    addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A    addon.userPermissions = null;%0A%0A    addon.icons = {};%0A    if (await pkg.hasResource('icon.png')) {%0A      addon.icons[32] = 'icon.png';%0A      addon.icons[48] = 'icon.png';%0A    }%0A%0A    if (await pkg.hasResource('icon64.png')) {%0A      addon.icons[64] = 'icon64.png';%0A    }%0A%0A    return addon;%0A  },%0A%0A  loadScope(addon) {%0A    let file = addon.file || addon._sourceBundle;%0A    let uri = getURIForResourceInFile(file, 'bootstrap.js').spec;%0A    let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A    let sandbox = new Cu.Sandbox(principal, {%0A      sandboxName: uri,%0A      addonId: addon.id,%0A      wantGlobalProperties: ['ChromeUtils'],%0A      metadata: { addonID: addon.id, URI: uri },%0A    });%0A%0A    try {%0A      Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A      XPCOMUtils.defineLazyGetter(sandbox, 'console', () =>%0A        new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 }));%0A%0A      Services.scriptloader.loadSubScript(uri, sandbox);%0A    } catch (e) {%0A      logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A    }%0A%0A    function findMethod(name) {%0A      if (sandbox.name) {%0A        return sandbox.name;%0A      }%0A%0A      try {%0A        let method = Cu.evalInSandbox(name, sandbox);%0A        return method;%0A      } catch (err) { }%0A%0A      return () => {%0A        logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A      };%0A    }%0A%0A    let install = findMethod('install');%0A    let uninstall = findMethod('uninstall');%0A    let startup = findMethod('startup');%0A    let shutdown = findMethod('shutdown');%0A%0A    return {%0A      install(...args) {%0A        install(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      uninstall(...args) {%0A        uninstall(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      startup(...args) {%0A        if (addon.type == 'extension') {%0A          logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A          Components.manager.addBootstrappedManifestLocation(file);%0A        }%0A        return startup(...args);%0A      },%0A%0A      shutdown(data, reason) {%0A        try {%0A          return shutdown(data, reason);%0A        } catch (err) {%0A          throw err;%0A        } finally {%0A          if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A            logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A            Components.manager.removeBootstrappedManifestLocation(file);%0A          }%0A        }%0A      },%0A    };%0A  },%0A};%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0A%0AObject.defineProperty(%0A  AddonInternal.prototype,%0A  "providesUpdatesSecurely",%0A  {enumerable: true, value: true}%0A);%0A
	`.trim()));
	Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
		.loadSubScript("resource://" + subst, new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils", "DOMParser", "Element", "fetch"]}));
})(Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService));} catch(ex) {Cu.reportError(ex);}

voqabuhe пишет

После каждого редактирования кнопки Информация о странице пункты в контекстном меню клонируются.

Так это и не кнопка. Это код для ucf custom_script_win.js
Но можно и в кнопке использовать, если добавить предпоследней строкой
    addDestructor(() => menuitem.remove());



Custom Buttons 0.0.7.0.0.20
paxmod и bootstrap в zip-папке.

Отсутствует

 

№1593904-10-2021 18:36:44

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 242
UA: Google 2.1

Re: Custom Buttons

Dumby пишет

Вобщем, тяп-ляп скопировал файло

Спасибо, буду пробовать


Жизнь иногда такое выкидывает, что хочется подобрать...

На форуме

 

№1594004-10-2021 18:50:40

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 92.0

Re: Custom Buttons

Dumby пишет

Так это и не кнопка. Это код для ucf custom_script_win.js
Но можно и в кнопке использовать, если добавить предпоследней строкой
    addDestructor(() => menuitem.remove());

Понятно. Спасибо.

Отсутствует

 

№1594104-10-2021 20:26:36

kokoss
Участник
 
Группа: Members
Зарегистрирован: 15-02-2018
Сообщений: 1728
UA: Firefox 52.0

Re: Custom Buttons

Dumby пишет

Custom Buttons 0.0.7.0.0.20
paxmod и bootstrap в zip-папке.

Спасибо!


Win7

Отсутствует

 

№1594205-10-2021 17:46:24

Kot DaVinci
Участник
 
Группа: Members
Зарегистрирован: 11-10-2020
Сообщений: 18
UA: Firefox 93.0

Re: Custom Buttons

Здравствуйте. Можно ли эту кнопку заставить работать на актуальной версии Лисы (93.0 x64)?

Многоцветное автовыделение
кнопка целиком

Выделить код

Код:

custombutton://%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0D%0A%3Ccustombutton%20xmlns%3Acb%3D%22http%3A//xsms.nm.ru/custombuttons/%22%3E%0A%20%20%3Cname%3E%u041C%u043D%u043E%u0433%u043E%u0446%u0432%u0435%u0442%u043D%u043E%u0435%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%3C/name%3E%0A%20%20%3Cimage%3E%3C%21%5BCDATA%5Bdata%3Aimage/x-icon%3Bbase64%2CAAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD+//4B////APX69AV9vWaPVJsR9GabDPiQrDGn6OzRG////wD+/v0B////AP///gL///0B////AP///wD///8A////AP3+/gP///8ActGnhRyqVP83q0r8PqMu/kqZDf9yoh7Wu8t8YNjan07m4bM9+ffqCf///wD///4B////AP///wD8/v8C//7/AOL5/A4d1NrUFc/G/yHEov0qt3b/NKtM/UGeHv9WlAD/eJcB/peZAP+nphzH5+bAIP///wD+/v8A/fz/A/3+/wH///8A5/b+CyG8/c4Jz/z/ENfm/RrLuf8nu4b/M61S/UGgJvtbnBH8fqAQ/IKXAP+lrjax/v76AP7+/gH///8A7fD9CNni/Bb///8Ac7/4dQCn9/8P1f38ENbh/xzIsf8ouoD/NKtL/0GeHf9enA79dJ0M/4yiGOrx8d0c/f3/AJaT7105O+TOHTjn5GuP8n7u9v4AOK/3ugW/+/8N2vX9FdDN/yDDoP8rtXD/N6c//kqdGPpcnBD/dqIZ3/L05BCVht5rLhXE/zMj0f0nKuL/EkHp7+Dm/Bq64/wyBKz4/w/W/fwP1+X/Gsq4/ya+kPwwsWH+M58o/jSSAP+exXNr////AFkwrepXMrT8SjPG9icg2v8xRunM8fX+BfL5/gcdpPbOBMH8/w/b9vwX0dH+FcKk/yS3evFZum2rpdOaUPj79gD///8BZy6R8lsqn/8/Hbb/NSfS6MPE+DDj7PwI8vP9ALHe/DQXs/nWAs/9/wDV5v9C1Mmwye/jI////wD///8B////AP7+/gG/oclQgVSpuIhuy4/OyfIheHbpgx815+4vZ+7C2uf8Fuz5/we+8P83y/f8Jvb9/QL///8A8vv4CZPUpGO837Y5////AP///wD6+f0D////AIVy1IsgEsz/Lini+Qow5/+Gq/Vq////AM/t/Ruo6/4+6/z9C////wB817lzE6dQ/zGeJfOs05lI/Pv9Bf///wDXy+knTCOu7UYvxf89KtH4Fhrh/4yb9GTB3/sjCJH07QC9+v854Pio9///AFHOsrMktnX/NKtO/0KoPsz9/P0D////AMey2ElWJaL/UzG4+k0vv/4sGs/5vLn1Mkl68aoLcvD/G6j4+QDH+v+29f8xY9zUkA/Eo/8swpH3JL6M/P38/gL///8A08DcNF8kkPlgNar9Sh2m/3texp/i4PsPKTDk7hxP7P8eefD7Apb1/47Z/E2x8vw2AMrv/wXN5/8O0Ova////AP///wH8+/wAoHe0iWkukfN+U66v8+31C9PL7iA3H8n6KSzi/yNF6/sMVe3/rc35PP///wB/zv1qM7L7u53e/j7///8A////AP///wD7+fsB6NzqD/n2+gT///8A7uf0EV80rONFKcD/LCTU/zxC47/u8P4D////Af7+/gH//f4A///+AP///wD///8A//7/AP79/gL///8A/v7/Av38/gT///8AsZHEZmAplv5gOrPQzMbwKf///wD9/f4C/f//A/7//wL9/v8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3D%5D%5D%3E%3C/image%3E%0A%20%20%3Cmode%3E0%3C/mode%3E%0A%20%20%3Cinitcode%3E%3C%21%5BCDATA%5B//%20%u041D%u0430%u0441%u0442%u0440%u043E%u0439%u043A%u0430%20%u0444%u0443%u043D%u043A%u0446%u0438%u0439%20%u043A%u043B%u0438%u043A%u043E%u0432%20%u043C%u044B%u0448%u0438%20%u0434%u043B%u044F%20%u043A%u043D%u043E%u043F%u043A%u0438%20..........%0Athis.onclick%20%3De%3D%3E%20%7B%0A%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20%u041B%u041A%u041C%20....%0A%20%20%20if%20%28%20e.button%20%3D%3D%200%20%29%20%7B%0A%20%20%20%20%20%20%20%20cbu.setPrefs%28s%2C%20%21cbu.getPrefs%28s%29%29%3B%0A%20%20%20%20%20%20%20%20cbu.getPrefs%28s%29%20%3F%20doHighlight%28%29%20%3A%20clearHighlight%28%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20%u0421%u041A%u041C%20....%20%20%20%20%0A%20%20%20if%20%28%20e.button%20%3D%3D%201%20%29%20toggleHighlightValue%28%27add%27%29%3B%0A%20%20%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20%u041F%u041A%u041C%20....%20%20%20%20%0A%20%20%20if%20%28%20e.button%20%3D%3D%202%20%26%26%20%21e.ctrlKey%20%26%26%20%21e.shiftKey%20%26%26%20%21e.altKey%20%26%26%20%21e.metaKey%20%29%20%7B%20%0A%20%20%20%20%20%20%20%20e.preventDefault%28%29%3B%20%20%0A%20%20%20%20%20%20%20%20toggleHighlightValue%28%27clear%27%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%0A%20%20%20//%20%u0434%u0435%u0439%u0441%u0442%u0432%u0438%u0435%20%u043F%u0440%u0438%20%u043A%u043B%u0438%u043A%u0435%20Ctrl%20+%20%u041F%u041A%u041C%20....%20.%20%20%0A%20%20%20if%20%28%20e.button%20%3D%3D%202%20%26%26%20%21e.ctrlKey%20%26%26%20e.shiftKey%20%26%26%20%21e.altKey%20%26%26%20%21e.metaKey%20%29%20%7B%20%0A%20%20%20%20%20%20%20%20e.preventDefault%28%29%3B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20//%20%u043E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0440%u0435%u0434%u0430%u043A%u0442%u043E%u0440%20%u043A%u043D%u043E%u043F%u043A%u0438%20%u0432%20%u0432%u043A%u043B%u0430%u0434%u043A%u0435%20%u0421%u043F%u0440%u0430%u0432%u043A%u0430%0A%20%20%20%20%20%20%20%20function%20observer%28subject%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20subject.addEventListener%28%22load%22%2C%20%28e%2C%20doc%20%3D%20e.target%29%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%20doc.URL.startsWith%28%22chrome%3A//custombuttons%22%29%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Services.ww.unregisterNotification%28observer%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20doc.getElementsByTagName%28%22tabs%22%29%5B0%5D.children%5B2%5D.click%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20Services.ww.registerNotification%28observer%29%3B%0A%0A%20%20%20%20%20%20%20%20custombuttons.editButton%28this%29%3B%20//%20%u043E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0440%u0435%u0434%u0430%u043A%u0442%u043E%u0440%0A%20%20%20%20%20%20%20%20%7D%20%0A%7D%3B%0A%0A%0A//%20%u0426%u0432%u0435%u0442%u0430%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%2C%20%u043C%u043E%u0436%u043D%u043E%20%u043C%u0435%u043D%u044F%u0442%u044C%20%u0438%20%u0434%u043E%u0431%u0430%u0432%u043B%u044F%u0442%u044C%20..........%0Avar%20colors%20%3D%20%5B%22%23FFFF00%22%2C%20%22%23FF0000%22%2C%20%22%2366FFFF%22%2C%20%22%2366FF99%22%2C%20%22%23FFA500%22%2C%20%22%23FF00FF%22%5D%3B%0A%0A%0A//%20%u041F%u043E%u0434%u0441%u043A%u0430%u0437%u043A%u0430%20%u0434%u043B%u044F%20%u043A%u043D%u043E%u043F%u043A%u0438%20...........%0Athis.onmouseover%20%3D%28%29%3D%3E%20%7B%0A%20%20%20var%20array%20%3D%20getArray%28%29.map%28str%3D%3E%20%7B%20return%20str.slice%280%2C50%29%20+%20%28str.length%20%3E%2050%20%3F%20%22...%22%20%3A%20%22%22%29%20%7D%29%3B%0A%20%20%20this.tooltipText%20%3D%20self.label%20+%20%22%5Cn%u041B%3A%20B%u043A%u043B%u044E%u0447%u0438%u0442%u044C/%u0432%u044B%u043A%u043B%u044E%u0447%u0438%u0442%u044C%20%5Cn%u0421%3A%20%u0414%u043E%u0431%u0430%u0432%u0438%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%5Cn%u041F%3A%20%u0421%u0431%u0440%u043E%u0441%u0438%u0442%u044C%20%u0432%u0435%u0441%u044C%20%u0442%u0435%u043A%u0441%u0442%5CnShift+%u041F%3A%20%u0420%u0435%u0434%u0430%u043A%u0442%u0438%u0440%u043E%u0432%u0430%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5Cn%5Cn%u0422%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%3A%20%5Cn%22%20+%20array.join%28%22%5Cn%22%29%3B%0A%7D%3B%0A%0A%0A//%20%u0423%u0441%u0442%u0430%u043D%u043E%u0432%u0438%u0442%u044C%20%u043D%u0443%u0436%u043D%u0443%u044E%20%u0438%u043A%u043E%u043D%u043A%u0443%20%u043A%u043D%u043E%u043F%u043A%u0438%20%u043F%u0440%u0438%20%u0441%u0442%u0430%u0440%u0442%u0435%20%u0431%u0440%u0430%u0443%u0437%u0435%u0440%u0430%20%u0438%u043B%u0438%20%u043F%u0440%u0438%20%u0438%u0437%u043C%u0435%u043D%u0435%u043D%u0438%u044F%u0445%20%u043D%u0430%u0441%u0442%u0440%u043E%u0435%u043A%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20..........%0Avar%20s%20%3D%20%22CB.autoHighlight%22%3B%0Afunction%20toggleImage%28%29%20%7B%0A%20%20%20self.ownerDocument.getAnonymousElementByAttribute%28self%2C%20%22class%22%2C%20%22toolbarbutton-icon%22%29.%0A%20%20%20src%20%3D%20cbu.getPrefs%28s%29%0A%20%20%20%3F%20self.image%0A%20%20%20%3A%20%22data%3Aimage/x-icon%3Bbase64%2CAAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A/v7+AP39/QH9/f0B/f39Af39/QH///8AmJiYKQAAALEDAwPqAAAA1gAAAGm7u7sA////Afv7+wD///8A////AP39/QH9/f0D////AP///wD7+/sB////AKKiogoAAADJAAAA/wAAAP0BAQH+AAAA/wsLC3HDw8MA////Bfb29gP+/v4A////Aefn5wDT09MJ1tbWCdTU1AT///8AXFxcSwAAAPsEBAT/AAAA/gAAAPwFBQX/AAAA63Nzczvu7u4A/Pz8Avz8/AHr6+sJMjIyZgAAAL8CAgLCAAAApsbGxhhoaGhLAAAA/gUFBf8AAAD+AAAA/wAAAP0DAwP/AAAA6hMTE5eHh4cr3d3dACsrK4gAAAD/AwMD/gMDA/wAAAD/Hh4emy0tLQwAAADwAQEB/wAAAP4AAAD/AAAA/wAAAP8BAQH9AwMD/wAAAOcrKys5AwMD7gYGBv4AAAD7BAQE/QAAAP9KSkp5////ABUVFcIAAAD/AQEB/QAAAP8AAAD/AAAA/wAAAP4BAQH6BwcH/wICAr8BAQGfAAAA/wEBAf8AAADrBQUFeMbGxgX///8AGhoawQAAAP8BAQH+AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD9BgYG66+vrwc1NTVCDw8PPgAAADUREREt0tLSF+7u7gAdHR27AAAA/wICAvsBAQH9AQEB/QEBAf0BAQH8AQEB+wICAv8BAQHC////AsHBwQAICAhzAAAA6AQEBP8AAADqWlpaL25ubj8AAAD4AgIC/wAAAP4AAAD/AAAA/wAAAP4CAgL/AAAA3VtbWznHx8cABgYGbwAAAP8BAQH+AAAA+AUFBf8AAAB18PDwAIyMjDwYGBiuGRkZwxgYGJQTExNzFhYWdQUFBWRgYGAb////ALS0tBwAAADXBQUF/wAAAPgFBQX/AAAA90tLSzunp6cApqamDaurqwP///8A////AP///wD///8A////AP///wD7+/sBtLS0LQAAAOwLCwv/CQkJ+wAAAP88PDyLkJCQDAICAqYAAADdAAAAkJCQkAP///8AW1tbLUlJSXFnZ2cR////AP7+/gHNzc0HDAwMpAAAAP8AAADuJycneRMTEw8PDw/AAQEB/wsLC/8AAAD6cnJyPHR0dB4AAADiAAAA/yAgIJX///8A/v7+Av///wC7u7sPgoKCRYqKii3T09MACgoKaAAAAP8BAQH4AgIC/AAAAP8vLy9GKysreQAAAP8FBQX/BwcH3LS0tAj///8A/f39Af39/QH///8A////AOzs7AYDAwPEAAAA/wAAAPsCAgL/AAAA8mtrawlMTEygAAAA/wgICP8AAADpampqGP///wD///8A/Pz8Afn5+QT+/v4B7e3tBAQEBMEAAAD/AQEB/QAAAP8TExNv////AFJSUosAAAD/AwMD/wEBAauurq4A////Av///wD///8A/v7+AP///wLv7+8AAwMDSwAAAOADAwPZAAAAZZOTkwP///8AT09PIQAAALYAAAC3oaGhIf///wD9/f0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3D%22%3B%0A%7D%3B%0AtoggleImage%28%29%3B%0AgPrefService.addObserver%28s%2C%20toggleImage%2C%20false%29%3B%0AaddDestructor%28%28%29%3D%3E%20gPrefService.removeObserver%28s%2C%20toggleImage%29%29%3B%20%0A%0A%0A//%20%u0410%u0432%u0442%u043E%u043C%u0430%u0442%u0438%u0447%u0435%u0441%u043A%u0438%20%u0432%u044B%u0434%u0435%u043B%u0438%u0442%u044C%20%u0432%u0441%u0435%20%u0438%u0441%u043A%u043E%u043C%u044B%u0435%20%u0442%u0435%u043A%u0441%u0442%u044B%20%u0440%u0430%u0437%u043D%u044B%u043C%u0438%20%u0446%u0432%u0435%u0442%u0430%u043C%u0438%20..........%0Afunction%20doHighlight%28node%20%3D%20content.document.body%29%20%7B%0A%20%20%20if%20%28%20%21cbu.getPrefs%28s%29%20%7C%7C%20%21node%29%20return%3B%0A%20%20%20%0A%20%20%20//%20%u0435%u0441%u043B%u0438%20%u044D%u0442%u043E%20%u043D%u0435%20%u043F%u0443%u0441%u0442%u043E%u0439%20%u0442%u0435%u043A%u0441%u0442%u043E%u0432%u043E%u0439%20%u0443%u0437%u0435%u043B%0A%20%20%20if%20%28%20node.nodeName%20%3D%3D%20%22%23text%22%20%26%26%20node.data.replace%28/%5E%5Cs+%7C%5Cs+%24/g%2C%22%22%29%20%21%3D%20%22%22%20%29%20%7B%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20data%20%3D%20node.data.toLowerCase%28%29%2C%20highlightText%20%3D%20getArray%28%29%3B%20%0A%20%20%20%20%20%20%20%20var%20arr%20%3D%20highlightText.filter%28str%3D%3E%20%7B%20return%20%7Edata.indexOf%28str%29%20%7D%29%3B%0A%0A%20%20%20%20%20%20%20%20if%20%28%20arr%20%21%3D%20%22%22%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20arr.reverse%28%29.forEach%28str%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20parentNode%20%3D%20node.parentNode%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20color%20%3D%20colors%5BhighlightText.indexOf%28str%29%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%20parentNode.className%20%3D%3D%20_id%20%29%20%7B%20//%20%u0435%u0441%u043B%u0438%20%u043D%u0430%u0434%20%u0442%u0435%u043A%u0441%u0442%u043E%u0432%u044B%u043C%20%u0443%u0437%u043B%u043E%u043C%20%u043A%u043D%u043E%u043F%u043A%u0430%20%u0443%u0436%u0435%20%u0441%u043E%u0437%u0434%u0430%u043B%u0430%20span%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20parentNode.style.backgroundColor%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20span%20%3D%20content.document.createElement%28%22span%22%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20span.style.backgroundColor%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20span.className%20%3D%20_id%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20middle%20%3D%20node.splitText%28data.indexOf%28str%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20middle.splitText%28str.length%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20node.parentNode.insertBefore%28span%2C%20middle%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20span.appendChild%28middle%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20else%20if%20%28%20node.nodeType%20%3D%3D%201%20%26%26%20node.firstChild%20%26%26%20%21/script%7Cstyle/.test%28node.localName%29%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B...node.childNodes%5D.forEach%28node%3D%3E%20doHighlight%28node%29%20%29%3B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%7D%3B%0AdoHighlight%28%29%3B%0A%0A%0A//%20%u041E%u0431%u0440%u0430%u0431%u043E%u0442%u0447%u0438%u043A%20%u0441%u043B%u0435%u0434%u0438%u0442%20%u0437%u0430%20%u0437%u0430%u0433%u0440%u0443%u0437%u043A%u043E%u0439%20%u0438%20%u043F%u0435%u0440%u0435%u043A%u043B%u044E%u0447%u0435%u043D%u0438%u0435%u043C%20%u0432%u043A%u043B%u0430%u0434%u043A%u0438%20%u0438%20%u0437%u0430%u043F%u0443%u0441%u043A%u0430%u044E%u0442%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%20..........%0Avar%20listener%20%3D%20%7B%20%0A%20%20%20onStateChange%3A%20function%28wpr%2C%20req%2C%20flag%29%20%7B%0A%20%20%20%20%20%20if%20%28%20gBrowser.currentURI.spec.startsWith%28%22http%22%29%20%26%26%20flag%20%26%20Ci.nsIWebProgressListener.STATE_STOP%20%29%0A%20%20%20%20%20%20%20%20%20%20%20setTimeout%28%28%29%3D%3E%20doHighlight%28%29%2C%200%29%3B%0A%20%20%20%7D%2C%0A%20%20%20%20%20%20%0A%20%20%20onLocationChange%3A%20function%28wpr%2C%20req%29%20%7B%0A%20%20%20%20%20%20if%20%28%20gBrowser.currentURI.spec.startsWith%28%22http%22%29%20%26%26%20%21req%20%26%26%20wpr.document.readyState%20%3D%3D%20%22complete%22%20%29%0A%20%20%20%20%20%20%20%20%20%20%20setTimeout%28%28%29%3D%3E%20doHighlight%28%29%2C%200%29%3B%0A%20%20%20%7D%20%20%20%20%20%20%20%0A%7D%3B%0AgBrowser.addProgressListener%28listener%29%3B%0AaddDestructor%28%28%29%3D%3E%20gBrowser.removeProgressListener%28listener%29%20%29%3B%0A%0A%0A//%20%u041F%u0435%u0440%u0435%u0431%u0440%u0430%u0442%u044C%20%u0432%u0441%u0435%20%u043E%u0442%u043A%u0440%u044B%u0442%u044B%u0435%20%u0441%u0442%u0440%u0430%u043D%u0438%u0446%u044B%20%u0438%20%u0443%u0434%u0430%u043B%u0438%u0442%u044C%20%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u0435%20..........%0Afunction%20clearHighlight%28reason%20%3D%20false%29%20%7B%0A%20%20%20if%20%28%20reason%20%3D%3D%20%27destructor%27%20%29%20return%3B%0A%20%20%20%0A%20%20%20for%20%28%20var%20br%20of%20gBrowser.browsers%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%5B...br.contentDocument.querySelectorAll%28%27SPAN%5Bclass%3D%27%20+%20_id%20+%20%27%5D%27%29%5D%0A%20%20%20%20%20%20%20%20%20.forEach%28el%3D%3E%20el.style%20%3D%20%22%22%20%29%3B%0A%20%20%20%20%20%20%20%20%20%7D%0A%7D%3B%20%0AaddDestructor%28clearHighlight%29%3B%0A%0A%20%0A//%20%u0414%u043E%u0431%u0430%u0432%u0438%u0442%u044C%20%u0438%u043B%u0438%20%u0443%u0434%u0430%u043B%u0438%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20..........%0Afunction%20toggleHighlightValue%28arg%29%20%7B%20%0A%20%20if%20%28%20arg%20%3D%3D%20%27clear%27%20%29%0A%20%20%20%20%20%20%20var%20text%20%3D%20%27%27%2C%20notification%20%3D%20%22%u0422%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20%u0443%u0434%u0430%u043B%u0451%u043D%22%3B%0A%20%20%0A%20%20if%20%28%20arg%20%3D%3D%20%27add%27%20%29%20%7B%20%20%0A%20%20%20%20%20%20%20var%20sel%20%3D%20getSelect%28%29%2C%20array%20%3D%20getArray%28%29%3B%20%0A%20%20%20%20%20%20%20var%20array%20%3D%20%28array%20%3D%3D%20%22%22%29%20%3F%20%22%22%20%3A%20array.join%28%22%5Cn%22%29%20+%20%22%5Cn%22%3B%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20if%20%28%20sel%20%29%20%7B%20var%20text%20%3D%20array%20+%20sel%20%7D%0A%20%20%20%20%20%20%20else%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20val%20%3D%20custombuttons.promptBox%28self.label%2C%20%27B%u0432%u0435%u0434%u0438%u0442%u0435%20%u0442%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%3A%27%29%5B1%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%20val%20%3D%3D%20%22%22%20%29%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20text%20%3D%20array%20+%20val.split%28%22%7C%22%29.join%28%22%5Cn%22%29%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20goDoCommand%28%22cmd_selectNone%22%29%3B%0A%20%20%20%20%20%20%20var%20notification%20%3D%20%22%u0414%u043E%u0431%u0430%u0432%u0438%u043B%20%u0442%u0435%u043A%u0441%u0442%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%3A%20%5Cn%22%20+%20%28sel%20%3F%20sel%20%3A%20val.replace%28/%5C%7C/g%2C%22%5Cn%22%29%29%3B%0A%20%20%20%20%20%20%20%7D%0A%20%20%20%0A%20%20saveTextToHelp%28text.toLowerCase%28%29%29%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%0A%20%20cbu.setPrefs%28s%2C%20%28arg%20%3D%3D%20%27add%27%29%29%3B%0A%20%20cbu.getPrefs%28s%29%20%3F%20doHighlight%28%29%20%3A%20clearHighlight%28%29%3B%0A%20%20%0A%20%20var%20alertsService%20%3D%20Cc%5B%22@mozilla.org/alerts-service%3B1%22%5D.getService%28Ci.nsIAlertsService%29%3B%0A%20%20alertsService.showAlertNotification%28self.image%2C%20self.label%2C%20notification%29%3B%0A%20%20setTimeout%28%28%29%3D%3E%20alertsService.closeAlert%28%29%2C%201500%29%3B%0A%7D%3B%0A%0A%0A//%20%u0417%u0430%u043F%u0438%u0441%u0430%u0442%u044C%20%u0442%u0435%u043A%u0441%u0442%20%u0432%20%u0432%u043A%u043B%u0430%u0434%u043A%u0443%20%27%u0421%u043F%u0440%u0430%u0432%u043A%u0430%27%20..........%0Afunction%20saveTextToHelp%28text%29%20%7B%0A%20%20%20var%20dir%20%3D%20Services.dirsvc.get%28%22ProfD%22%2C%20Ci.nsIFile%29%3B%0A%20%20%20dir.initWithPath%28dir.path%20+%20%22%5C%5Cextensions%5C%5Ccustombuttons@xsms.org%5C%5Ccomponents%5C%5CCustomButtonsService.js%22%29%3B%0A%20%20%20var%20url%20%3D%20Services.io.newFileURI%28dir%29.spec%2C%20cbs%20%3D%20%7B%7D%3B%0A%20%20%20Services.scriptloader.loadSubScript%28url%2C%20cbs%29%3B%0A%20%20%20var%20AO%20%3D%20new%20cbs.AppObject%28%29%2C%20btn%20%3D%20AO.getButton%28_id%29%3B%0A%0A%20%20%20self.setAttribute%28%22Help%22%2C%20text%29%3B%0A%20%20%20btn.setAttribute%28%22Help%22%2C%20text%29%3B%0A%20%20%20AO.overlay.saveOverlayToProfile%28%29%3B%20%20%20%0A%7D%3B%0A%0A%0A//%20%u041F%u043E%u043B%u0443%u0447%u0438%u0442%u044C%20%u0438%u0437%20%u0432%u043A%u043B%u0430%u0434%u043A%u0438%20%27%u0421%u043F%u0440%u0430%u0432%u043A%u0430%27%20%u043C%u0430%u0441%u0441%u0438%u0432%20%u0441%20%u0442%u0435%u043A%u0441%u0442%u0430%u043C%u0438%20%u0434%u043B%u044F%20%u0430%u0432%u0442%u043E%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u0438%u044F%20..........%0Afunction%20getArray%28%29%20%7B%0A%20%20%20return%20%28custombutton.buttonGetHelp%28self%29%29.toLowerCase%28%29.split%28%22%5Cn%22%29.filter%28el%3D%3E%20/%5CS/.test%28el%29%29%3B%0A%7D%3B%0A%0A%0A//%20%u041F%u043E%u043B%u0443%u0447%u0438%u0442%u044C%20%u0432%u044B%u0434%u0435%u043B%u0435%u043D%u043D%u044B%u0439%20%u0442%u0435%u043A%u0441%u0442%20%u0438%u0437%20%u0441%u0442%u0440%u0430%u043D%u0438%u0446%u044B%20%u0438%u043B%u0438%20false%20..........%0Afunction%20getSelect%28%29%20%7B%0A%20%20%20var%20el%20%3D%20document.commandDispatcher.focusedElement%3B%0A%20%20%20try%20%7B%20return%20el.value.substring%28el.selectionStart%2C%20el.selectionEnd%29%20%7D%20catch%28e%29%20%7B%7D%3B%0A%20%20%20var%20sel%20%3D%20document.commandDispatcher.focusedWindow.getSelection%28%29%3B%0A%20%20%20return%20%28sel%20%3D%3D%20%27%27%29%20%3F%20false%20%3A%20sel.toString%28%29.replace%28/%5E%5Cs+%7C%5Cs+%24/g%2C%22%22%29%3B%0A%7D%3B%5D%5D%3E%3C/initcode%3E%0A%20%20%3Ccode%3E%3C%21%5BCDATA%5B/*CODE*/%5D%5D%3E%3C/code%3E%0A%20%20%3Caccelkey%3E%3C%21%5BCDATA%5B%5D%5D%3E%3C/accelkey%3E%0A%20%20%3Chelp%3E%3C%21%5BCDATA%5B%5D%5D%3E%3C/help%3E%0A%20%20%3Cattributes/%3E%0A%3C/custombutton%3E

иконка

Выделить код

Код:

data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD+//4B////APX69AV9vWaPVJsR9GabDPiQrDGn6OzRG////wD+/v0B////AP///gL///0B////AP///wD///8A////AP3+/gP///8ActGnhRyqVP83q0r8PqMu/kqZDf9yoh7Wu8t8YNjan07m4bM9+ffqCf///wD///4B////AP///wD8/v8C//7/AOL5/A4d1NrUFc/G/yHEov0qt3b/NKtM/UGeHv9WlAD/eJcB/peZAP+nphzH5+bAIP///wD+/v8A/fz/A/3+/wH///8A5/b+CyG8/c4Jz/z/ENfm/RrLuf8nu4b/M61S/UGgJvtbnBH8fqAQ/IKXAP+lrjax/v76AP7+/gH///8A7fD9CNni/Bb///8Ac7/4dQCn9/8P1f38ENbh/xzIsf8ouoD/NKtL/0GeHf9enA79dJ0M/4yiGOrx8d0c/f3/AJaT7105O+TOHTjn5GuP8n7u9v4AOK/3ugW/+/8N2vX9FdDN/yDDoP8rtXD/N6c//kqdGPpcnBD/dqIZ3/L05BCVht5rLhXE/zMj0f0nKuL/EkHp7+Dm/Bq64/wyBKz4/w/W/fwP1+X/Gsq4/ya+kPwwsWH+M58o/jSSAP+exXNr////AFkwrepXMrT8SjPG9icg2v8xRunM8fX+BfL5/gcdpPbOBMH8/w/b9vwX0dH+FcKk/yS3evFZum2rpdOaUPj79gD///8BZy6R8lsqn/8/Hbb/NSfS6MPE+DDj7PwI8vP9ALHe/DQXs/nWAs/9/wDV5v9C1Mmwye/jI////wD///8B////AP7+/gG/oclQgVSpuIhuy4/OyfIheHbpgx815+4vZ+7C2uf8Fuz5/we+8P83y/f8Jvb9/QL///8A8vv4CZPUpGO837Y5////AP///wD6+f0D////AIVy1IsgEsz/Lini+Qow5/+Gq/Vq////AM/t/Ruo6/4+6/z9C////wB817lzE6dQ/zGeJfOs05lI/Pv9Bf///wDXy+knTCOu7UYvxf89KtH4Fhrh/4yb9GTB3/sjCJH07QC9+v854Pio9///AFHOsrMktnX/NKtO/0KoPsz9/P0D////AMey2ElWJaL/UzG4+k0vv/4sGs/5vLn1Mkl68aoLcvD/G6j4+QDH+v+29f8xY9zUkA/Eo/8swpH3JL6M/P38/gL///8A08DcNF8kkPlgNar9Sh2m/3texp/i4PsPKTDk7hxP7P8eefD7Apb1/47Z/E2x8vw2AMrv/wXN5/8O0Ova////AP///wH8+/wAoHe0iWkukfN+U66v8+31C9PL7iA3H8n6KSzi/yNF6/sMVe3/rc35PP///wB/zv1qM7L7u53e/j7///8A////AP///wD7+fsB6NzqD/n2+gT///8A7uf0EV80rONFKcD/LCTU/zxC47/u8P4D////Af7+/gH//f4A///+AP///wD///8A//7/AP79/gL///8A/v7/Av38/gT///8AsZHEZmAplv5gOrPQzMbwKf///wD9/f4C/f//A/7//wL9/v8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

инициализация

Выделить код

Код:

// Настройка функций кликов мыши для кнопки ..........
this.onclick =e=> {
   
   // действие при клике ЛКМ ....
   if ( e.button == 0 ) {
        cbu.setPrefs(s, !cbu.getPrefs(s));
        cbu.getPrefs(s) ? doHighlight() : clearHighlight();
        }
        
   // действие при клике СКМ ....    
   if ( e.button == 1 ) toggleHighlightValue('add');
     
   // действие при клике ПКМ ....    
   if ( e.button == 2 && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey ) { 
        e.preventDefault();  
        toggleHighlightValue('clear');
        }
   
   // действие при клике Ctrl + ПКМ .... .  
   if ( e.button == 2 && !e.ctrlKey && e.shiftKey && !e.altKey && !e.metaKey ) { 
        e.preventDefault();
        
        // открыть редактор кнопки в вкладке Справка
        function observer(subject) {
           subject.addEventListener("load", (e, doc = e.target)=> {
              if ( doc.URL.startsWith("chrome://custombuttons") ) {
                   Services.ww.unregisterNotification(observer);
                   doc.getElementsByTagName("tabs")[0].children[2].click();
                   }
           });
        };
        Services.ww.registerNotification(observer);

        custombuttons.editButton(this); // открыть редактор
        } 
};


// Цвета для автовыделение, можно менять и добавлять ..........
var colors = ["#FFFF00", "#FF0000", "#66FFFF", "#66FF99", "#FFA500", "#FF00FF"];


// Подсказка для кнопки ...........
this.onmouseover =()=> {
   var array = getArray().map(str=> { return str.slice(0,50) + (str.length > 50 ? "..." : "") });
   this.tooltipText = self.label + "\nЛ: Bключить/выключить \nС: Добавить текст\nП: Сбросить весь текст\nShift+П: Редактировать текст\
                      \n\nТекст для автовыделения: \n" + array.join("\n");
};


// Установить нужную иконку кнопки при старте браузера или при изменениях настроек автовыделения ..........
var s = "CB.autoHighlight";
function toggleImage() {
   self.ownerDocument.getAnonymousElementByAttribute(self, "class", "toolbarbutton-icon").
   src = cbu.getPrefs(s)
   ? self.image
   : "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAD///8A/v7+AP39/QH9/f0B/f39Af39/QH///8AmJiYKQAAALEDAwPqAAAA1gAAAGm7u7sA////Afv7+wD///8A////AP39/QH9/f0D////AP///wD7+/sB////AKKiogoAAADJAAAA/wAAAP0BAQH+AAAA/wsLC3HDw8MA////Bfb29gP+/v4A////Aefn5wDT09MJ1tbWCdTU1AT///8AXFxcSwAAAPsEBAT/AAAA/gAAAPwFBQX/AAAA63Nzczvu7u4A/Pz8Avz8/AHr6+sJMjIyZgAAAL8CAgLCAAAApsbGxhhoaGhLAAAA/gUFBf8AAAD+AAAA/wAAAP0DAwP/AAAA6hMTE5eHh4cr3d3dACsrK4gAAAD/AwMD/gMDA/wAAAD/Hh4emy0tLQwAAADwAQEB/wAAAP4AAAD/AAAA/wAAAP8BAQH9AwMD/wAAAOcrKys5AwMD7gYGBv4AAAD7BAQE/QAAAP9KSkp5////ABUVFcIAAAD/AQEB/QAAAP8AAAD/AAAA/wAAAP4BAQH6BwcH/wICAr8BAQGfAAAA/wEBAf8AAADrBQUFeMbGxgX///8AGhoawQAAAP8BAQH+AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD9BgYG66+vrwc1NTVCDw8PPgAAADUREREt0tLSF+7u7gAdHR27AAAA/wICAvsBAQH9AQEB/QEBAf0BAQH8AQEB+wICAv8BAQHC////AsHBwQAICAhzAAAA6AQEBP8AAADqWlpaL25ubj8AAAD4AgIC/wAAAP4AAAD/AAAA/wAAAP4CAgL/AAAA3VtbWznHx8cABgYGbwAAAP8BAQH+AAAA+AUFBf8AAAB18PDwAIyMjDwYGBiuGRkZwxgYGJQTExNzFhYWdQUFBWRgYGAb////ALS0tBwAAADXBQUF/wAAAPgFBQX/AAAA90tLSzunp6cApqamDaurqwP///8A////AP///wD///8A////AP///wD7+/sBtLS0LQAAAOwLCwv/CQkJ+wAAAP88PDyLkJCQDAICAqYAAADdAAAAkJCQkAP///8AW1tbLUlJSXFnZ2cR////AP7+/gHNzc0HDAwMpAAAAP8AAADuJycneRMTEw8PDw/AAQEB/wsLC/8AAAD6cnJyPHR0dB4AAADiAAAA/yAgIJX///8A/v7+Av///wC7u7sPgoKCRYqKii3T09MACgoKaAAAAP8BAQH4AgIC/AAAAP8vLy9GKysreQAAAP8FBQX/BwcH3LS0tAj///8A/f39Af39/QH///8A////AOzs7AYDAwPEAAAA/wAAAPsCAgL/AAAA8mtrawlMTEygAAAA/wgICP8AAADpampqGP///wD///8A/Pz8Afn5+QT+/v4B7e3tBAQEBMEAAAD/AQEB/QAAAP8TExNv////AFJSUosAAAD/AwMD/wEBAauurq4A////Av///wD///8A/v7+AP///wLv7+8AAwMDSwAAAOADAwPZAAAAZZOTkwP///8AT09PIQAAALYAAAC3oaGhIf///wD9/f0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
toggleImage();
gPrefService.addObserver(s, toggleImage, false);
addDestructor(()=> gPrefService.removeObserver(s, toggleImage)); 


// Автоматически выделить все искомые тексты разными цветами ..........
function doHighlight(node = content.document.body) {
   if ( !cbu.getPrefs(s) || !node) return;
   
   // если это не пустой текстовой узел
   if ( node.nodeName == "#text" && node.data.replace(/^\s+|\s+$/g,"") != "" ) {
        
        var data = node.data.toLowerCase(), highlightText = getArray(); 
        var arr = highlightText.filter(str=> { return ~data.indexOf(str) });

        if ( arr != "" ) {
             arr.reverse().forEach(str=> {
                var parentNode = node.parentNode;
                var color = colors[highlightText.indexOf(str)];
                
                if ( parentNode.className == _id ) { // если над текстовым узлом кнопка уже создала span
                     parentNode.style.backgroundColor = color;
                     return;
                     }
                     
                var span = content.document.createElement("span");
                span.style.backgroundColor = color;
                span.className = _id;

                var middle = node.splitText(data.indexOf(str));
                middle.splitText(str.length);
                node.parentNode.insertBefore(span, middle);
                span.appendChild(middle);
             });
             }
        }
    else if ( node.nodeType == 1 && node.firstChild && !/script|style/.test(node.localName) ) {
              [...node.childNodes].forEach(node=> doHighlight(node) ); 
              }
};
doHighlight();


// Обработчик следит за загрузкой и переключением вкладки и запускают автовыделение ..........
var listener = { 
   onStateChange: function(wpr, req, flag) {
      if ( gBrowser.currentURI.spec.startsWith("http") && flag & Ci.nsIWebProgressListener.STATE_STOP )
           setTimeout(()=> doHighlight(), 0);
   },
      
   onLocationChange: function(wpr, req) {
      if ( gBrowser.currentURI.spec.startsWith("http") && !req && wpr.document.readyState == "complete" )
           setTimeout(()=> doHighlight(), 0);
   }       
};
gBrowser.addProgressListener(listener);
addDestructor(()=> gBrowser.removeProgressListener(listener) );


// Перебрать все открытые страницы и удалить выделение ..........
function clearHighlight(reason = false) {
   if ( reason == 'destructor' ) return;
   
   for ( var br of gBrowser.browsers ) {
         [...br.contentDocument.querySelectorAll('SPAN[class=' + _id + ']')]
         .forEach(el=> el.style = "" );
         }
}; 
addDestructor(clearHighlight);

 
// Добавить или удалить текст для автовыделения ..........
function toggleHighlightValue(arg) { 
  if ( arg == 'clear' )
       var text = '', notification = "Текст для автовыделения удалён";
  
  if ( arg == 'add' ) {  
       var sel = getSelect(), array = getArray(); 
       var array = (array == "") ? "" : array.join("\n") + "\n";     

       if ( sel ) { var text = array + sel }
       else 
            {
            var val = custombuttons.promptBox(self.label, 'Bведите текст для автовыделения:')[1];
            if ( val == "" ) return;
            var text = array + val.split("|").join("\n");                
            }
       goDoCommand("cmd_selectNone");
       var notification = "Добавил текст для автовыделения: \n" + (sel ? sel : val.replace(/\|/g,"\n"));
       }
   
  saveTextToHelp(text.toLowerCase());                       
  
  cbu.setPrefs(s, (arg == 'add'));
  cbu.getPrefs(s) ? doHighlight() : clearHighlight();
  
  var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
  alertsService.showAlertNotification(self.image, self.label, notification);
  setTimeout(()=> alertsService.closeAlert(), 1500);
};


// Записать текст в вкладку 'Справка' ..........
function saveTextToHelp(text) {
   var dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
   dir.initWithPath(dir.path + "\\extensions\\custombuttons@xsms.org\\components\\CustomButtonsService.js");
   var url = Services.io.newFileURI(dir).spec, cbs = {};
   Services.scriptloader.loadSubScript(url, cbs);
   var AO = new cbs.AppObject(), btn = AO.getButton(_id);

   self.setAttribute("Help", text);
   btn.setAttribute("Help", text);
   AO.overlay.saveOverlayToProfile();   
};


// Получить из вкладки 'Справка' массив с текстами для автовыделения ..........
function getArray() {
   return (custombutton.buttonGetHelp(self)).toLowerCase().split("\n").filter(el=> /\S/.test(el));
};


// Получить выделенный текст из страницы или false ..........
function getSelect() {
   var el = document.commandDispatcher.focusedElement;
   try { return el.value.substring(el.selectionStart, el.selectionEnd) } catch(e) {};
   var sel = document.commandDispatcher.focusedWindow.getSelection();
   return (sel == '') ? false : sel.toString().replace(/^\s+|\s+$/g,"");
};

Отсутствует

 

№1594309-10-2021 16:03:56

Алексей78
Участник
 
Группа: Members
Откуда: Беларусь г.Витебск
Зарегистрирован: 01-05-2007
Сообщений: 179
UA: Firefox 93.0

Re: Custom Buttons

Всем привет!
Ребята, сразу прошу прощения, возможно этот вопрос уже поднимался, а я просто не внимательно эту тему читал. Есть скрипт вставки скопированного текста по мидл клик, описан в посте https://forum.mozilla-russia.org/viewto … 32#p778032, так вот с недавних пор он перестал у меня работать. Вставка по мидл клик работает, но если в строке поиска есть уже текст, то выделение его и вставка нового по мидл клик не срабатывает - старый текст не удаляется, а просто дополняется новым из буфера обмена. Может кто в курсе как заставить скрипт снова работать?

Отредактировано Алексей78 (09-10-2021 16:06:10)

Отсутствует

 

№1594409-10-2021 17:54:16

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
UA: Firefox 78.0

Re: Custom Buttons

Алексей78 пишет

возможно этот вопрос уже поднимался, а я просто не внимательно эту тему читал

Да нет, вроде никто не спрашивал.
Я когда-то давно его в jsm'ку перекладывал, а затем родили баг :usch:,
и пришлось на mousedown переделывать.


Вобщем, сейчас такой. В новом UCF, импортировать созданный модуль
можно добавив в конфигурационный CustomStylesScripts.jsm
в массив UcfStylesScripts.scriptsbackground объект вида
{ func: 'ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/MMPaste.jsm");' },

MMPaste.jsm

Выделить код

Код:

var MMPasteChild, EXPORTED_SYMBOLS = ["MMPasteChild"];
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

var sel = ":is(textarea,input):not([readonly],[disabled])";
var x, y, sn = Ci.nsISelectionController.SELECTION_NORMAL;
var inRect = r => y > r.top && y < r.bottom && x < r.right && x > r.left;

var mousedown = e => {
	if (e.button != 1) return;
	var trg = (e.originalTarget || e.target).closest(sel);
	if (!trg) return;

	var ed = trg.editor;
	if (!ed || ed.selection.isCollapsed || !ed.canPaste(sn)) return;

	x = e.clientX; y = e.clientY;
	var rng = ed.selection.getRangeAt(0);
	if (!inRect(rng.getBoundingClientRect())) return;

	var list = rng.getClientRects();
	if (list.length == 1 || Array.from(list).some(inRect))
		ed.paste(sn),
		trg.ownerGlobal.addEventListener(...args);
}
var args = ["auxclick", e => e.preventDefault(), {once: true, capture: true}];

if (ChromeUtils.domProcessChild.childID)
	(MMPasteChild = class extends JSWindowActorChild {}).prototype.handleEvent = mousedown;

else ((pref, gmon) => ({
	mousedown,
	get pref() {
		return Services.prefs.getBoolPref(pref);
	},
	startup() {
		this.handleEvent = e => this[e.type](e);
		this.unload = e => this.listen(e.target.ownerGlobal);
		this.e10s = Services.appinfo.browserTabsRemoteAutostart;

		Services.prefs.addObserver(pref, this);
		Services.obs.addObserver(this, "quit-application-granted", false);
		this.pref && this.init();
		this.gmon();
	},
	async gmon() {
		Cu.importGlobalProperties(["fetch"]);
		var url = "chrome://custombuttons/content/editExternal.js";
		try {var src = await (await fetch(url)).text();} catch {return;}
		src = src.replace(/function gmon_edit_mouseclick[^}]+?}/, gmon);
		var arr = [["override", url, "data:," + encodeURIComponent(src)]];
		url = Services.io.getProtocolHandler("resource").getSubstitution("gre");
		this.gmonHelper = Cc["@mozilla.org/addons/addon-manager-startup;1"]
			.getService(Ci.amIAddonManagerStartup).registerChrome(url, arr);
	},
	shutdown() {
		this.pref && this.destroy();
		Services.prefs.removeObserver(pref, this);
		Services.obs.removeObserver(this, "quit-application-granted");
		this.gmonHelper?.destruct();
	},
	opts: {
		allFrames: true,
		remoteTypes: ["w", "file", "extension", "privileged"],
		child: {moduleURI: __URI__, events: {mousedown: {capture: true}}}
	},
	init() {
		this.e10s && ChromeUtils.registerWindowActor("MMPaste", this.opts);
		Services.obs.addObserver(this, "widget-first-paint", false);
		this.wins("addEventListener");
	},
	destroy() {
		this.e10s && ChromeUtils.unregisterWindowActor("MMPaste");
		Services.obs.removeObserver(this, "widget-first-paint");
		this.wins();
	},
	observe(subj, topic, data) {
		var char = topic[0];
		if (char == "w") return this.listen(subj, "addEventListener");
		if (char == "q") return this.shutdown();
		data == pref && this[this.pref ? "init" : "destroy"]();
	},
	wins(arg) {
		for(var win of Services.wm.getEnumerator(null)) this.listen(win, arg);
	},
	listen(trg, meth = "removeEventListener") {
		trg[meth]("mousedown", this, true);
		trg[meth]("unload", this);
	}
}).startup())("middlemouse.paste", `function gmon_edit_mouseclick(e) {
	var mmp = Cc["@mozilla.org/preferences-service;1"]
		.getService(Ci.nsIPrefBranch).getBoolPref.bind(null, "middlemouse.paste");
	(gmon_edit_mouseclick = e => e.button != 1 || mmp() || edittarget(e.target))(e);
}`);

Отсутствует

 

№1594509-10-2021 20:09:57

Алексей78
Участник
 
Группа: Members
Откуда: Беларусь г.Витебск
Зарегистрирован: 01-05-2007
Сообщений: 179
UA: Firefox 93.0

Re: Custom Buttons

Dumby пишет
Алексей78 пишет

возможно этот вопрос уже поднимался, а я просто не внимательно эту тему читал

Да нет, вроде никто не спрашивал.
Я когда-то давно его в jsm'ку перекладывал, а затем родили баг :usch:,
и пришлось на mousedown переделывать.


Вобщем, сейчас такой. В новом UCF, импортировать созданный модуль
можно добавив в конфигурационный CustomStylesScripts.jsm
в массив UcfStylesScripts.scriptsbackground объект вида
{ func: 'ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/MMPaste.jsm");' },

MMPaste.jsm

Выделить код

Код:

var MMPasteChild, EXPORTED_SYMBOLS = ["MMPasteChild"];
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

var sel = ":is(textarea,input):not([readonly],[disabled])";
var x, y, sn = Ci.nsISelectionController.SELECTION_NORMAL;
var inRect = r => y > r.top && y < r.bottom && x < r.right && x > r.left;

var mousedown = e => {
	if (e.button != 1) return;
	var trg = (e.originalTarget || e.target).closest(sel);
	if (!trg) return;

	var ed = trg.editor;
	if (!ed || ed.selection.isCollapsed || !ed.canPaste(sn)) return;

	x = e.clientX; y = e.clientY;
	var rng = ed.selection.getRangeAt(0);
	if (!inRect(rng.getBoundingClientRect())) return;

	var list = rng.getClientRects();
	if (list.length == 1 || Array.from(list).some(inRect))
		ed.paste(sn),
		trg.ownerGlobal.addEventListener(...args);
}
var args = ["auxclick", e => e.preventDefault(), {once: true, capture: true}];

if (ChromeUtils.domProcessChild.childID)
	(MMPasteChild = class extends JSWindowActorChild {}).prototype.handleEvent = mousedown;

else ((pref, gmon) => ({
	mousedown,
	get pref() {
		return Services.prefs.getBoolPref(pref);
	},
	startup() {
		this.handleEvent = e => this[e.type](e);
		this.unload = e => this.listen(e.target.ownerGlobal);
		this.e10s = Services.appinfo.browserTabsRemoteAutostart;

		Services.prefs.addObserver(pref, this);
		Services.obs.addObserver(this, "quit-application-granted", false);
		this.pref && this.init();
		this.gmon();
	},
	async gmon() {
		Cu.importGlobalProperties(["fetch"]);
		var url = "chrome://custombuttons/content/editExternal.js";
		try {var src = await (await fetch(url)).text();} catch {return;}
		src = src.replace(/function gmon_edit_mouseclick[^}]+?}/, gmon);
		var arr = [["override", url, "data:," + encodeURIComponent(src)]];
		url = Services.io.getProtocolHandler("resource").getSubstitution("gre");
		this.gmonHelper = Cc["@mozilla.org/addons/addon-manager-startup;1"]
			.getService(Ci.amIAddonManagerStartup).registerChrome(url, arr);
	},
	shutdown() {
		this.pref && this.destroy();
		Services.prefs.removeObserver(pref, this);
		Services.obs.removeObserver(this, "quit-application-granted");
		this.gmonHelper?.destruct();
	},
	opts: {
		allFrames: true,
		remoteTypes: ["w", "file", "extension", "privileged"],
		child: {moduleURI: __URI__, events: {mousedown: {capture: true}}}
	},
	init() {
		this.e10s && ChromeUtils.registerWindowActor("MMPaste", this.opts);
		Services.obs.addObserver(this, "widget-first-paint", false);
		this.wins("addEventListener");
	},
	destroy() {
		this.e10s && ChromeUtils.unregisterWindowActor("MMPaste");
		Services.obs.removeObserver(this, "widget-first-paint");
		this.wins();
	},
	observe(subj, topic, data) {
		var char = topic[0];
		if (char == "w") return this.listen(subj, "addEventListener");
		if (char == "q") return this.shutdown();
		data == pref && this[this.pref ? "init" : "destroy"]();
	},
	wins(arg) {
		for(var win of Services.wm.getEnumerator(null)) this.listen(win, arg);
	},
	listen(trg, meth = "removeEventListener") {
		trg[meth]("mousedown", this, true);
		trg[meth]("unload", this);
	}
}).startup())("middlemouse.paste", `function gmon_edit_mouseclick(e) {
	var mmp = Cc["@mozilla.org/preferences-service;1"]
		.getService(Ci.nsIPrefBranch).getBoolPref.bind(null, "middlemouse.paste");
	(gmon_edit_mouseclick = e => e.button != 1 || mmp() || edittarget(e.target))(e);
}`);

Спасибо Вам за Ваш ответ! Но к сожалению, я в скриптах как свинья в апельсинах. Не могли бы Вы подробнее описать, как это все сделать. Спасибо :)

Отсутствует

 

№1594609-10-2021 20:28:48

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 93.0

Re: Custom Buttons

Dumby пишет

Да нет, вроде никто не спрашивал.

Я давно собирался спросить, да всё как-то не доходило... :) Спасибо, теперь всё замечательно втыкает.

Отсутствует

 

№1594709-10-2021 21:42:27

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 242
UA: Google 2.1

Re: Custom Buttons

Dumby пишет

тяп-ляп скопировал

Спасибо у меня работает bootstrap-loader.js на 91esr...


Жизнь иногда такое выкидывает, что хочется подобрать...

На форуме

 

№1594809-10-2021 22:01:41

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 93.0

Re: Custom Buttons

Dumby
https://www.kino-teatr.ru/
Не могу поисковик сделать даже кнопкой . Параметр не найду .

Отсутствует

 

№1594910-10-2021 11:26:50

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
UA: Firefox 78.0

Re: Custom Buttons

Алексей78 пишет

Не могли бы Вы подробнее описать, как это все сделать.

Лучше бы наоборот, мне почитать что уже сделано и что не получается.
Ладно, допустим UCF уже развёрнут, обновлён и работает.

скрытый текст

1. Идём в папку %Профиль%\chrome\user_chrome_files\custom_scripts
и создаём там текстовый файл с названием MMPaste.jsm
Расширение чтоб было .jsm, а не какой-нибудь .jsm.txt
Открываем файл на редактирование и заполняем содержимым скрипта,
кодировка подойдёт UTF-8 без BOM, сохраняем.


2. Теперь редактируем файл CustomStylesScripts.jsm (лежит в той же папке).
Добавляем предложенную строку после scriptsbackground: [ в скобки ], сохраняем.
Скриншот.


3. Осталось убедиться, что вкючена настройка middlemouse.paste
и в настройках UCF чтоб стояла галка
Включить скрипты:
[✔] В фоне [System Principal]


Всё. Там же, в настройках, жмём «Перезапустить*», и должно работать
как написано, только вставка не по клику СКМ, а по нажатию.
И в Fission OOP-фреймах теперь тоже должно работать.

ВВП пишет

Не могу поисковик сделать

Да мне особо проверить негде. Попробуй так

скрытый текст

Выделить код

Код:

Services.search.addPolicyEngine({

	iconURL: "data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/8P8A/NL8APm0+QD2lvYA83jzAPBa8ADtPO0A6h7qAOcA5wDPAM8AtwC3AJ8AnwCHAIcAbwBvAFcAVwBAAEAA9Ov/AOjO/ADcsfkA0JT2AMR38wC4WvAArD3tAKAg6gCRAOcAggDPAHMAtwBkAJ8AVQCHAEYAbwA3AFcAKABAAODg/wDExPwAqKj5AIyM9gBwcPMAVFTwADg47QAcHOoAAADnAAAAzwAAALcAAACfAAAAhwAAAG8AAABXAAAAQADg9f8AxOj8AKjc+QCM0PYAcMTzAFS48AA4rO0AHKDqAACR5wAAgs8AAHO3AABknwAAVYcAAEZvAAA3VwAAKEAA4P//AMT8/ACo+fkAjPb2AHDz8wBU8PAAOO3tABzq6gAA5+cAAM/PAAC3twAAn58AAIeHAABvbwAAV1cAAEBAAOD/8ADE/OsAqPnmAIz24QBw89wAVPDXADjt0gAc6s0AAOfJAADPsgAAt5sAAJ+EAACHbQAAb1YAAFc/AABAKADg/+AAxPzEAKj5qACM9owAcPNwAFTwVAA47TgAHOocAADnAAAAzwAAALcAAACfAAAAhwAAAG8AAABXAAAAQAAA8P/gAOv8xADm+agA4faMANzzcADX8FQA0u04AM3qHADJ5wAAss8AAJu3AACEnwAAbYcAAFZvAAA/VwAAKEAAAP//4AD8/MQA+fmoAPb2jADz83AA8PBUAO3tOADq6hwA5+cAAM/PAAC3twAAn58AAIeHAABvbwAAV1cAAEBAAAD/+OAA/PLEAPnsqAD25owA8+BwAPDaVADt1DgA6s4cAOfLAADPtgAAt6EAAJ+MAACHdwAAb2IAAFdNAABANwAA//DgAPzoxAD54KgA9tiMAPPQcADwyFQA7cA4AOq4HADnrgAAz5wAALeKAACfeAAAh2YAAG9UAABXQgAAQDAAAP/o4AD82sQA+cyoAPa+jADzsHAA8KJUAO2UOADqhhwA53QAAM9oAAC3XAAAn1AAAIdEAABvOAAAVywAAEAcAAD/4OAA/MTEAPmoqAD2jIwA83BwAPBUVADtODgA6hwcAOcAAADPAAAAtwAAAJ8AAACHAAAAbwAAAFcAAABAAAAA/+jwAPzL5AD5rtgA9pHMAPN0wADwV7QA7TqoAOodnADnAJEAzwCCALcAcwCfAGQAhwBVAG8ARgBXADcAQAAoAP///wDw8PAA6OjoAODg4ADY2NgA0NDQAMjIyADAwMAAuLi4ALCwsACoqKgAoKCgAJiYmACQkJAAiIiIAICAgACAgIAAcHBwAGhoaABgYGAAWFhYAFBQUABISEgAQEBAADg4OAAwMDAAKCgoACAgIAAYGBgAEBAQAAgICAAAAAAA4ODg4ODg4ODgLS0tLeDg4ODg4ODg4ODg4C0tLS3g4ODg4ODg4ODg4OAdLS0t4ODg4ODg4ODg4ODgLC0tLeDg4ODg4ODg4ODg4C0tLS3g4ODg4ODg4ODg4OAdLS0t4ODg4ODg4ODgLC0tLS0tLS0tLPHx8eDg4P7+/vItLS0tLS3x8fHg4OD+/v7+LS0tLS0t8fHx4O7x8fHx4ODg4ODg4PHx8fHx8fHv4ODg4ODg4ODx8fHx8fHp4ODg4ODg4ODg8fHx8fHx6eDg4ODg4ODg4PHx8fHx8fHx4ODg4ODg4OD+/vHg5fHx8fHx4ODg4ODg8fHx4ODg6fHx8eDg4ODg4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAA=",
	chrome_settings_overrides: {
		search_provider: {
			encoding: "WINDOWS-1251",
			name: "Поиск по сайту - Кино-Театр.Ру",
			search_form: "https://www.kino-teatr.ru/",
			search_url: "https://www.kino-teatr.ru/search",
			search_url_post_params: "text={searchTerms}"
		}
	}
});

Отсутствует

 

№1595010-10-2021 12:15:50

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
UA: Firefox 93.0

Re: Custom Buttons

Dumby
search_url_post_params: "text={searchTerms}"  Ни в одном поисковике нет такого....Encoding то догадался , но это...

Отсутствует

 

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]