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

Будьте в курсе последних изменений в мире Mozilla, следя за нашим микроблогом в Twitter.

№1640126-04-2022 23:35:19

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 332
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)

Отсутствует

 

№1640202-05-2022 16:59:02

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
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");

Отсутствует

 

№1640302-05-2022 17:40:29

vitalii201
Участник
 
Группа: Members
Зарегистрирован: 24-03-2011
Сообщений: 676
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);}


Или где?

Отсутствует

 

№1640402-05-2022 17:51:32

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

Re: Custom Buttons

Dumby
Спасибо!


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


Win7

Отсутствует

 

№1640502-05-2022 18:00:40

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

Re: Custom Buttons

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

Отсутствует

 

№1640602-05-2022 18:35:18

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
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,
а третий — он, вот его и заменить.

Отсутствует

 

№1640702-05-2022 19:54:27

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
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;
	}
}

Отсутствует

 

№1640802-05-2022 20:42:10

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

Re: Custom Buttons

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

Отсутствует

 

№1640902-05-2022 22:40:58

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

Re: Custom Buttons

ВВП пишет

пропали .panel-arrow

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

Отсутствует

 

№1641003-05-2022 11:27:14

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

Re: Custom Buttons

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

Отсутствует

 

№1641103-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 его воткнуть, пишет, что повреждено?

Отсутствует

 

№1641203-05-2022 22:24:28

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 243
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);}

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

Отсутствует

 

№1641303-05-2022 22:53:39

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

Re: Custom Buttons

voqabuhe пишет

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

А зачем?


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


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


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

Отсутствует

 

№1641403-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)

Отсутствует

 

№1641504-05-2022 17:22:05

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

Re: Custom Buttons

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

Отсутствует

 

№1641604-05-2022 19:05:47

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

Re: Custom Buttons

ВВП пишет

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

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


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


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

Отсутствует

 

№1641704-05-2022 19:41:41

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

Re: Custom Buttons

del

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

Отсутствует

 

№1641810-05-2022 11:37:32

manuk
Участник
 
Группа: Members
Зарегистрирован: 17-10-2010
Сообщений: 275
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)

Отсутствует

 

№1641910-05-2022 19:57:05

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2129
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");

Отсутствует

 

№1642010-05-2022 20:33:48

manuk
Участник
 
Группа: Members
Зарегистрирован: 17-10-2010
Сообщений: 275
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);

Отсутствует

 

№1642110-05-2022 20:45:59

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

Re: Custom Buttons

manuk
Что не так ?

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

Выделить код

Код:

this.onclick = 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"]);
			
	}

Отсутствует

 

№1642210-05-2022 21:11:02

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

Re: Custom Buttons

ВВП, При ПКМ запрос: "Запустить плеер из буфера обмена ?" - "Нет". Далее появляется следующий запрос: "Открыть ссылку в плеере ?" - "ОК". После чего ссылка добавляется в конец плейлиста, а не запускается в новом. Может дадите код кнопки целиком? Я что-то не то делаю, вероятно.

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

Выделить код

Код:

(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.onclick = 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"]);
		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);

Отсутствует

 

№1642310-05-2022 21:27:01

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

Re: Custom Buttons

manuk
В Pot в плейлист добавляется и все . не основное добавляется , это не GOMPLAYER

Выделить код

Код:

custombutton://%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0D%0A%3Ccustombutton%20xmlns%3Acb%3D%22http%3A//xsms.nm.ru/custombuttons/%22%3E%0A%20%20%3Cname%3E%u0412%u0438%u0434%u0435%u043E%20%u0432%20Potplayer%3C/name%3E%0A%20%20%3Cimage%3E%3C%21%5BCDATA%5Bdata%3Aimage/x-icon%3Bbase64%2CAAABAAEAEhMAAAEAIADMBQAAFgAAACgAAAASAAAAJgAAAAEAIAAAAAAApAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8kEBaqMcAOxzCgD7dAsA93UMAPh1DQD4dgwA+HULAPh2DAD4dg0A+HUMAPh2DQD4dQwA93IJAPiWGADt5kAAfgAAAAD+bQAWxSgA1FcAAP9LAAD8TwAA/E4AAPtPAAD7TwAA/E8AAP1PAAD8TwAA/U8AAPxPAAD7TgAA+08AAPpLAAD9rBwA4/VVBiDpTw8+mhkA81AAAP9ZAAD7WgAA+1gAAPtaAAD8WwAA/VwAAPxbAAD8WgAA/VoAAPxYAAD7WgAA+lwAAPpPAAD5ixQA8OpNDFHeQQpNfgsA9E8AAP5XAAD7YAAA/F8AAPxbAAD8WAAA/VgAAP1YAAD9WAAA/VsAAP1hAQD8YgAA/FoAAPtRAAD5gA4A6+JFDmbkSA5KiBAA9E8AAP1hAQD7aAQA/F0AAPxYAAD8WAAA/VgAAP1YAAD9WAAA/VgAAP1hAQD8awQA/GcCAPtaAAD5ihMA6uVKD2bvUBFKjRQA804AAP1pBAD7bQcA/FwAAPxXAAD8VwAA/VcAAP1XAAD9VwAA/VgAAP1hAQD7cwcA/HEFAPtgAAD5kBcA6udMD2b6Uw1LuCgA9VQAAP+KFgD8ZgQA/FQAAP1TAAD+TwAA/UgAAP5RAAD+WgAA/lgAAP1WAAD+awYA/ZYYAP2MEgD6tSkA8/tdEWD/VglL4DkA9H8MAP+LEgD8VAAA/U4AAPxwCAD9pCEA/oYPAP9PAAD+RwAA/1QAAP5VAAD9VQAA+5QYAP29KQD63jwA8/9iEmD/WwhL/0oD9K0iAP9zBgD8UQAA/UoAAP2PFwD99VgP/u1WDv+rKQD+bQMA/0wAAP5OAAD9TgAA/X4NAP3eNgD7/1EE8/9uHmD/YwxL/2AT9MssAP9hAAD9UQAA/ksAAP2XHAL++3Ym//+RNv7/eyn/1VYa/pomB/9pBAD+TgAA/WsFAP7nPwb7/2od8/9+M2D/bhFL/3Ej9NgzAP9cAAD9UQAA/kwAAP2gJQf+/Y1C//+oXv7/o1///61b/v2MOP+fLw3+RwAA/WQEAP7iThT7/3wv8/+GPGD/dyNL/4Mw9dpAC/9mAAD8TwAA/lAAAP2uLgn+/6BT///Bdv7/uW3+/69f/+qBPP6TKhL9QwAA/WwGAP3pXRr7/4cy8/+GOmD/gDBK/5ZH8+BRE/+IEQH8UQAA/mYAAP3BPwn9/79T/v/kb///vFL++3Ig/8knAP6LCQD9aQAA+58cA/39fDH7/59L8v+LPV//hjhJ/6Zd8/ZwJv/ARRf8bgIA/ZAIAPzjVQr9/9VO/v/NTP3/bxT+/EAA/u5LEf3xUhP+3EIM/e97KP3/v1r6/81i8f+YQV//jD1M/6pn9P+fUf/2mkP8uzAE/LcZAPzuRQD9/3IY/P9hGP3/TQ/+/2Qc/f90JP7/biD9/5E5/P/xaPz//2j6//Ns8v+jP2H/pE46/7Ni7v+4bv//2Gf//rM3/+9SCP/8NwH+/0kP//9gGv//dif//4Ew//+DL///nk7//+ib////pv///5f+//uX7v+YQEsAAAAL/6pMsv+2av//5Wf6//9w+v/nffv/plz6/4U6+/+HOvv/mU37/7Fl+//Yofv///D7///w+v//svv//7H3/9V1uf+6ZPAAAAAA/4EkFP+9T3v/6Wqh//+Knf//rJ7//7Gf//qyn//+vaD//8Sg//+9n///wJ7//9Od///Lnf/0o57/ymOB/50kGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAD//8AA%5D%5D%3E%3C/image%3E%0A%20%20%3Cmode%3E0%3C/mode%3E%0A%20%20%3Cinitcode%3E%3C%21%5BCDATA%5B%28func%20%3D%3E%20%7B%0A%09var%20sysPlayerName%20%3D%20%22Pot%20Player%22%3B%0A%20%20%20%20%20%20%20%20%0A%09var%20path%20%3D%20%22D%3A%5C%5CPotPlayer%5C%5CPotPlayerMini64.exe%22%3B%0A%09var%20videoMoved%20%3D%20%22%u0412%u0438%u0434%u0435%u043E%20%u043F%u0435%u0440%u0435%u043D%u0435%u0441%u0435%u043D%u043E%20%u0432%20%22%20+%20sysPlayerName%3B%0A%09var%20noFound%20%3D%20%22%u041D%u0435%20%u043D%u0430%u0439%u0434%u0435%u043D%u043E%20%u0432%u0438%u0434%u0435%u043E%20%u043D%u0430%20%u0441%u0442%u0440%u0430%u043D%u0438%u0446%u0435%2C%20%u0434%u043E%u0441%u0442%u0443%u043F%u043D%u043E%u0435%20%u0434%u043B%u044F%20%u043F%u0435%u0440%u0435%u043D%u043E%u0441%u0430%20%u0432%20%22%20+%20sysPlayerName%3B%0A%20%20%20%20%20%20%20%20%0A%09%0A%09this.label%20%3D%20%22%u041E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0432%u0438%u0434%u0435%u043E%20%u0432%20%22%20+%20sysPlayerName%3B%0A%09this.tooltipText%20%3D%20%22%u041B%3A%20%u0412%u0438%u0434%u0435%u043E%20%u0432%20%u043F%u043B%u0435%u0435%u0440%5Cn%u041F%3A%20%u0412%u0438%u0434%u0435%u043E%20%u0438%u0437%20Clipboard%22%3B%0A%0A%09this._handleClick%20%3D%20%28%29%20%3D%3E%20%7B%0A%09%09var%20msgName%20%3D%20_id%20+%20%22%3APlayer%22%3B%0A%09%09var%20listener%20%3D%20%28%7Bdata%7D%29%20%3D%3E%20data%20%3F%20run%28%5Bdata%5D%29%20%3A%20notify%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20listener%20%3D%20%28%7Bdata%7D%29%20%3D%3E%20data%20%3F%20run%28%5Bdata%5D%2C%20true%29%20%3A%20notify%28%29%3B%0A%09%09messageManager.addMessageListener%28msgName%2C%20listener%29%3B%0A%09%09addDestructor%28%28%29%20%3D%3E%20messageManager.removeMessageListener%28msgName%2C%20listener%29%29%3B%0A%0A%09%09var%20url%20%3D%20%22data%3Acharset%3Dutf-8%2C%22%20+%20encodeURIComponent%28%0A%09%09%09%60%28%24%7Bfunc%7D%29%28%29%60.replace%28%22MSG_NAME%22%2C%20msgName%29%0A%09%09%09%09.replace%28%22VIDEO_MOVED%22%2C%20encodeURIComponent%28videoMoved%29%29%0A%09%09%09%09.replace%28%22CONFIRM%22%2C%20encodeURIComponent%28%22%u041E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0441%u0441%u044B%u043B%u043A%u0443%20%u0432%20%u043F%u043B%u0435%u0435%u0440%u0435%20%3F%22%29%29%0A%09%09%29%3B%0A%09%09%28this._handleClick%20%3D%20%28%29%20%3D%3E%20gBrowser.selectedBrowser.messageManager.loadFrameScript%28url%2C%20false%29%29%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%09%7D%0A%09this.onclick%20%3D%20e%20%3D%3E%20e.button%20%21%3D%201%20%7C%7C%20gShowPopup%28this%29%3B%20%0A%09this.oncontextmenu%20%3D%20e%20%3D%3E%20%7B%0A%09%09if%20%28e.ctrlKey%20%7C%7C%20e.shiftKey%20%7C%7C%20e.altKey%29%20return%3B%0A%09%09e.preventDefault%28%29%3B%0A%09%09custombuttons.confirmBox%28null%2C%20%22%u0417%u0430%u043F%u0443%u0441%u0442%u0438%u0442%u044C%20%u043F%u043B%u0435%u0435%u0440%20%u0438%u0437%20%u0431%u0443%u0444%u0435%u0440%u0430%20%u043E%u0431%u043C%u0435%u043D%u0430%20%3F%22%2C%20%22%u0414%u0430%22%2C%20%22%u041D%u0435%u0442%22%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20run%28%5BgClipboard.read%28%29%2C%20%22/play%22%5D%29%3B%0A%09%09%09%0A%09%7D%0A%09var%20popup%20%3D%20document.getElementById%28%22contentAreaContextMenu%22%29%3B%0A%09addEventListener%28%22popupshowing%22%2C%20%7B%0A%09%09get%20hidden%28%29%20%7B%0A%09%09%09return%20%21%28gContextMenu.onLink%20%7C%7C%20gContextMenu.onVideo%20%7C%7C%20gContextMenu.onPlainTextLink%29%3B%0A%09%09%7D%2C%0A%09%09handleEvent%28%29%20%7B%0A%09%09%09if%20%28this.hidden%29%20return%3B%0A%09%09%09var%20menuitem%20%3D%20document.createXULElement%28%22menuitem%22%29%3B%0A%09%09%09for%28var%20args%20of%20Object.entries%28%7B%0A%09%09%09%09image%3A%20self.image%2C%0A%09%09%09%09oncommand%3A%20%22play%28%29%3B%22%2C%0A%09%09%09%09class%3A%20%22menuitem-iconic%22%2C%0A%09%09%09%09label%3A%20%22%u041E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0432%20%22%20+%20sysPlayerName%0A%09%09%09%7D%29%29%0A%09%09%09%09menuitem.setAttribute%28...args%29%3B%0A%09%09%09menuitem.play%20%3D%20%28%29%20%3D%3E%20play%28gContextMenu.linkURL%20%7C%7C%20gContextMenu.mediaURL%29%3B%0A%09%09%09document.getElementById%28%22context-savelink%22%29.before%28menuitem%29%3B%0A%09%09%09addDestructor%28%28%29%20%3D%3E%20menuitem.remove%28%29%29%3B%0A%09%09%09this.handleEvent%20%3D%20e%20%3D%3E%20%7B%0A%09%09%09%09if%20%28e.target%20%3D%3D%20popup%29%20menuitem.hidden%20%3D%20this.hidden%3B%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%2C%20false%2C%20popup%20%7C%7C%201%29%3B%0A%0A%09var%20play%20%3D%20link%20%3D%3E%20custombuttons.confirmBox%28null%2C%20%22%u041E%u0442%u043A%u0440%u044B%u0442%u044C%20%u0441%u0441%u044B%u043B%u043A%u0443%20%u0432%20%u043F%u043B%u0435%u0435%u0440%u0435%20%3F%22%2C%20%22%u0414%u0430%22%2C%20%22%u041E%u0442%u043C%u0435%u043D%u0430%22%29%20%26%26%20run%28%5Blink%5D%29%3B%0A%09/*%0A%09var%20run%20%3D%20args%20%3D%3E%20%7B%0A%09%09var%20file%20%3D%20FileUtils.File%28path%29%3B%0A%09%09%28run%20%3D%20args%20%3D%3E%20%7B%0A%09%09%09if%20%28%21file.exists%28%29%29%20return%20custombuttons.alertBox%28%22File%20not%20exists%21%22%2C%20path%29%3B%0A%09%09%09var%20process%20%3D%20Cc%5B%22@mozilla.org/process/util%3B1%22%5D.createInstance%28Ci.nsIProcess%29%3B%0A%09%09%09process.init%28file%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20args.push%28%22/play%22%29%3B%0A%09%09%09process.runwAsync%28args%2C%20args.length%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20quit%20%26%26%20window.close%28%29%3B%0A%09%09%7D%29%28args%29%3B%0A%09%7D%0A*/%0A%09var%20run%20%3D%20%28...a%29%20%3D%3E%20%7B%0A%09%09var%20file%20%3D%20FileUtils.File%28path%29%3B%0A%09%09%28run%20%3D%20%28args%2C%20quit%29%20%3D%3E%20%7B%0A%09%09%09if%20%28%21file.exists%28%29%29%20return%20custombuttons.alertBox%28%22File%20not%20exists%21%22%2C%20path%29%3B%0A%09%09%09var%20process%20%3D%20Cc%5B%22@mozilla.org/process/util%3B1%22%5D.createInstance%28Ci.nsIProcess%29%3B%0A%09%09%09gBrowser.selectedBrowser.browsingContext.mediaController.pause%28%29%3B%0A%09%09%09process.init%28file%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20args.push%28%22/play%22%29%3B%0A%09%09%09process.runwAsync%28args%2C%20args.length%29%3B%0A%09%09%09%0A%09%09%7D%29%28...a%29%3B%0A%09%7D%0A%09var%20notify%20%3D%20%28%29%20%3D%3E%20%7B%0A%09%09var%20name%20%3D%20_id%20+%20%22-noFound%22%3B%0A%09%09var%20as%20%3D%20Cc%5B%22@mozilla.org/alerts-service%3B1%22%5D.getService%28Ci.nsIAlertsService%29%3B%0A%09%09%28notify%20%3D%20%28%29%20%3D%3E%20setTimeout%28as.closeAlert%2C%201150%2C%20name%2C%20as.showAlertNotification%28%0A%09%09%09%22chrome%3A//global/skin/icons/question-48.png%22%2C%20%22%22%2C%20noFound%2C%20false%2C%20%22%22%2C%20null%2C%20name%0A%09%09%29%29%29%28%29%3B%0A%09%7D%0A%0A%7D%29%28%28%29%20%3D%3E%20%7B%0A%0A%09var%20found%2C%20videoMoved%2C%20SEND%20%3D%20msg%20%3D%3E%20%7B%0A%09%09found%20%3D%20true%3B%0A%09%09if%20%28%21msg%20%7C%7C%20Cc%5B%22@mozilla.org/embedcomp/prompt-service%3B1%22%5D%0A%09%09%09.getService%28Ci.nsIPromptService%29%0A%09%09%09.confirm%28content%2C%20null%2C%20decodeURIComponent%28%22CONFIRM%22%29%29%0A%09%09%29%20%7B%0A%09%09%09if%20%28msg%29%20videoMoved%20%3D%20decodeURIComponent%28%22VIDEO_MOVED%22%29%3B%0A%09%09%09sendAsyncMessage%28%22MSG_NAME%22%2C%20msg%29%3B%0A%09%09%7D%0A%09%09else%20return%20true%3B%0A%09%7D%0A%0A%09var%20YoutubeID%20%3D%20/%28%3F%3Ayoutube%28%3F%3A-nocookie%29%3F%5C.com%5C/%28%3F%3A%5B%5E%5C/%5Cn%5Cs%5D+%5C/%5CS+%5C/%7C%28%3F%3Av%7Ce%28%3F%3Ambed%29%3F%29%5C/%7C%5CS*%3F%5B%3F%26%5Dv%3D%29%7Cyoutu%5C.be%5C/%29%28%5Ba-zA-Z0-9_-%5D%7B11%7D%29%28%3F%3A%5CW%7C%24%29/%3B%0A%0A%09var%20tmp%20%3D%20%27%27%2C%0A%09tmpp%20%3D%20%27%27%2C%0A%09innerA%20%3D%20%27%3Cdiv%20style%3D%22display%3Ablock%21important%3Bcolor%3A%2300ff00%21important%3Bwidth%3A250px%21important%3Bfont%3Abold%2016px%20serif%21important%3Bz-index%3A999%21important%3Bopacity%3A1%21important%3Bvisibility%3A%20visible%21important%3B%27%2C%0A%09innerB%20%3D%20%27left%3A5px%21important%3Bposition%3Aabsolute%21important%3Bheight%3Aauto%21important%3Bbox-sizing%3Aborder-box%21important%3Bpadding%3A5px%21important%3Bmargin%3A5px%21important%3B%27%2C%0A%09//stopPl%20%3D%20%22javascript%3A%28function%28%29%7Bv%3Ddocument.getElementById%28%27movie_player%27%29%3Bif%28v%29%7Bv.stopVideo%28%29%7Delse%7Bv%3Ddocument.getElementsByTagName%28%27video%27%29%3Bif%28v%29%7Bv%5B0%5D.src%3D%27%27%3Btry%7Bv%5B0%5D.load%28%29%7Dcatch%28e%29%7B%7D%7D%3B%7D%7D%29%28%29%3B%22%2C%0A%09ytIMGouter%20%3D%20function%20%28ytID%29%20%7B%0A%09%09return%20%27%3Cdiv%20width%3D%22100%25%22%3E%3Cbr%20/%3E%3Ca%20target%3D%22_blank%22%20href%3D%22https%3A//www.youtube.com/watch%3Fv%3D%27%20+%20ytID%20+%20%27%22%3E%3Cimg%20src%3D%22https%3A//i.ytimg.com/vi/%27%20+%20ytID%20+%20%27/hqdefault.jpg%22%3E%3C/a%3E%3Cbr%20/%3E%27%20+%20innerA%20+%20%27background-color%3Ablack%21important%3Bposition%3Arelative%21important%3Bbottom%3A20px%21important%3B%22%3E%26nbsp%3B%26nbsp%3B%27%20+%20videoMoved%20+%20%27%3C/div%3E%3Cbr%20/%3E%3C/div%3E%3Cbr%20/%3E%27%0A%09%7D%2C%0A%09handlWin%20%3D%20function%20%28currentWin%29%20%7B%0A%09%09tmp%20%3D%20%27%27%3B%0A%09%09var%20elem%20%3D%20currentWin.document.getElementsByTagName%28%27video%27%29%2C%0A%09%09currLoc%20%3D%20currentWin.location%3B%0A%09%09if%20%28elem.length%20%3E%200%29%20%7B%0A%09%09%09if%20%28currLoc.hostname.indexOf%28%27youtu%27%29%20%21%3D%20-1%20%26%26%20%28tmp%20%3D%20currLoc.toString%28%29.match%28YoutubeID%29%29%20%26%26%20tmp%5B1%5D.length%20%3D%3D%2011%29%20%7B%0A%0A%09%09%09%09if%20%28SEND%28%27https%3A//www.youtube.com/watch%3Fv%3D%27%20+%20tmp%5B1%5D%29%29%20return%3B%0A%0A%09%09%09%09videoMovedbox%20%3D%20currentWin.document.createElement%28%27videoMoved%27%29%3B%0A%09%09%09%09videoMovedbox.innerHTML%20%3D%20innerA%20+%20innerB%20+%20%27top%3A-15px%21important%3B%22%3E%3Cb%3E%27%20+%20videoMoved%20+%20%27%3C/b%3E%3C/div%3E%27%3B%0A%0A%09%09%09%09//loadURI%28stopPl%29%3B%0A%09%09%09%09%28function%28d%29%7Bvar%20v%3Dd.getElementById%28%27movie_player%27%29%3Bif%28v%29%7Btry%7Bv.stopVideo%28%29%7Dcatch%7B%7D%7D%0A%09%09%09%09%09else%7Bv%3Dd.getElementsByTagName%28%27video%27%29%3Bif%28v%5B0%5D%29%7Bv%5B0%5D.src%3D%27%27%3Btry%7Bv%5B0%5D.load%28%29%7Dcatch%7B%7D%7D%3B%7D%7D%29%28currentWin.document%29%3B%0A%0A%09%09%09%09currentWin.document.getElementById%28%27eow-title%27%29.appendChild%28videoMovedbox%29%3B%0A%09%09%09%09return%20true%3B%0A%09%09%09%7D%3B%0A%09%09%09for%20%28i%20%3D%200%3B%20i%20%3C%20elem.length%3B%20i++%29%20%7B%0A%09%09%09%09if%20%28%28%28tmp%20%3D%20getSrc%28elem%5Bi%5D.parentNode%2C%20currLoc%29%29%20%26%26%20tmp.length%20%3E%202%29%20%7C%7C%20%28i%20%3D%3D%200%20%26%26%20currentWin.document.body.innerHTML.substring%280%2C%207%29%20%3D%3D%20%27%3Cvideo%20%27%20%26%26%20%28tmp%20%3D%20currLoc.toString%28%29%29%29%29%20%7B%0A%0A%09%09%09%09%09if%20%28SEND%28tmp%29%29%20return%3B%0A%0A%09%09%09%09%09videoMovedbox%20%3D%20currentWin.document.createElement%28%27videoMoved%27%29%3B%0A%09%09%09%09%09videoMovedbox.innerHTML%20%3D%20innerA%20+%20innerB%20+%20%27top%3A20px%21important%3Bbackground-color%3Ablack%21important%3B%22%3E%27%20+%20videoMoved%20+%20%27%3C/div%3E%27%3B%0A%0A%09%09%09%09%09if%20%28currLoc.hostname%20%3D%3D%20%27www.youtube.com%27%29%20%7B%0A%09%09%09%09%09%09elem%5Bi%5D.parentNode.parentNode.appendChild%28videoMovedbox%29%3B%0A%09%09%09%09%09%7D%20else%20%7B%0A%09%09%09%09%09%09elem%5Bi%5D.parentNode.appendChild%28videoMovedbox%29%3B%0A%09%09%09%09%09%7D%3B%0A%09%09%09%09%09elem%5Bi%5D.src%20%3D%20%27%27%3B%0A%09%09%09%09%09try%20%7B%0A%09%09%09%09%09%09elem%5Bi%5D.load%28%29%0A%09%09%09%09%09%7D%20catch%20%28e%29%20%7B%7D%3B%0A%09%09%09%09%09return%20true%3B%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%09%09%7D%3B%0A%0A%09%09currentWin._elems%20%3D%20currentWin.document.getElementsByTagName%28%27iframe%27%29%3B%0A%09%09if%20%28currentWin._elems.length%20%3E%200%29%20%7B%0A%09%09%09for%20%28currentWin._iCounter%20%3D%200%3B%20currentWin._iCounter%20%3C%20currentWin._elems.length%3B%20currentWin._iCounter++%29%20%7B%0A%09%09%09%09if%20%28%28currentWin._elems%5BcurrentWin._iCounter%5D.src.indexOf%28%27youtube.com%27%29%20%3E%20-1%29%20%26%26%20%28tmp%20%3D%20currentWin._elems%5BcurrentWin._iCounter%5D.src.match%28YoutubeID%29%29%20%26%26%20%28tmp%5B1%5D.length%20%3D%3D%2011%29%29%20%7B%0A%0A%09%09%09%09if%20%28SEND%28%27https%3A//www.youtube.com/watch%3Fv%3D%27%20+%20tmp%5B1%5D%29%29%20return%3B%0A%0A%09%09%09%09currentWin._elems%5BcurrentWin._iCounter%5D.outerHTML%20%3D%20ytIMGouter%28tmp%5B1%5D%29%3B%0A%09%09%09%09%09return%20true%3B%0A%09%09%09%09%7D%3B%0A%09%09%09%09if%20%28currentWin._elems%5BcurrentWin._iCounter%5D.clientWidth%20%3E%2080%20%26%26%20currentWin._elems%5BcurrentWin._iCounter%5D.clientHeight%20%3E%2040%20%26%26%20handlWin%28currentWin._elems%5BcurrentWin._iCounter%5D.contentWindow%29%29%0A%09%09%09%09%09return%20true%3B%0A%09%09%09%7D%0A%09%09%7D%3B%0A%0A%09%09elem%20%3D%20currentWin.document.getElementsByTagName%28%27object%27%29%3B%0A%09%09currLoc%20%3D%20currentWin.location%3B%0A%09%09if%20%28elem.length%20%3D%3D%200%29%20%7B%0A%09%09%09elem%20%3D%20currentWin.document.getElementsByTagName%28%27embed%27%29%0A%09%09%7D%3B%0A%09%09if%20%28elem.length%20%3E%200%29%20%7B%0A%09%09%09for%20%28i%20%3D%200%3B%20i%20%3C%20elem.length%3B%20i++%29%20%7B%0A%09%09%09%09if%20%28elem%5Bi%5D.innerHTML.indexOf%28%27youtu%27%29%20%21%3D%20-1%20%26%26%20%28tmp%20%3D%20elem%5Bi%5D.innerHTML.match%28YoutubeID%29%29%20%26%26%20tmp%5B1%5D.length%20%3D%3D%2011%29%20%7B%0A%0A%09%09%09%09%09if%20%28SEND%28%27https%3A//www.youtube.com/watch%3Fv%3D%27%20+%20tmp%5B1%5D%29%29%20return%3B%0A%0A%09%09%09%09%09elem%5Bi%5D.outerHTML%20%3D%20ytIMGouter%28tmp%5B1%5D%29%3B%0A%09%09%09%09%09return%20true%3B%0A%09%09%09%09%7D%20else%20%7B%0A%09%09%09%09%09if%20%28elem%5Bi%5D.clientWidth%20%3E%2080%20%26%26%20elem%5Bi%5D.clientHeight%20%3E%2040%29%20%7B%0A%09%09%09%09%09%09if%20%28%28%28tmp%20%3D%20getSrc%28elem%5Bi%5D.parentNode%2C%20currLoc%29%29%20%7C%7C%20%28tmp%20%3D%20getLink%28elem%5Bi%5D%2C%20currLoc%29%29%29%20%26%26%20tmp.length%20%3E%202%29%20%7B%0A%0A%09%09%09%09%09%09%09if%20%28SEND%28tmp%29%29%20return%3B%0A%0A%09%09%09%09%09%09%09elem%5Bi%5D.outerHTML%20%3D%20innerA%20+%20%27background-color%3Ablack%21important%3Bbottom%3A20px%21important%3B%22%3E%26nbsp%3B%26nbsp%3B%27%20+%20videoMoved%20+%20%27%3C/div%3E%27%3B%0A%09%09%09%09%09%09%09return%20true%3B%0A%09%09%09%09%09%09%7D%3B%0A%09%09%09%09%09%7D%3B%0A%09%09%09%09%7D%0A%09%09%09%7D%3B%0A%09%09%7D%3B%0A%09%09return%20false%3B%0A%09%7D%3B%0A%0A%09function%20restProtHost%28lnkR%2C%20curLoc%29%20%7B%0A%09%09if%20%28lnkR.length%20%3D%3D%200%29%0A%09%09%09return%20%27%27%3B%0A%09%09let%20tr%20%3D%20lnkR.replace%28/%5E%3A%5C/%5C//%2C%20curLoc.protocol%20+%20%22//%22%29%3B%0A%09%09if%20%28%21tr.match%28/%5Ehttps%3F%3A%5C/%5C//i%29%29%20%7B%0A%09%09%09lnkR%20%3D%20tr.replace%28/%5E%5C/+/%2C%20%27%27%29%3B%0A%09%09%09if%20%28lnkR.split%28%27/%27%29%5B0%5D.split%28%27%3F%27%29%5B0%5D.split%28%27%23%27%29%5B0%5D.toLowerCase%28%29.match%28/%5E%28%3F%3A%5B-a-z%5Cd%5D+%5C.%29+%5Ba-z%5Cd%5D%7B2%2C6%7D%24/%29%29%20%7B%0A%09%09%09%09tr%20%3D%20curLoc.protocol%20+%20%27//%27%20+%20lnkR%3B%0A%09%09%09%7D%20else%20%7B%0A%09%09%09%09tr%20%3D%20curLoc.protocol%20+%20%27//%27%20+%20curLoc.host%20+%20%22/%22%20+%20lnkR%3B%0A%09%09%09%7D%0A%09%09%7D%3B%0A%09%09return%20tr%3B%0A%09%7D%3B%0A%0A%09function%20getSrc%28vobj%2C%20currentLoc%29%20%7B%0A%09%09var%20t%20%3D%20%27%27%2C%0A%09%09tt%20%3D%20%27%27%3B%0A%09%09if%20%28%28%28%28t%20%3D%20vobj.innerHTML.match%28/%3Cvideo.*%3F%5Cssrc%3D%28%3F%3A%28%3F%3A%27%28%5B%5E%27%5D*%29%27%29%7C%28%3F%3A%22%28%5B%5E%22%5D*%29%22%29%7C%28%5B%5E%5Cs%5D*%29%29/i%29%29%20%26%26%20%28t%29%20%26%26%20%28tt%20%3D%20t%5B1%5D%20%7C%7C%20t%5B2%5D%20%7C%7C%20t%5B3%5D%29%20%26%26%20tt.indexOf%28%27blob%3A%27%29%20%3D%3D%20-1%29%20%7C%7C%20%28%28t%20%3D%20vobj.innerHTML.match%28/%3Csource.*%3F%5Cssrc%3D%28%3F%3A%28%3F%3A%27%28%5B%5E%27%5D*%29%27%29%7C%28%3F%3A%22%28%5B%5E%22%5D*%29%22%29%7C%28%5B%5E%5Cs%5D*%29%29.*%3F%5Cstype%3D%5B%27%22%5D%3Fvideo%5C//i%29%29%20%26%26%20%28t%29%20%26%26%20%28tt%20%3D%20t%5B1%5D%20%7C%7C%20t%5B2%5D%20%7C%7C%20t%5B3%5D%29%29%29%20%26%26%20tt.length%20%3E%202%20%26%26%20tt.indexOf%28%27blob%3A%27%29%20%3D%3D%20-1%29%20%7B%0A%09%09%09if%20%28tt.indexOf%28%22.mp4/%3F%22%29%20%3D%3D%20-1%29%20%7B%0A%09%09%09%09tt%20%3D%20tt.replace%28/%26amp%3B/g%2C%20%22%26%22%29%0A%09%09%09%7D%3B%0A%09%09%09t%20%3D%20restProtHost%28tt%2C%20currentLoc%29%3B%0A%09%09%09return%20t%3B%0A%09%09%7D%3B%0A%09%09return%20%27%27%3B%0A%09%7D%3B%0A%0A%09function%20getLink%28obj%2C%20curLocation%29%20%7B%0A%0A%09%09if%20%28%21obj%20%7C%7C%20%21obj.tagName%29%0A%09%09%09return%20%27%27%3B%0A%09%09q%20%3D%20obj.tagName.toLowerCase%28%29%3B%0A%0A%09%09var%20getParam%20%3D%20function%20%28e%2C%20n%29%20%7B%0A%09%09%09var%20v%20%3D%20%27%27%2C%0A%09%09%09r%20%3D%20new%20RegExp%28%27%5E%28%27%20+%20n%20+%20%27%29%24%27%2C%20%27i%27%29%2C%0A%09%09%09param%20%3D%20e.getElementsByTagName%28%27param%27%29%3B%0A%09%09%09for%20%28var%20igp%20%3D%200%2C%20p%3B%20p%20%3D%20param%5Bigp%5D%3B%20igp++%29%20%7B%0A%09%09%09%09if%20%28p.hasAttribute%28%27name%27%29%20%26%26%20p.getAttribute%28%27name%27%29.match%28r%29%29%20%7B%0A%09%09%09%09%09v%20%3D%20p.getAttribute%28%27value%27%29%3B%0A%09%09%09%09%09break%0A%09%09%09%09%7D%3B%0A%09%09%09%7D%3B%0A%09%09%09return%20v%3B%0A%09%09%7D%3B%0A%0A%09%09var%20restPath%20%3D%20function%20%28f%2C%20s%29%20%7B%0A%09%09%09return%20%28f.substring%280%2C%204%29%20%3D%3D%20%27http%27%29%20%3F%20f%20%3A%20s.replace%28/%5B%23%3F%5D.*%24/%2C%20%27%27%29.replace%28/%5B%5E%5C/%5D*%24/%2C%20f%29%0A%09%09%7D%3B%0A%0A%09%09function%20videoLinkExtract%28fl%29%20%7B%0A%09%09%09alert%28fl%29%3B%0A%09%09%09var%20linkArr%20%3D%20%5B%5D%2C%0A%09%09%09outLinks%20%3D%20%5B%5D%2C%0A%09%09%09jj%20%3D%200%2C%0A%09%09%09lba%20%3D%20%27%27%2C%0A%09%09%09lbb%20%3D%20%27%27%2C%0A%09%09%09decodeURL%20%3D%20gBrowser.currentURI.spec%3B%20%7B%0A%09%09%09%09try%20%7B%0A%09%09%09%09%09return%20decodeURIComponent%28s%29%0A%09%09%09%09%7D%20catch%20%28e%29%20%7B%0A%09%09%09%09%09return%20unescape%28s%29%0A%09%09%09%09%7D%0A%09%09%09%7D%3B%0A%0A%09%09%09for%20%28var%20ij%20%3D%200%3B%20ij%20%3C%203%3B%20ij++%29%20%7B%0A%09%09%09%09lba%20%3D%20lba%20+%20String.fromCharCode%28parseInt%28%28Math.random%28%29%20*%2015%20+%201%29%20+%20%27%27%2C%2010%29%29%3B%0A%09%09%09%09lbb%20%3D%20lbb%20+%20String.fromCharCode%28parseInt%28%28Math.random%28%29%20*%2015%20+%2016%29%20+%20%27%27%2C%2010%29%29%3B%0A%09%09%09%7D%3B%0A%0A%09%09%09function%20pushWithMerit%28lnk%29%20%7B%0A%0A%09%09%09%09var%20merit%20%3D%20-11%3B%0A%09%09%09%09if%20%28lnk.match%28/%5Ehttps%3F%3A%5C/%5C//i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%2040%3B%0A%09%09%09%09if%20%28outLinks.length%20%3D%3D%200%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%201%3B%0A%09%09%09%09if%20%28lnk.match%28/%5E%5C//%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%207%3B%0A%09%09%09%09if%20%28lnk.match%28/%5E%5C/%5C//%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%2030%3B%0A%09%09%09%09if%20%28lnk.match%28/240p%28%5B%5Ea-z%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%201%3B%0A%09%09%09%09if%20%28lnk.match%28/%5B%5Ea-z%5D240%28%5B%5Ea-z0-9%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%201%3B%0A%09%09%09%09if%20%28lnk.match%28/360p%28%5B%5Ea-z%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%203%3B%0A%09%09%09%09if%20%28lnk.match%28/%5B%5Ea-z%5D360%28%5B%5Ea-z0-9%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%203%3B%0A%09%09%09%09if%20%28lnk.match%28/480p%28%5B%5Ea-z%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%205%3B%0A%09%09%09%09if%20%28lnk.match%28/%5B%5Ea-z%5D480%28%5B%5Ea-z0-9%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%205%3B%0A%09%09%09%09if%20%28lnk.match%28/720p%28%5B%5Ea-z%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%207%3B%0A%09%09%09%09if%20%28lnk.match%28/%5B%5Ea-z%5D720%28%5B%5Ea-z0-9%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%207%3B%0A%09%09%09%09if%20%28lnk.match%28/%5C.mp4%28%5B%5Ea-z%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%208%3B%0A%09%09%09%09if%20%28lnk.match%28/_hd%28%5B%5Ea-z%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20+%206%3B%0A%09%09%09%09if%20%28lnk.match%28/%5C.%28jpg%7Cxml%29%28%5B%5Ea-z%5D%7C%24%29/i%29%29%0A%09%09%09%09%09merit%20%3D%20merit%20-%2040%3B%0A%09%09%09%09if%20%28merit%20%3E%200%29%0A%09%09%09%09%09outLinks.push%28merit%20+%20lba%20+%20lnk%29%3B%0A%09%09%09%09Services.console.logStringMessage%28%27merit%3A%27%20+%20merit%20+%20%27%20lnk-%3E%27%20+%20lnk%29%3B%0A%09%09%09%7D%3B%0A%0A%09%09%09linkArr.push%28fl%29%3B%0A%09%09%09while%20%28linkArr.length%20%3E%20jj%20%26%26%20jj%20%3C%2030%29%20%7B%0A%0A%09%09%09%09var%20testPaths%20%3D%20%5B%5D%3B%0A%09%09%09%09testPaths%20%3D%20linkArr%5Bjj%5D.split%28/%28%5C.%28%3F%3Aflv%7Cmp4%7Cm3u8%29%29/i%29%3B%0A%09%09%09%09if%20%28testPaths%5BtestPaths.length%20-%201%5D%20%3D%3D%20%27%27%29%0A%09%09%09%09%09testPaths.pop%28%29%3B%0A%0A%09%09%09%09for%20%28k%20%3D%201%3B%20k%20%3C%20testPaths.length%3B%20k%20%3D%20k%20+%202%29%20%7B%0A%0A%09%09%09%09%09if%20%28testPaths%5Bk%20-%201%5D.indexOf%28lba%29%20%3E%20-1%29%20%7B%0A%09%09%09%09%09%09pref%20%3D%20testPaths%5Bk%20-%201%5D%3B%0A%09%09%09%09%09%7D%20else%20%7B%0A%09%09%09%09%09%09var%20testAboutDom%20%3D%20testPaths%5Bk%20-%201%5D.toLowerCase%28%29.split%28/%28https%3F%3A%5C/%5C/%29/%29%3B%0A%09%09%09%09%09%09if%20%28testAboutDom%5BtestAboutDom.length%20-%201%5D%20%3D%3D%20%27%27%29%0A%09%09%09%09%09%09%09testAboutDom.pop%28%29%3B%0A%09%09%09%09%09%09var%20pTest%20%3D%20testAboutDom%5BtestAboutDom.length%20-%201%5D.split%28/%28%5C%3F%5B%5E%5C%3F%5D*%3F%26%29/%29%3B%0A%09%09%09%09%09%09if%20%28pTest.length%20%3E%202%29%20%7B%0A%09%09%09%09%09%09%09pTest.pop%28%29%3B%0A%09%09%09%09%09%09%09pTest.pop%28%29%3B%0A%09%09%09%09%09%09%7D%3B%0A%09%09%09%09%09%09testAboutDom%5BtestAboutDom.length%20-%201%5D%20%3D%20pTest.join%28%27%27%29%3B%0A%09%09%09%09%09%09pref%20%3D%20testPaths%5Bk%20-%201%5D.substring%28testAboutDom.join%28%27%27%29.lastIndexOf%28%22%26%22%29%20+%201%29%3B%0A%09%09%09%09%09%7D%3B%0A%0A%09%09%09%09%09t2%20%3D%20pref.lastIndexOf%28lbb%29%3B%0A%09%09%09%09%09if%20%28t2%20%3E%20-1%29%20%7B%0A%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%203%29%3B%0A%09%09%09%09%09%7D%20else%20%7B%0A%0A%09%09%09%09%09%09t2%20%3D%20pref.lastIndexOf%28%27%7B%22%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%202%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.lastIndexOf%28%27%5B%22%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%202%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.lastIndexOf%28%27%2C%22%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%202%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.toLowerCase%28%29.lastIndexOf%28%27%22http%3A//%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%201%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.toLowerCase%28%29.lastIndexOf%28%27%22https%3A//%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%201%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.toLowerCase%28%29.lastIndexOf%28%27%2Chttp%3A//%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%201%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.toLowerCase%28%29.lastIndexOf%28%27%2Chttps%3A//%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%201%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.toLowerCase%28%29.lastIndexOf%28%27%3Bhttp%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%201%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.toLowerCase%28%29.lastIndexOf%28%27*https%3A//%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%201%29%3B%0A%09%09%09%09%09%09t2%20%3D%20pref.toLowerCase%28%29.lastIndexOf%28%27%20or%20%27%29%3B%0A%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%204%29%3B%0A%0A%09%09%09%09%09%09pref%20%3D%20pref.substring%28pref.split%28%27/%27%29%5B0%5D.toLowerCase%28%29.split%28%27%252f%27%29%5B0%5D.lastIndexOf%28%27%3D%27%29%20+%201%29%3B%0A%0A%09%09%09%09%09%7D%0A%0A%09%09%09%09%09if%20%28pref.length%20%3E%200%29%20%7B%0A%0A%09%09%09%09%09%09if%20%28pref.split%28%27%3F%27%29%5B0%5D.toLowerCase%28%29.match%28/%25%5B2-3%5D%5B0-9a-f%5D/%29%29%20%7B%0A%0A%09%09%09%09%09%09%09t2%20%3D%20pref.indexOf%28%27%22%27%29%0A%09%09%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%201%29%3B%0A%09%09%09%09%09%09%09%09suff%20%3D%20testPaths%5Bk%20+%201%5D%20%3F%20testPaths%5Bk%20+%201%5D.split%28%27%26%27%29%5B0%5D.split%28%27%22%27%29%5B0%5D.split%28%27%3B%27%29%5B0%5D.split%28/%2Chttp/i%29%5B0%5D%20%3A%20%27%27%3B%0A%09%09%09%09%09%09%09if%20%28%28suff%20%21%3D%20testPaths%5Bk%20+%201%5D%29%20%7C%7C%20%28testPaths.length%20%3C%20k%20+%203%29%29%20%7B%0A%09%09%09%09%09%09%09%09if%20%28testPaths.length%20%3E%20k%20+%201%29%20%7B%0A%09%09%09%09%09%09%09%09%09testPaths%5Bk%20+%201%5D%20%3D%20%28%28pref%20%3D%3D%20testPaths%5Bk%20-%201%5D%29%20%3F%20%27%27%20%3A%20%27%26%27%29%20+%20testPaths%5Bk%20+%201%5D.substr%28suff.length%29%0A%09%09%09%09%09%09%09%09%7D%3B%0A%09%09%09%09%09%09%09%09t2%20%3D%20pref.lastIndexOf%28lba%29%3B%0A%09%09%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%203%29%0A%09%09%09%09%09%09%09%09%09%09linkArr.push%28decodeURL%28pref%20+%20testPaths%5Bk%5D%20+%20suff%29%29%3B%0A%0A%09%09%09%09%09%09%09%7D%20else%20%7B%0A%09%09%09%09%09%09%09%09testPaths%5Bk%20+%201%5D%20%3D%20%28pref%20%3D%3D%20testPaths%5Bk%20-%201%5D%20%3F%20%27%27%20%3A%20lbb%29%20+%20pref%20+%20testPaths%5Bk%5D%20+%20suff%0A%09%09%09%09%09%09%09%7D%0A%09%09%09%09%09%09%7D%20else%20%7B%0A%09%09%09%09%09%09%09suff%20%3D%20testPaths%5Bk%20+%201%5D%20%3F%20testPaths%5Bk%20+%201%5D.split%28%27%3B%27%29%5B0%5D.split%28%27%22%5D%27%29%5B0%5D.split%28%27%22%7D%27%29%5B0%5D.split%28%27%22%2C%27%29%5B0%5D.split%28/%2Chttps%3F%3A%5C/%5C//i%29%5B0%5D.split%28%27*https%3A//%27%29%5B0%5D.split%28%27%20or%20%27%29%5B0%5D%20%3A%20%27%27%3B%0A%09%09%09%09%09%09%09t2%20%3D%20suff.indexOf%28%27%26%27%29%3B%0A%09%09%09%09%09%09%09if%20%28%28t2%20%3E%20-1%29%20%26%26%20%28pref%20%21%3D%20testPaths%5Bk%20-%201%5D%29%29%20%7B%0A%09%09%09%09%09%09%09%09if%20%28t2%20%3D%3D%200%29%0A%09%09%09%09%09%09%09%09%09suff%20%3D%20%27%27%3B%0A%09%09%09%09%09%09%09%09if%20%28suff.charAt%280%29%20%21%3D%20%27%3F%27%29%0A%09%09%09%09%09%09%09%09%09suff%20%3D%20suff.split%28/%28%26%5B%5E%26%5D+%3Dhttps%3F%3A%5C/%5C/%29/i%29%5B0%5D%3B%0A%09%09%09%09%09%09%09%7D%3B%0A%09%09%09%09%09%09%09if%20%28%28suff%20%21%3D%20testPaths%5Bk%20+%201%5D%29%20%7C%7C%20%28testPaths.length%20%3C%20k%20+%203%29%29%20%7B%0A%09%09%09%09%09%09%09%09if%20%28testPaths.length%20%3E%20k%20+%201%29%20%7B%0A%09%09%09%09%09%09%09%09%09testPaths%5Bk%20+%201%5D%20%3D%20%28%28pref%20%3D%3D%20testPaths%5Bk%20-%201%5D%29%20%3F%20%27%27%20%3A%20%27%26%27%29%20+%20testPaths%5Bk%20+%201%5D.substr%28suff.length%29%0A%09%09%09%09%09%09%09%09%7D%3B%0A%09%09%09%09%09%09%09%09t2%20%3D%20pref.lastIndexOf%28lba%29%3B%0A%09%09%09%09%09%09%09%09if%20%28t2%20%3E%20-1%29%0A%09%09%09%09%09%09%09%09%09pref%20%3D%20pref.substring%28t2%20+%203%29%3B%0A%09%09%09%09%09%09%09%09pushWithMerit%28pref%20+%20testPaths%5Bk%5D%20+%20suff%29%3B%0A%0A%09%09%09%09%09%09%09%7D%20else%20%7B%0A%09%09%09%09%09%09%09%09testPaths%5Bk%20+%201%5D%20%3D%20lba%20+%20%28pref%20%3D%3D%20testPaths%5Bk%20-%201%5D%20%3F%20%27%27%20%3A%20lbb%29%20+%20pref%20+%20testPaths%5Bk%5D%20+%20suff%0A%09%09%09%09%09%09%09%7D%0A%09%09%09%09%09%09%7D%0A%09%09%09%09%09%7D%0A%09%09%09%09%7D%3B%0A%09%09%09%09jj%20%3D%20jj%20+%201%3B%0A%09%09%09%7D%3B%0A%0A%09%09%09if%20%28outLinks.length%20%3D%3D%200%29%0A%09%09%09%09return%20%27%27%3B%0A%09%09%09function%20srt%28a%2C%20b%29%20%7B%0A%09%09%09%09a%20%3D%20parseInt%28a.substr%280%2C%20a.indexOf%28lba%29%29%2C%2010%29%3B%0A%09%09%09%09b%20%3D%20parseInt%28b.substr%280%2C%20b.indexOf%28lba%29%29%2C%2010%29%3B%0A%09%09%09%09if%20%28a%20%3C%20b%29%0A%09%09%09%09%09return%201%3B%0A%09%09%09%09if%20%28a%20%3E%20b%29%0A%09%09%09%09%09return%20-1%3B%0A%09%09%09%09return%200%0A%09%09%09%7D%3B%0A%09%09%09outLinks.sort%28srt%29%3B%0A%09%09%09outLinks%5B0%5D%20%3D%20outLinks%5B0%5D.substr%28outLinks%5B0%5D.indexOf%28lba%29%20+%203%29%0A%09%09%09%09if%20%28outLinks%5B0%5D.indexOf%28%27_hq.mp4/%3Ftime%3D%27%29%20%3E%200%29%0A%09%09%09%09%09outLinks%5B0%5D%20%3D%20outLinks%5B0%5D.replace%28/%26/g%2C%20%27%26amp%3B%27%29%3B%0A%09%09%09%09return%20outLinks%5B0%5D%3B%0A%09%09%7D%3B%0A%0A%09%09if%20%28%21ol%29%0A%09%09%09return%20%27%27%3B%0A%09%09//ol%20%3D%20ol.replace%28/%5E%3A%3F%5C/%5C//%2C%20curLocation.protocol%20+%20%22//%22%29%3B%0A%09%09//return%20restPath%28ol%2C%20src%29%3B%0A%09%09return%20restProtHost%28ol%2C%20curLocation%29%3B%0A%09%7D%3B%0A%0A%09try%20%7BhandlWin%28content%29%3B%7D%20finally%20%7Bfound%20%7C%7C%20SEND%28%29%3B%7D%0A%7D%29%3B%0A%0A%20var%20style%20%3D%20custombutton.buttonGetHelp%28self%29.replace%28/id/g%2C%20_id%29%3B%0Avar%20uri%20%3D%20makeURI%28%27data%3Atext/css%2C%27+%20encodeURIComponent%28style%29%29%3B%0Avar%20sss%20%3D%20Cc%5B%22@mozilla.org/content/style-sheet-service%3B1%22%5D.getService%28Ci.nsIStyleSheetService%29%3B%0Asss.loadAndRegisterSheet%28uri%2C%200%29%3B%20%5D%5D%3E%3C/initcode%3E%0A%20%20%3Ccode%3E%3C%21%5BCDATA%5B/*CODE*/%5D%5D%3E%3C/code%3E%0A%20%20%3Caccelkey%3E%3C%21%5BCDATA%5B%5D%5D%3E%3C/accelkey%3E%0A%20%20%3Chelp%3E%3C%21%5BCDATA%5B%5D%5D%3E%3C/help%3E%0A%20%20%3Cattributes/%3E%0A%3C/custombutton%3E

Отсутствует

 

№1642410-05-2022 21:37:21

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

Re: Custom Buttons

manuk пишет

не так, наверно

Да нет, заменил правильно, всё как и предложено.
Строка this.onauxclick = …, надо полагать, удалена за ненадобностью, но это и не важно.


Тогда не знаю, может таймаут поднять с 500 до сколько-то побольше.
Интересно, если после rmb || args.push("/add");
добавить alert(rmb); то, после ПКМ, неужели покажет не true?

Отсутствует

 

№1642510-05-2022 21:54:16

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

Re: Custom Buttons

Dumby, изменил таймаут на 3500 (от балды) и всё стало работать, как я хотел. ЛКМ добавляет в плейлист, а ПКМ открывает новый плейлист и запускает. Большое спасибо. Я, лох, две кнопки для этого держал.
    P.S. Ещё бы, перфекционизма ради, в контекстном меню два пункта было - для ЛКМ и ПКМ.:)

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

Выделить код

Код:

(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, 3500);
		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);

Отредактировано manuk (10-05-2022 23:09:45)

Отсутствует

 

Board footer

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