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

Общайтесь со знакомыми и друзьями в нашем сообществе в Facebook.

№1632624-04-2022 23:19:47

_zt
Участник
 
Группа: Members
Зарегистрирован: 10-11-2014
Сообщений: 1644
UA: Firefox 99.0

Re: Custom Buttons

Dumby
Спасибо, проблем не видно. Даже ПКМ по пункту работает.

скрытый текст
Только фрагмент стиля:

Выделить код

Код:

#context_autoreloadTab:not([checked]) > menupopup > :first-child {
			display: none !important;
		}

заменил на

Выделить код

Код:

#context_autoreloadTab:not([checked]) > .menu-iconic-left > image {
			fill: gray !important;
			-moz-context-properties: fill !important;
			list-style-image: url("chrome://browser/skin/reload.svg") !important;
		}

а то текст съезжает влево при неактой перезагрузке.

Отсутствует

 

№1632726-04-2022 10:43:18

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

Re: Custom Buttons

Attributes Inspector нет новой версии ?

Отсутствует

 

№1632826-04-2022 12:44:27

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

Re: Custom Buttons

ВВП
Номер версии и ссылка на гитхаб есть в начале кода.


И, чем вызван вопрос?
Версия 0.6.5pre2 работает и в FF 101.0a1 (2022-04-25).

Отредактировано Dumby (26-04-2022 12:46:35)

Отсутствует

 

№1632926-04-2022 13:33:00

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

Re: Custom Buttons

Dumby
В 99 здесь скачет...
https://ibb.co/PrMgt9f
Zoom -по умолчанию больше 100% и все , тогда скачет...
Да, и надписи в СВ , типа ini неровные
s9yo8bc4.jpg

Отредактировано ВВП (26-04-2022 19:41:01)

Отсутствует

 

№1633026-04-2022 20:37:52

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

Re: Custom Buttons

ВВП пишет

Zoom -по умолчанию больше 100% и все , тогда скачет...

Во, так вижу.
Надо бы найти регрессора и отписаться Infocatcher'у, но времени нет.


Предположительный хотфикс: добавить после y = e.screenY;

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

Выделить код

Код:

…
				if(this.fxVersion >= 99) {
					var zoom = e.view.browsingContext.fullZoom;
					x *= zoom;
					y *= zoom;
				}

надписи в СВ , типа ini неровные

Это, надеюсь, сугубо в твоих стилях надо поискать почему неровные.

Отсутствует

 

№1633126-04-2022 23:35:19

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

Re: Custom Buttons

Dumby
Скрипт подрихтовал,норм. Кнопки в СВ в скине есть Tab  и мешало ...сделал так

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

Выделить код

Код:

.tab-middle.box-inherit > .tab-text {
 padding: 1px 6px 2px  8px!important;

}
[data-l10n-id="editor-settingsAccelkey"]{margin-top: 10px !important;}
#accelkey {margin-left: 12px !important; margin-top: 10px !important;}


В 100 скрипты заработали ! Класс !

Отредактировано ВВП (02-05-2022 14:58:35)

Отсутствует

 

№1633202-05-2022 16:59:02

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

Re: Custom Buttons

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


И, код запускатора с поправкой на это.

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

Выделить код

Код:

//
(async xp => {
	var imprt, ids = [
		"custombuttons@xsms.org",
	];
	if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return;

	if (Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
		var {XPIInternal} = (imprt = url => Cu.import(url, {}))(xp);

	else { // Fx 101+
		var g = Cu.getGlobalForObject(Cu), te = new g.TextEncoder();
		var imp = g.ChromeUtils.import, {XPIInternal} = imp(xp);
		var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
		var rph = ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		imprt = (url, id) => {
			var subst = te.encode(id).join("");
			rph.setSubstitution(subst, ios.newURI(url));
			return imp(`resource://${subst}/`);
		}
	}
	var load = async (file, id) => {
		var rootURI = XPIInternal.getURIForResourceInFile(file, "");
		imprt(rootURI.resolve("startup.jsm"), id).start(rootURI);
	}
	var proto = XPIInternal.BootstrapScope.prototype;
	var func = proto._beforeCallBootstrapMethod;

	proto._beforeCallBootstrapMethod = () => {
		proto._beforeCallBootstrapMethod = func;
		for(var {id, loader, file} of XPIInternal.XPIStates.enabledAddons())
			ids.includes(id) && !loader && load(file, id);
	}
})("resource://gre/modules/addons/XPIProvider.jsm");

Отсутствует

 

№1633302-05-2022 17:40:29

vitalii201
Участник
 
Группа: Members
Зарегистрирован: 24-03-2011
Сообщений: 679
UA: Firefox 99.0

Re: Custom Buttons

Т. е. перед установкой …25-fx-bootstrap отредактировать config.js на №16402?
Сейчас, с custom_buttons-0.0.7.0.0.24-fx-bootstrap.xpi:

config.js

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=798621#p798621
try {(nsvo => {
	var g = Cu.getGlobalForObject(nsvo), o = g.Object, {freeze} = o, NEW;
	o.freeze = obj => {
		if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm")
			return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;
		if ((NEW = "MOZ_ALLOW_ADDON_SIDELOAD" in obj))
			lockPref("extensions.experiments.enabled", true);
		else
			obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
			lockPref("extensions.legacy.enabled", true);

		return (o.freeze = freeze)(obj);
	}
	lockPref("xpinstall.signatures.required", false);
	lockPref("extensions.langpacks.signatures.required", false);

	nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {});
	var shouldVerify = nsvo.shouldVerifySignedState;
	if (shouldVerify.length == 1)
		nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);
	else {
		var {Services} = g.ChromeUtils.import("resource://gre/modules/Services.jsm");
		var subst = "pkg-proto-patch-tmp-script";
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		var func = async proto => Object.assign(proto, eval(
			`({${proto.verifySignedState}})`.replace("(!", "(addonId || !")
		));
		var code = encodeURIComponent(`(${func})(Package.prototype);`);
		rph.setSubstitution(subst, Services.io.newURI("data:," + code));
		Services.scriptloader.loadSubScript(`resource://${subst}/`, nsvo);
		rph.setSubstitution(subst, null);
	}
	if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false;
})(
	"permitCPOWsInScope" in Cu
		? Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}) : Cu
);}
catch(ex) {Cu.reportError(ex);}

// bootstrap-loader.js https://forum.mozilla-russia.org/viewtopic.php?pid=795196#p795196
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[data-l10n-id="preferences-addon-button"]').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%0Aif (AddonManager.isReady) {%0A  AddonManager.getAllAddons().then(addons => {%0A    addons.forEach(addon => {%0A      if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) {%0A        addon.reload();%0A      };%0A    });%0A  });%0A}%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);}

// https://forum.mozilla-russia.org/viewtopic.php?pid=780458#p780458
try {({
	ids: [
		"custombuttons@xsms.org",
	],
	init(xrt) {
		if (xrt.inSafeMode) return;
		Cu.import("resource://gre/modules/addons/XPIProvider.jsm", this);
		var load = async file => {
			var rootURI = this.XPIInternal.getURIForResourceInFile(file, "");
			Cu.import(rootURI.resolve("startup.jsm"), {}).start(rootURI);
		}
		var proto = this.XPIInternal.BootstrapScope.prototype;
		var func = proto._beforeCallBootstrapMethod;

		proto._beforeCallBootstrapMethod = () => {
			proto._beforeCallBootstrapMethod = func;
			for(var addon of this.XPIInternal.XPIStates.enabledAddons())
				this.ids.includes(addon.id) && !addon.loader && load(addon.file);
		}
	}
}).init(Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime));}
catch(ex) {Cu.reportError(ex);}


Или где?

Отсутствует

 

№1633402-05-2022 17:51:32

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

Re: Custom Buttons

Dumby
Спасибо!


vitalii201
Весь код не надо редактировать, а только код запускатора CB.


Win7

Отсутствует

 

№1633502-05-2022 18:00:40

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

Re: Custom Buttons

Dumby
Как избавиться от красного ?
oxxsa55d.jpg

Отсутствует

 

№1633602-05-2022 18:35:18

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

Re: Custom Buttons

vitalii201 пишет

Т. е. перед установкой …25-fx-bootstrap отредактировать config.js на №16402?
Сейчас, с custom_buttons-0.0.7.0.0.24-fx-bootstrap.xpi

Не-не.
Если используется bootstrap, то запускатор не нужен,
bootstrap-loader это уже, в том числе, и запускатор.


Но можно и отредактировать, на всякий случай.
А затем закомментировать код запускатора как неиспользуемый.
В приведённом config.js — это третий фрагмент.
Первый — антиподписячий, второй — bootstrap-loader,
а третий — он, вот его и заменить.

Отсутствует

 

№1633702-05-2022 19:54:27

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

Re: Custom Buttons

ВВП пишет

Как избавиться от красного ?

Ну, например

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

Выделить код

Код:

@-moz-document regexp("moz-extension://\.{36}/options.html\.*") {
	div#firefox-bug.row.alert.alert-warning {
		display: none !important;
	}
}

Отсутствует

 

№1633802-05-2022 20:42:10

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

Re: Custom Buttons

Dumby
Класс ! пропали .panel-arrow кроме закладки . ну этот треугольник ...если принудительно
.panel-arrow {
  display: block !important;
}
То на панелях слева появляется, что делать 7

Отсутствует

 

№1633902-05-2022 22:40:58

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

Re: Custom Buttons

ВВП пишет

пропали .panel-arrow

Что значит «пропали»?
Они выпилены ещё в Firefox 96.

Отсутствует

 

№1634003-05-2022 11:27:14

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

Re: Custom Buttons

Dumby
Пилить интерфейс это "круто"...Декодер МР4 -никогда. От уж фуфло.

Отсутствует

 

№1634103-05-2022 22:06:30

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

Re: Custom Buttons

Dumby пишет

Если используется bootstrap, то запускатор не нужен,
bootstrap-loader это уже, в том числе, и запускатор.

А как этот bootstrap устанавливать, у меня при попытке на [firefox]100 его воткнуть, пишет, что повреждено?

Отсутствует

 

№1634203-05-2022 22:24:28

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

Re: Custom Buttons

voqabuhe пишет

А как этот bootstrap устанавливать

ну попробуйте такой вариант...

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

про это Dumby как-то говорил Антиподписячий код с поправкой.

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=798621#p798621
try {(nsvo => {
	var g = Cu.getGlobalForObject(nsvo), o = g.Object, {freeze} = o, NEW;
	o.freeze = obj => {
		if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm")
			return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;
		if ((NEW = "MOZ_ALLOW_ADDON_SIDELOAD" in obj))
			lockPref("extensions.experiments.enabled", true);
		else
			obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
			lockPref("extensions.legacy.enabled", true);

		return (o.freeze = freeze)(obj);
	}
	lockPref("xpinstall.signatures.required", false);
	lockPref("extensions.langpacks.signatures.required", false);

	nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {});
	var shouldVerify = nsvo.shouldVerifySignedState;
	if (shouldVerify.length == 1)
		nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);
	else {
		var {Services} = g.ChromeUtils.import("resource://gre/modules/Services.jsm");
		var subst = "pkg-proto-patch-tmp-script";
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		var func = async proto => Object.assign(proto, eval(
			`({${proto.verifySignedState}})`.replace("(!", "(addonId || !")
		));
		var code = encodeURIComponent(`(${func})(Package.prototype);`);
		rph.setSubstitution(subst, Services.io.newURI("data:," + code));
		Services.scriptloader.loadSubScript(`resource://${subst}/`, nsvo);
		rph.setSubstitution(subst, null);
	}
	if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false;
})(
	"permitCPOWsInScope" in Cu
		? Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}) : Cu
);}
catch(ex) {Cu.reportError(ex);}

// bootstrap-loader.js https://forum.mozilla-russia.org/viewtopic.php?pid=795196#p795196
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[data-l10n-id="preferences-addon-button"]').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%0Aif (AddonManager.isReady) {%0A  AddonManager.getAllAddons().then(addons => {%0A    addons.forEach(addon => {%0A      if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) {%0A        addon.reload();%0A      };%0A    });%0A  });%0A}%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);}

// https://forum.mozilla-russia.org/viewtopic.php?pid=799413#p799413
try {(async xp => {
	var imprt, ids = [
		"custombuttons@xsms.org",
	];
	if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return;

	if (Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
		var {XPIInternal} = (imprt = url => Cu.import(url, {}))(xp);

	else { // Fx 101+
		var g = Cu.getGlobalForObject(Cu), te = new g.TextEncoder();
		var imp = g.ChromeUtils.import, {XPIInternal} = imp(xp);
		var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
		var rph = ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		imprt = (url, id) => {
			var subst = te.encode(id).join("");
			rph.setSubstitution(subst, ios.newURI(url));
			return imp(`resource://${subst}/`);
		}
	}
	var load = async (file, id) => {
		var rootURI = XPIInternal.getURIForResourceInFile(file, "");
		imprt(rootURI.resolve("startup.jsm"), id).start(rootURI);
	}
	var proto = XPIInternal.BootstrapScope.prototype;
	var func = proto._beforeCallBootstrapMethod;

	proto._beforeCallBootstrapMethod = () => {
		proto._beforeCallBootstrapMethod = func;
		for(var {id, loader, file} of XPIInternal.XPIStates.enabledAddons())
			ids.includes(id) && !loader && load(file, id);
	}
})("resource://gre/modules/addons/XPIProvider.jsm");}
catch(ex) {Cu.reportError(ex);}
//
try {
	const { Services } = Cu.import('resource://gre/modules/Services.jsm');
	Cu.import(Services.io.newFileURI(Services.dirsvc.get('ProfD', Ci.nsIFile)).spec + 'chrome/utils/boot.jsm');
} catch(ex) {Cu.reportError(ex);}

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

Отсутствует

 

№1634303-05-2022 22:53:39

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

Re: Custom Buttons

voqabuhe пишет

А как этот bootstrap устанавливать

А зачем?


bootstrap — это для тех, у кого уже подключён
какой-нибудь bootstrap-loader, соответственно,
установлены другие bootstrap расширения, то есть,
получается, что paxmod поставить можно, но как бы никчему, избыточно.


А подключать bootstrap-loader только,
чтобы поставить bootstrap CB — как-то странно.


И, отвечая на вопрос, если bootstrap-loader подключён и в порядке,
то устанавливать точно так же, как другие расширения и WebExtensions.
На about:addons кнопка-шестерёнка, пункт «Установить дополнение из файла…».
Ещё, вроде, можно перетащить xpi в лису мышью.

Отсутствует

 

№1634403-05-2022 23:16:42

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

Re: Custom Buttons

Dumby пишет

А зачем?

Дык просто любопытство одолело, почему не ставится. :)

bootstrap — это для тех, у кого уже подключён
какой-нибудь bootstrap-loader, соответственно,
установлены другие bootstrap расширения, то есть,
получается, что paxmod поставить можно, но как бы никчему, избыточно.

А подключать bootstrap-loader только,
чтобы поставить bootstrap CB — как-то странно.

Спасибо. Вот теперь всё доходчиво и понятно. И спасибо большое за CB

Отредактировано voqabuhe (03-05-2022 23:30:48)

Отсутствует

 

№1634504-05-2022 17:22:05

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

Re: Custom Buttons

Dumby
.panel-arrow точно никак не вставить?  Вставить то вставил , только оно всегда слева в popup...

Отсутствует

 

№1634604-05-2022 19:05:47

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

Re: Custom Buttons

ВВП пишет

Вставить то вставил

Понимаешь хоть, как это со стороны выглядит?
Отсюда не видно что и как вставил.
Хочешь чтобы я в кофейную гущу пальцем ткнул?


Хорошо, допустим притащил добро из старой версии,
в том числе и кустомэлементщину, включая
chrome://global/content/elements/panel.js


А там используется XULPopupElement.isAnchored, которого больше нет.
Так что пробуй в panel.js заменить this.isAnchored на event.isAnchored
Заменить все, там их несколько.

Отсутствует

 

№1634704-05-2022 19:41:41

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

Re: Custom Buttons

del

Отредактировано ВВП (08-05-2022 20:02:08)

Отсутствует

 

№1634810-05-2022 11:37:32

manuk
Участник
 
Группа: Members
Зарегистрирован: 17-10-2010
Сообщений: 306
UA: Firefox 100.0

Re: Custom Buttons

В кнопке открыть в плеере "Л: Видео в плеер\nП: Видео из Clipboard" при ЛКМ видео добавляется в плейлист. Если убрать строку 62 "args.push("/add");", то видео будет сразу воспроизводится в плеере. А как сделать, чтобы вместо ненужного ПКМ "Видео из Clipboard" сразу запускалось воспроизведение, а по ЛКМ осталось добавление в плейлист? Т.е ЛКМ работало со строкой  "args.push("/add");" а ПКМ без неё.

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

Выделить код

Код:

(func => {
	var sysPlayerName = "Pot Player";
	var path = "R:\\PotPlayer\\PotPlayerMini.exe";
	var videoMoved = "Видео перенесено в " + sysPlayerName;
	var noFound = "Не найдено видео на странице, доступное для переноса в " + sysPlayerName;

	
	this.label = "Открыть видео в " + sysPlayerName;
	this.tooltipText = "Л: Видео в плеер\nП: Видео из Clipboard";

	this._handleClick = () => {
		var msgName = _id + ":Player";
		var listener = ({data}) => data ? run([data]) : notify();
		messageManager.addMessageListener(msgName, listener);
		addDestructor(() => messageManager.removeMessageListener(msgName, listener));

		var url = "data:charset=utf-8," + encodeURIComponent(
			`(${func})()`.replace("MSG_NAME", msgName)
				.replace("VIDEO_MOVED", encodeURIComponent(videoMoved))
				.replace("CONFIRM", encodeURIComponent("Открыть ссылку в плеере ?"))
		);
		(this._handleClick = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}
	this.onauxclick = e => e.button != 1 || gShowPopup(this);
	this.oncontextmenu = e => {
		if (e.ctrlKey || e.shiftKey || e.altKey) return;
		e.preventDefault();
		custombuttons.confirmBox(null, "Запустить плеер из буфера обмена ?", "Да", "Нет")
			&& run([gClipboard.read(), "/play"]);
	}
	var popup = document.getElementById("contentAreaContextMenu");
	addEventListener("popupshowing", {
		get hidden() {
			return !(gContextMenu.onLink || gContextMenu.onVideo || gContextMenu.onPlainTextLink);
		},
		handleEvent() {
			if (this.hidden) return;
			var menuitem = document.createXULElement("menuitem");
			for(var args of Object.entries({
				image: self.image,
				oncommand: "play()",
				class: "menuitem-iconic",
				label: "Открыть в " + sysPlayerName
			}))
				menuitem.setAttribute(...args);
			menuitem.play = () => play(gContextMenu.linkURL || gContextMenu.mediaURL);
			document.getElementById("context-savelink").before(menuitem);
			addDestructor(() => menuitem.remove());
			this.handleEvent = e => {
				if (e.target == popup) menuitem.hidden = this.hidden;
			}
		}
	}, false, popup || 1);

	var play = link => custombuttons.confirmBox(null, "Открыть ссылку в плеере ?", "Да", "Отмена") && run([link]);
	var run = args => {
		var file = FileUtils.File(path);
		(run = args => {
			if (!file.exists()) return custombuttons.alertBox("File not exists!", path);
			var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
			process.init(file);
                        args.push("/add");
			process.runwAsync(args, args.length);
		})(args);
	}
	var notify = () => {
		var name = _id + "-noFound";
		var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
		(notify = () => setTimeout(as.closeAlert, 1150, name, as.showAlertNotification(
			"chrome://global/skin/icons/question-48.png", "", noFound, false, "", null, name
		)))();
	}

})(() => {

	var found, videoMoved, SEND = msg => {
		found = true;
		if (!msg || Cc["@mozilla.org/embedcomp/prompt-service;1"]
			.getService(Ci.nsIPromptService)
			.confirm(content, null, decodeURIComponent("CONFIRM"))
		) {
			if (msg) videoMoved = decodeURIComponent("VIDEO_MOVED");
			sendAsyncMessage("MSG_NAME", msg);
		}
		else return true;
	}

	var YoutubeID = /(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})(?:\W|$)/;

	var tmp = '',
	tmpp = '',
	innerA = '<div style="display:block!important;color:#00ff00!important;width:250px!important;font:bold 16px serif!important;z-index:999!important;opacity:1!important;visibility: visible!important;',
	innerB = 'left:5px!important;position:absolute!important;height:auto!important;box-sizing:border-box!important;padding:5px!important;margin:5px!important;',
	//stopPl = "javascript:(function(){v=document.getElementById('movie_player');if(v){v.stopVideo()}else{v=document.getElementsByTagName('video');if(v){v[0].src='';try{v[0].load()}catch(e){}};}})();",
	ytIMGouter = function (ytID) {
		return '<div width="100%"><br /><a target="_blank" href="https://www.youtube.com/watch?v=' + ytID + '"><img src="https://i.ytimg.com/vi/' + ytID + '/hqdefault.jpg"></a><br />' + innerA + 'background-color:black!important;position:relative!important;bottom:20px!important;">&nbsp;&nbsp;' + videoMoved + '</div><br /></div><br />'
	},
	handlWin = function (currentWin) {
		tmp = '';
		var elem = currentWin.document.getElementsByTagName('video'),
		currLoc = currentWin.location;
		if (elem.length > 0) {
			if (currLoc.hostname.indexOf('youtu') != -1 && (tmp = currLoc.toString().match(YoutubeID)) && tmp[1].length == 11) {

				if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return;

				videoMovedbox = currentWin.document.createElement('videoMoved');
				videoMovedbox.innerHTML = innerA + innerB + 'top:-15px!important;"><b>' + videoMoved + '</b></div>';

				//loadURI(stopPl);
				(function(d){var v=d.getElementById('movie_player');if(v){try{v.stopVideo()}catch{}}
					else{v=d.getElementsByTagName('video');if(v[0]){v[0].src='';try{v[0].load()}catch{}};}})(currentWin.document);

				currentWin.document.getElementById('eow-title').appendChild(videoMovedbox);
				return true;
			};
			for (i = 0; i < elem.length; i++) {
				if (((tmp = getSrc(elem[i].parentNode, currLoc)) && tmp.length > 2) || (i == 0 && currentWin.document.body.innerHTML.substring(0, 7) == '<video ' && (tmp = currLoc.toString()))) {

					if (SEND(tmp)) return;

					videoMovedbox = currentWin.document.createElement('videoMoved');
					videoMovedbox.innerHTML = innerA + innerB + 'top:20px!important;background-color:black!important;">' + videoMoved + '</div>';

					if (currLoc.hostname == 'www.youtube.com') {
						elem[i].parentNode.parentNode.appendChild(videoMovedbox);
					} else {
						elem[i].parentNode.appendChild(videoMovedbox);
					};
					elem[i].src = '';
					try {
						elem[i].load()
					} catch (e) {};
					return true;
				}
			}
		};

		currentWin._elems = currentWin.document.getElementsByTagName('iframe');
		if (currentWin._elems.length > 0) {
			for (currentWin._iCounter = 0; currentWin._iCounter < currentWin._elems.length; currentWin._iCounter++) {
				if ((currentWin._elems[currentWin._iCounter].src.indexOf('youtube.com') > -1) && (tmp = currentWin._elems[currentWin._iCounter].src.match(YoutubeID)) && (tmp[1].length == 11)) {

				if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return;

				currentWin._elems[currentWin._iCounter].outerHTML = ytIMGouter(tmp[1]);
					return true;
				};
				if (currentWin._elems[currentWin._iCounter].clientWidth > 80 && currentWin._elems[currentWin._iCounter].clientHeight > 40 && handlWin(currentWin._elems[currentWin._iCounter].contentWindow))
					return true;
			}
		};

		elem = currentWin.document.getElementsByTagName('object');
		currLoc = currentWin.location;
		if (elem.length == 0) {
			elem = currentWin.document.getElementsByTagName('embed')
		};
		if (elem.length > 0) {
			for (i = 0; i < elem.length; i++) {
				if (elem[i].innerHTML.indexOf('youtu') != -1 && (tmp = elem[i].innerHTML.match(YoutubeID)) && tmp[1].length == 11) {

					if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return;

					elem[i].outerHTML = ytIMGouter(tmp[1]);
					return true;
				} else {
					if (elem[i].clientWidth > 80 && elem[i].clientHeight > 40) {
						if (((tmp = getSrc(elem[i].parentNode, currLoc)) || (tmp = getLink(elem[i], currLoc))) && tmp.length > 2) {

							if (SEND(tmp)) return;

							elem[i].outerHTML = innerA + 'background-color:black!important;bottom:20px!important;">&nbsp;&nbsp;' + videoMoved + '</div>';
							return true;
						};
					};
				}
			};
		};
		return false;
	};

	function restProtHost(lnkR, curLoc) {
		if (lnkR.length == 0)
			return '';
		let tr = lnkR.replace(/^:\/\//, curLoc.protocol + "//");
		if (!tr.match(/^https?:\/\//i)) {
			lnkR = tr.replace(/^\/+/, '');
			if (lnkR.split('/')[0].split('?')[0].split('#')[0].toLowerCase().match(/^(?:[-a-z\d]+\.)+[a-z\d]{2,6}$/)) {
				tr = curLoc.protocol + '//' + lnkR;
			} else {
				tr = curLoc.protocol + '//' + curLoc.host + "/" + lnkR;
			}
		};
		return tr;
	};

	function getSrc(vobj, currentLoc) {
		var t = '',
		tt = '';
		if ((((t = vobj.innerHTML.match(/<video.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*))/i)) && (t) && (tt = t[1] || t[2] || t[3]) && tt.indexOf('blob:') == -1) || ((t = vobj.innerHTML.match(/<source.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*)).*?\stype=['"]?video\//i)) && (t) && (tt = t[1] || t[2] || t[3]))) && tt.length > 2 && tt.indexOf('blob:') == -1) {
			if (tt.indexOf(".mp4/?") == -1) {
				tt = tt.replace(/&amp;/g, "&")
			};
			t = restProtHost(tt, currentLoc);
			return t;
		};
		return '';
	};

	function getLink(obj, curLocation) {

		if (!obj || !obj.tagName)
			return '';
		q = obj.tagName.toLowerCase();

		var getParam = function (e, n) {
			var v = '',
			r = new RegExp('^(' + n + ')$', 'i'),
			param = e.getElementsByTagName('param');
			for (var igp = 0, p; p = param[igp]; igp++) {
				if (p.hasAttribute('name') && p.getAttribute('name').match(r)) {
					v = p.getAttribute('value');
					break
				};
			};
			return v;
		};

		var restPath = function (f, s) {
			return (f.substring(0, 4) == 'http') ? f : s.replace(/[#?].*$/, '').replace(/[^\/]*$/, f)
		};

		function videoLinkExtract(fl) {
			alert(fl);
			var linkArr = [],
			outLinks = [],
			jj = 0,
			lba = '',
			lbb = '',
			decodeURL = gBrowser.currentURI.spec; {
				try {
					return decodeURIComponent(s)
				} catch (e) {
					return unescape(s)
				}
			};

			for (var ij = 0; ij < 3; ij++) {
				lba = lba + String.fromCharCode(parseInt((Math.random() * 15 + 1) + '', 10));
				lbb = lbb + String.fromCharCode(parseInt((Math.random() * 15 + 16) + '', 10));
			};

			function pushWithMerit(lnk) {

				var merit = -11;
				if (lnk.match(/^https?:\/\//i))
					merit = merit + 40;
				if (outLinks.length == 0)
					merit = merit + 1;
				if (lnk.match(/^\//))
					merit = merit + 7;
				if (lnk.match(/^\/\//))
					merit = merit + 30;
				if (lnk.match(/240p([^a-z]|$)/i))
					merit = merit + 1;
				if (lnk.match(/[^a-z]240([^a-z0-9]|$)/i))
					merit = merit + 1;
				if (lnk.match(/360p([^a-z]|$)/i))
					merit = merit + 3;
				if (lnk.match(/[^a-z]360([^a-z0-9]|$)/i))
					merit = merit + 3;
				if (lnk.match(/480p([^a-z]|$)/i))
					merit = merit + 5;
				if (lnk.match(/[^a-z]480([^a-z0-9]|$)/i))
					merit = merit + 5;
				if (lnk.match(/720p([^a-z]|$)/i))
					merit = merit + 7;
				if (lnk.match(/[^a-z]720([^a-z0-9]|$)/i))
					merit = merit + 7;
				if (lnk.match(/\.mp4([^a-z]|$)/i))
					merit = merit + 8;
				if (lnk.match(/_hd([^a-z]|$)/i))
					merit = merit + 6;
				if (lnk.match(/\.(jpg|xml)([^a-z]|$)/i))
					merit = merit - 40;
				if (merit > 0)
					outLinks.push(merit + lba + lnk);
				Services.console.logStringMessage('merit:' + merit + ' lnk->' + lnk);
			};

			linkArr.push(fl);
			while (linkArr.length > jj && jj < 30) {

				var testPaths = [];
				testPaths = linkArr[jj].split(/(\.(?:flv|mp4|m3u8))/i);
				if (testPaths[testPaths.length - 1] == '')
					testPaths.pop();

				for (k = 1; k < testPaths.length; k = k + 2) {

					if (testPaths[k - 1].indexOf(lba) > -1) {
						pref = testPaths[k - 1];
					} else {
						var testAboutDom = testPaths[k - 1].toLowerCase().split(/(https?:\/\/)/);
						if (testAboutDom[testAboutDom.length - 1] == '')
							testAboutDom.pop();
						var pTest = testAboutDom[testAboutDom.length - 1].split(/(\?[^\?]*?&)/);
						if (pTest.length > 2) {
							pTest.pop();
							pTest.pop();
						};
						testAboutDom[testAboutDom.length - 1] = pTest.join('');
						pref = testPaths[k - 1].substring(testAboutDom.join('').lastIndexOf("&") + 1);
					};

					t2 = pref.lastIndexOf(lbb);
					if (t2 > -1) {
						pref = pref.substring(t2 + 3);
					} else {

						t2 = pref.lastIndexOf('{"');
						if (t2 > -1)
							pref = pref.substring(t2 + 2);
						t2 = pref.lastIndexOf('["');
						if (t2 > -1)
							pref = pref.substring(t2 + 2);
						t2 = pref.lastIndexOf(',"');
						if (t2 > -1)
							pref = pref.substring(t2 + 2);
						t2 = pref.toLowerCase().lastIndexOf('"http://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf('"https://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(',http://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(',https://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(';http');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf('*https://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(' or ');
						if (t2 > -1)
							pref = pref.substring(t2 + 4);

						pref = pref.substring(pref.split('/')[0].toLowerCase().split('%2f')[0].lastIndexOf('=') + 1);

					}

					if (pref.length > 0) {

						if (pref.split('?')[0].toLowerCase().match(/%[2-3][0-9a-f]/)) {

							t2 = pref.indexOf('"')
								if (t2 > -1)
									pref = pref.substring(t2 + 1);
								suff = testPaths[k + 1] ? testPaths[k + 1].split('&')[0].split('"')[0].split(';')[0].split(/,http/i)[0] : '';
							if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) {
								if (testPaths.length > k + 1) {
									testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length)
								};
								t2 = pref.lastIndexOf(lba);
								if (t2 > -1)
									pref = pref.substring(t2 + 3)
										linkArr.push(decodeURL(pref + testPaths[k] + suff));

							} else {
								testPaths[k + 1] = (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff
							}
						} else {
							suff = testPaths[k + 1] ? testPaths[k + 1].split(';')[0].split('"]')[0].split('"}')[0].split('",')[0].split(/,https?:\/\//i)[0].split('*https://')[0].split(' or ')[0] : '';
							t2 = suff.indexOf('&');
							if ((t2 > -1) && (pref != testPaths[k - 1])) {
								if (t2 == 0)
									suff = '';
								if (suff.charAt(0) != '?')
									suff = suff.split(/(&[^&]+=https?:\/\/)/i)[0];
							};
							if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) {
								if (testPaths.length > k + 1) {
									testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length)
								};
								t2 = pref.lastIndexOf(lba);
								if (t2 > -1)
									pref = pref.substring(t2 + 3);
								pushWithMerit(pref + testPaths[k] + suff);

							} else {
								testPaths[k + 1] = lba + (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff
							}
						}
					}
				};
				jj = jj + 1;
			};

			if (outLinks.length == 0)
				return '';
			function srt(a, b) {
				a = parseInt(a.substr(0, a.indexOf(lba)), 10);
				b = parseInt(b.substr(0, b.indexOf(lba)), 10);
				if (a < b)
					return 1;
				if (a > b)
					return -1;
				return 0
			};
			outLinks.sort(srt);
			outLinks[0] = outLinks[0].substr(outLinks[0].indexOf(lba) + 3)
				if (outLinks[0].indexOf('_hq.mp4/?time=') > 0)
					outLinks[0] = outLinks[0].replace(/&/g, '&amp;');
				return outLinks[0];
		};

		if (!ol)
			return '';
		//ol = ol.replace(/^:?\/\//, curLocation.protocol + "//");
		//return restPath(ol, src);
		return restProtHost(ol, curLocation);
	};

	try {handlWin(content);} finally {found || SEND();}
});

 var style = custombutton.buttonGetHelp(self).replace(/id/g, _id);
var uri = makeURI('data:text/css,'+ encodeURIComponent(style));
var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
sss.loadAndRegisterSheet(uri, 0);

Отредактировано manuk (10-05-2022 11:38:32)

Отсутствует

 

№1634910-05-2022 19:57:05

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

Re: Custom Buttons

manuk пишет

Т.е ЛКМ работало со строкой  "args.push("/add");" а ПКМ без неё.

Самое простое — по тайминг-флагу.

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

Выделить код

Код:

…
	var rmb, reset = () => rmb = false;
	this.oncontextmenu = e => {
		if (e.ctrlKey || e.shiftKey || e.altKey) return;
		e.preventDefault();
		//custombuttons.confirmBox(null, "Запустить плеер из буфера обмена ?", "Да", "Нет")
		//	&& run([gClipboard.read(), "/play"]);
		rmb = true;
		setTimeout(reset, 500);
		this._handleClick();
	}
Выделить код

Код:

…
                        //args.push("/add");
			rmb || args.push("/add");

Отсутствует

 

№1635010-05-2022 20:33:48

manuk
Участник
 
Группа: Members
Зарегистрирован: 17-10-2010
Сообщений: 306
UA: Firefox 100.0

Re: Custom Buttons

Dumby, заменил на ваши строки своими кривыми ручонками, да не так, наверно. По ЛКМ и ПКМ одинаково добавляет в плейлист.:(

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

Выделить код

Код:

(func => {
	var sysPlayerName = "Pot Player";
	var path = "R:\\PotPlayer\\PotPlayerMini.exe";
	var videoMoved = "Видео перенесено в " + sysPlayerName;
	var noFound = "Не найдено видео на странице, доступное для переноса в " + sysPlayerName;

	
	this.label = "Открыть видео в " + sysPlayerName;
	this.tooltipText = "Л: Видео в плейлист";

	this._handleClick = () => {
		var msgName = _id + ":Player";
		var listener = ({data}) => data ? run([data]) : notify();
		messageManager.addMessageListener(msgName, listener);
		addDestructor(() => messageManager.removeMessageListener(msgName, listener));

		var url = "data:charset=utf-8," + encodeURIComponent(
			`(${func})()`.replace("MSG_NAME", msgName)
				.replace("VIDEO_MOVED", encodeURIComponent(videoMoved))
				.replace("CONFIRM", encodeURIComponent("Открыть ссылку в плеере ?"))
		);
		(this._handleClick = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}
	var rmb, reset = () => rmb = false;
	this.oncontextmenu = e => {
		if (e.ctrlKey || e.shiftKey || e.altKey) return;
		e.preventDefault();
		//custombuttons.confirmBox(null, "Запустить плеер из буфера обмена ?", "Да", "Нет")
		//	&& run([gClipboard.read(), "/play"]);
		rmb = true;
		setTimeout(reset, 500);
		this._handleClick();
	}
	var popup = document.getElementById("contentAreaContextMenu");
	addEventListener("popupshowing", {
		get hidden() {
			return !(gContextMenu.onLink || gContextMenu.onVideo || gContextMenu.onPlainTextLink);
		},
		handleEvent() {
			if (this.hidden) return;
			var menuitem = document.createXULElement("menuitem");
			for(var args of Object.entries({
				image: self.image,
				oncommand: "play()",
				class: "menuitem-iconic", 
				label: "  В плейлист " + sysPlayerName
			}))
				menuitem.setAttribute(...args);
			menuitem.play = () => play(gContextMenu.linkURL || gContextMenu.mediaURL);
			document.getElementById("context-savelink").before(menuitem);
			addDestructor(() => menuitem.remove());
			this.handleEvent = e => {
				if (e.target == popup) menuitem.hidden = this.hidden;
			}
		}
	}, false, popup || 1);

	var play = link => custombuttons.confirmBox(null, "Открыть ссылку в плеере ?", "Да", "Отмена") && run([link]);
	var run = args => {
		var file = FileUtils.File(path);
		(run = args => {
			if (!file.exists()) return custombuttons.alertBox("File not exists!", path);
			var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
			process.init(file);
                        //args.push("/add");
			rmb || args.push("/add");
			process.runwAsync(args, args.length);
		})(args);
	}
	var notify = () => {
		var name = _id + "-noFound";
		var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
		(notify = () => setTimeout(as.closeAlert, 1150, name, as.showAlertNotification(
			"chrome://global/skin/icons/question-48.png", "", noFound, false, "", null, name
		)))();
	}

})(() => {

	var found, videoMoved, SEND = msg => {
		found = true;
		if (!msg || Cc["@mozilla.org/embedcomp/prompt-service;1"]
			.getService(Ci.nsIPromptService)
			.confirm(content, null, decodeURIComponent("CONFIRM"))
		) {
			if (msg) videoMoved = decodeURIComponent("VIDEO_MOVED");
			sendAsyncMessage("MSG_NAME", msg);
		}
		else return true;
	}

	var YoutubeID = /(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})(?:\W|$)/;

	var tmp = '',
	tmpp = '',
	innerA = '<div style="display:block!important;color:#00ff00!important;width:250px!important;font:bold 16px serif!important;z-index:999!important;opacity:1!important;visibility: visible!important;',
	innerB = 'left:5px!important;position:absolute!important;height:auto!important;box-sizing:border-box!important;padding:5px!important;margin:5px!important;',
	//stopPl = "javascript:(function(){v=document.getElementById('movie_player');if(v){v.stopVideo()}else{v=document.getElementsByTagName('video');if(v){v[0].src='';try{v[0].load()}catch(e){}};}})();",
	ytIMGouter = function (ytID) {
		return '<div width="100%"><br /><a target="_blank" href="https://www.youtube.com/watch?v=' + ytID + '"><img src="https://i.ytimg.com/vi/' + ytID + '/hqdefault.jpg"></a><br />' + innerA + 'background-color:black!important;position:relative!important;bottom:20px!important;">&nbsp;&nbsp;' + videoMoved + '</div><br /></div><br />'
	},
	handlWin = function (currentWin) {
		tmp = '';
		var elem = currentWin.document.getElementsByTagName('video'),
		currLoc = currentWin.location;
		if (elem.length > 0) {
			if (currLoc.hostname.indexOf('youtu') != -1 && (tmp = currLoc.toString().match(YoutubeID)) && tmp[1].length == 11) {

				if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return;

				videoMovedbox = currentWin.document.createElement('videoMoved');
				videoMovedbox.innerHTML = innerA + innerB + 'top:-15px!important;"><b>' + videoMoved + '</b></div>';

				//loadURI(stopPl);
				(function(d){var v=d.getElementById('movie_player');if(v){try{v.stopVideo()}catch{}}
					else{v=d.getElementsByTagName('video');if(v[0]){v[0].src='';try{v[0].load()}catch{}};}})(currentWin.document);

				currentWin.document.getElementById('eow-title').appendChild(videoMovedbox);
				return true;
			};
			for (i = 0; i < elem.length; i++) {
				if (((tmp = getSrc(elem[i].parentNode, currLoc)) && tmp.length > 2) || (i == 0 && currentWin.document.body.innerHTML.substring(0, 7) == '<video ' && (tmp = currLoc.toString()))) {

					if (SEND(tmp)) return;

					videoMovedbox = currentWin.document.createElement('videoMoved');
					videoMovedbox.innerHTML = innerA + innerB + 'top:20px!important;background-color:black!important;">' + videoMoved + '</div>';

					if (currLoc.hostname == 'www.youtube.com') {
						elem[i].parentNode.parentNode.appendChild(videoMovedbox);
					} else {
						elem[i].parentNode.appendChild(videoMovedbox);
					};
					elem[i].src = '';
					try {
						elem[i].load()
					} catch (e) {};
					return true;
				}
			}
		};

		currentWin._elems = currentWin.document.getElementsByTagName('iframe');
		if (currentWin._elems.length > 0) {
			for (currentWin._iCounter = 0; currentWin._iCounter < currentWin._elems.length; currentWin._iCounter++) {
				if ((currentWin._elems[currentWin._iCounter].src.indexOf('youtube.com') > -1) && (tmp = currentWin._elems[currentWin._iCounter].src.match(YoutubeID)) && (tmp[1].length == 11)) {

				if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return;

				currentWin._elems[currentWin._iCounter].outerHTML = ytIMGouter(tmp[1]);
					return true;
				};
				if (currentWin._elems[currentWin._iCounter].clientWidth > 80 && currentWin._elems[currentWin._iCounter].clientHeight > 40 && handlWin(currentWin._elems[currentWin._iCounter].contentWindow))
					return true;
			}
		};

		elem = currentWin.document.getElementsByTagName('object');
		currLoc = currentWin.location;
		if (elem.length == 0) {
			elem = currentWin.document.getElementsByTagName('embed')
		};
		if (elem.length > 0) {
			for (i = 0; i < elem.length; i++) {
				if (elem[i].innerHTML.indexOf('youtu') != -1 && (tmp = elem[i].innerHTML.match(YoutubeID)) && tmp[1].length == 11) {

					if (SEND('https://www.youtube.com/watch?v=' + tmp[1])) return;

					elem[i].outerHTML = ytIMGouter(tmp[1]);
					return true;
				} else {
					if (elem[i].clientWidth > 80 && elem[i].clientHeight > 40) {
						if (((tmp = getSrc(elem[i].parentNode, currLoc)) || (tmp = getLink(elem[i], currLoc))) && tmp.length > 2) {

							if (SEND(tmp)) return;

							elem[i].outerHTML = innerA + 'background-color:black!important;bottom:20px!important;">&nbsp;&nbsp;' + videoMoved + '</div>';
							return true;
						};
					};
				}
			};
		};
		return false;
	};

	function restProtHost(lnkR, curLoc) {
		if (lnkR.length == 0)
			return '';
		let tr = lnkR.replace(/^:\/\//, curLoc.protocol + "//");
		if (!tr.match(/^https?:\/\//i)) {
			lnkR = tr.replace(/^\/+/, '');
			if (lnkR.split('/')[0].split('?')[0].split('#')[0].toLowerCase().match(/^(?:[-a-z\d]+\.)+[a-z\d]{2,6}$/)) {
				tr = curLoc.protocol + '//' + lnkR;
			} else {
				tr = curLoc.protocol + '//' + curLoc.host + "/" + lnkR;
			}
		};
		return tr;
	};

	function getSrc(vobj, currentLoc) {
		var t = '',
		tt = '';
		if ((((t = vobj.innerHTML.match(/<video.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*))/i)) && (t) && (tt = t[1] || t[2] || t[3]) && tt.indexOf('blob:') == -1) || ((t = vobj.innerHTML.match(/<source.*?\ssrc=(?:(?:'([^']*)')|(?:"([^"]*)")|([^\s]*)).*?\stype=['"]?video\//i)) && (t) && (tt = t[1] || t[2] || t[3]))) && tt.length > 2 && tt.indexOf('blob:') == -1) {
			if (tt.indexOf(".mp4/?") == -1) {
				tt = tt.replace(/&amp;/g, "&")
			};
			t = restProtHost(tt, currentLoc);
			return t;
		};
		return '';
	};

	function getLink(obj, curLocation) {

		if (!obj || !obj.tagName)
			return '';
		q = obj.tagName.toLowerCase();

		var getParam = function (e, n) {
			var v = '',
			r = new RegExp('^(' + n + ')$', 'i'),
			param = e.getElementsByTagName('param');
			for (var igp = 0, p; p = param[igp]; igp++) {
				if (p.hasAttribute('name') && p.getAttribute('name').match(r)) {
					v = p.getAttribute('value');
					break
				};
			};
			return v;
		};

		var restPath = function (f, s) {
			return (f.substring(0, 4) == 'http') ? f : s.replace(/[#?].*$/, '').replace(/[^\/]*$/, f)
		};

		function videoLinkExtract(fl) {
			alert(fl);
			var linkArr = [],
			outLinks = [],
			jj = 0,
			lba = '',
			lbb = '',
			decodeURL = gBrowser.currentURI.spec; {
				try {
					return decodeURIComponent(s)
				} catch (e) {
					return unescape(s)
				}
			};

			for (var ij = 0; ij < 3; ij++) {
				lba = lba + String.fromCharCode(parseInt((Math.random() * 15 + 1) + '', 10));
				lbb = lbb + String.fromCharCode(parseInt((Math.random() * 15 + 16) + '', 10));
			};

			function pushWithMerit(lnk) {

				var merit = -11;
				if (lnk.match(/^https?:\/\//i))
					merit = merit + 40;
				if (outLinks.length == 0)
					merit = merit + 1;
				if (lnk.match(/^\//))
					merit = merit + 7;
				if (lnk.match(/^\/\//))
					merit = merit + 30;
				if (lnk.match(/240p([^a-z]|$)/i))
					merit = merit + 1;
				if (lnk.match(/[^a-z]240([^a-z0-9]|$)/i))
					merit = merit + 1;
				if (lnk.match(/360p([^a-z]|$)/i))
					merit = merit + 3;
				if (lnk.match(/[^a-z]360([^a-z0-9]|$)/i))
					merit = merit + 3;
				if (lnk.match(/480p([^a-z]|$)/i))
					merit = merit + 5;
				if (lnk.match(/[^a-z]480([^a-z0-9]|$)/i))
					merit = merit + 5;
				if (lnk.match(/720p([^a-z]|$)/i))
					merit = merit + 7;
				if (lnk.match(/[^a-z]720([^a-z0-9]|$)/i))
					merit = merit + 7;
				if (lnk.match(/\.mp4([^a-z]|$)/i))
					merit = merit + 8;
				if (lnk.match(/_hd([^a-z]|$)/i))
					merit = merit + 6;
				if (lnk.match(/\.(jpg|xml)([^a-z]|$)/i))
					merit = merit - 40;
				if (merit > 0)
					outLinks.push(merit + lba + lnk);
				Services.console.logStringMessage('merit:' + merit + ' lnk->' + lnk);
			};

			linkArr.push(fl);
			while (linkArr.length > jj && jj < 30) {

				var testPaths = [];
				testPaths = linkArr[jj].split(/(\.(?:flv|mp4|m3u8))/i);
				if (testPaths[testPaths.length - 1] == '')
					testPaths.pop();

				for (k = 1; k < testPaths.length; k = k + 2) {

					if (testPaths[k - 1].indexOf(lba) > -1) {
						pref = testPaths[k - 1];
					} else {
						var testAboutDom = testPaths[k - 1].toLowerCase().split(/(https?:\/\/)/);
						if (testAboutDom[testAboutDom.length - 1] == '')
							testAboutDom.pop();
						var pTest = testAboutDom[testAboutDom.length - 1].split(/(\?[^\?]*?&)/);
						if (pTest.length > 2) {
							pTest.pop();
							pTest.pop();
						};
						testAboutDom[testAboutDom.length - 1] = pTest.join('');
						pref = testPaths[k - 1].substring(testAboutDom.join('').lastIndexOf("&") + 1);
					};

					t2 = pref.lastIndexOf(lbb);
					if (t2 > -1) {
						pref = pref.substring(t2 + 3);
					} else {

						t2 = pref.lastIndexOf('{"');
						if (t2 > -1)
							pref = pref.substring(t2 + 2);
						t2 = pref.lastIndexOf('["');
						if (t2 > -1)
							pref = pref.substring(t2 + 2);
						t2 = pref.lastIndexOf(',"');
						if (t2 > -1)
							pref = pref.substring(t2 + 2);
						t2 = pref.toLowerCase().lastIndexOf('"http://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf('"https://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(',http://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(',https://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(';http');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf('*https://');
						if (t2 > -1)
							pref = pref.substring(t2 + 1);
						t2 = pref.toLowerCase().lastIndexOf(' or ');
						if (t2 > -1)
							pref = pref.substring(t2 + 4);

						pref = pref.substring(pref.split('/')[0].toLowerCase().split('%2f')[0].lastIndexOf('=') + 1);

					}

					if (pref.length > 0) {

						if (pref.split('?')[0].toLowerCase().match(/%[2-3][0-9a-f]/)) {

							t2 = pref.indexOf('"')
								if (t2 > -1)
									pref = pref.substring(t2 + 1);
								suff = testPaths[k + 1] ? testPaths[k + 1].split('&')[0].split('"')[0].split(';')[0].split(/,http/i)[0] : '';
							if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) {
								if (testPaths.length > k + 1) {
									testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length)
								};
								t2 = pref.lastIndexOf(lba);
								if (t2 > -1)
									pref = pref.substring(t2 + 3)
										linkArr.push(decodeURL(pref + testPaths[k] + suff));

							} else {
								testPaths[k + 1] = (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff
							}
						} else {
							suff = testPaths[k + 1] ? testPaths[k + 1].split(';')[0].split('"]')[0].split('"}')[0].split('",')[0].split(/,https?:\/\//i)[0].split('*https://')[0].split(' or ')[0] : '';
							t2 = suff.indexOf('&');
							if ((t2 > -1) && (pref != testPaths[k - 1])) {
								if (t2 == 0)
									suff = '';
								if (suff.charAt(0) != '?')
									suff = suff.split(/(&[^&]+=https?:\/\/)/i)[0];
							};
							if ((suff != testPaths[k + 1]) || (testPaths.length < k + 3)) {
								if (testPaths.length > k + 1) {
									testPaths[k + 1] = ((pref == testPaths[k - 1]) ? '' : '&') + testPaths[k + 1].substr(suff.length)
								};
								t2 = pref.lastIndexOf(lba);
								if (t2 > -1)
									pref = pref.substring(t2 + 3);
								pushWithMerit(pref + testPaths[k] + suff);

							} else {
								testPaths[k + 1] = lba + (pref == testPaths[k - 1] ? '' : lbb) + pref + testPaths[k] + suff
							}
						}
					}
				};
				jj = jj + 1;
			};

			if (outLinks.length == 0)
				return '';
			function srt(a, b) {
				a = parseInt(a.substr(0, a.indexOf(lba)), 10);
				b = parseInt(b.substr(0, b.indexOf(lba)), 10);
				if (a < b)
					return 1;
				if (a > b)
					return -1;
				return 0
			};
			outLinks.sort(srt);
			outLinks[0] = outLinks[0].substr(outLinks[0].indexOf(lba) + 3)
				if (outLinks[0].indexOf('_hq.mp4/?time=') > 0)
					outLinks[0] = outLinks[0].replace(/&/g, '&amp;');
				return outLinks[0];
		};

		if (!ol)
			return '';
		//ol = ol.replace(/^:?\/\//, curLocation.protocol + "//");
		//return restPath(ol, src);
		return restProtHost(ol, curLocation);
	};

	try {handlWin(content);} finally {found || SEND();}
});

 var style = custombutton.buttonGetHelp(self).replace(/id/g, _id);
var uri = makeURI('data:text/css,'+ encodeURIComponent(style));
var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
sss.loadAndRegisterSheet(uri, 0);

Отсутствует

 

Board footer

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