Setting Context Item position in Firefox addons SDK - firefox-addon

I'm writing an extension that involving adding an item to Firefox's context menu, but it appends to the end of the menu and I couldn't find any pointers customizing item's position using Addon SDK (insertBefore/insertAfter), I know how this can be done using XUL, but I'm trying to do it using Addon SDK or some sort of Addon SDK/XUL combination
This is the code snippet related to context menu
main.js
var pageMod = require("sdk/page-mod");
var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
var cm = require("sdk/context-menu");
pageMod.PageMod({
include: "*.youtube.com",
contentScriptFile: data.url("page.js"),
onAttach: function (worker) {
worker.port.emit('link', data.url('convertbutton.png'));
}});
cm.Item({
label: "Convert File",
image: data.url("bighdconverterlogo128png.png"),
context: [
cm.URLContext(["*.youtube.com"]),
cm.PageContext()
],
contentScriptFile: data.url("menu.js"),
onMessage: function(vUrl){
tabs.open(vUrl);
}
});
data/menu.js
self.on("click", function(){
self.postMessage('http://hdconverter.co/' + 'c.php?url=' + window.location.href);
});
Thanks

i dont know about sdk but for non-sdk addons its easy. but because you dont have the boiler plate setup its going to look long. add this code to your addon at the bottom:
var positionToInsertMenu = 0; //set the position you want it at here
var myLabelText = 'Convert File';
const {interfaces: Ci,utils: Cu} = Components;
Cu.import('resource://gre/modules/Services.jsm');
/*start - windowlistener*/
var windowListener = {
//DO NOT EDIT HERE
onOpenWindow: function (aXULWindow) {
// Wait for the window to finish loading
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
aDOMWindow.addEventListener("load", function () {
aDOMWindow.removeEventListener("load", arguments.callee, false);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}, false);
},
onCloseWindow: function (aXULWindow) {},
onWindowTitleChange: function (aXULWindow, aNewTitle) {},
register: function () {
// Load into any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}
// Listen to new windows
Services.wm.addListener(windowListener);
},
unregister: function () {
// Unload from any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.unloadFromWindow(aDOMWindow, aXULWindow);
}
//Stop listening so future added windows dont get this attached
Services.wm.removeListener(windowListener);
},
//END - DO NOT EDIT HERE
loadIntoWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
var contentAreaContextMenu = aDOMWindow.document.getElementById('contentAreaContextMenu');
var myMenuItem;
if (contentAreaContextMenu) {
var menuItems = contentAreaContextMenu.querySelector('menuitem');
[].forEach.call(menuItems, function(item) {
if (item.getAttribute('label') == myLabelText) {
myMenuItem = item;
}
});
contentAreaContextMenu.removeChild(myMenuItem);
if (contentAreaContextMenu.childNodes.length >= positionToInsertMenu) { //position is greater then number of childNodes so append to end
contentAreaContextMenu.appendChild(myMenuItem);
} else {
contentAreaContextMenu.insertBefore(myMenuItem, contentAreaContextMenu.childNodes[thePosition]);
}
}
},
unloadFromWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
var myMenuItem = aDOMWindow.document.getElementById('myMenuItem');
if (myMenuItem) {
myMenuItem.parentNode.removeChild(myMenuItem);
}
}
};
windowListener.register();
on unload of your addon add this:
windowListener.unregister();
i copied pasted from a template and modded it real fast. for position to be accurate you probably have to consider which menuitems are hidden and which are not

Related

indexeddb on IOS devices

I have a problem with an indexeddb query with index when running on IOS devices.
$.indexedDB(dbName).objectStore(tablename).index("INDICE").each(function(itemLocal) {
itemLocal.delete();
}, [VALORINDICE]).then(function() {
callback();
}, function() {
console.log("error");
});
The problem is if there is more than one record that matches the index, it does not eliminate them, it eliminates the first one and leaves. But if for example I put console.log (itemLocal) instead of itemLocal.delete() if it shows all those that match the index. Any suggestions of something that may be leaking?
I have tried with this code and I get the same error(code without api jquery)
var request = indexedDB.open(DATABASE_NAME);
request.onsuccess = function(event) {
var db = request.result;
var transaction = db.transaction(["TABLE"], "readwrite");
var table = transaction.objectStore("TABLE");
var index = table.index("INDEX");
var req = index.openCursor();
req.onsuccess = function() {
var cursor = req.result;
if (cursor) {
console.info(cursor.value);
cursor["delete"]();
cursor["continue"]();
}
};
req.onerror = function(e) {
console.error(e, req);
};
};
request.onerror = function(e) {
console.error(e, request);
};

Read a GeometryCollection from a string

in OpenLayers 3 I can read Features or a single Geometry from a GeoJSON string through an instance of ol.format.GeoJSON().
Example:
var parser = new ol.format.GeoJSON();
var arrOfFeatures = parser.readFeatures(geoJsonString);
//or
var usrGeom = parser.readGeometry(geoJsonString);
Now I have a string representing a GeometryCollection (not a MultiPoint, MultiLine or MultyPolygon, nor a FeatureCollection).
I cannot find a way to readGeometryCollection(GeoJSONstring) or something, having an ol.geom.GeometryCollection object as the result.
Can anyone help with this?
Thanks in advance.
Ok, the solution was to read a GeometryCollection just like any other Openlayers ol.geom.Geometry and then loop to read any single geometry, so: 1)read the string into a Geometry object, 2) if .getType() is "GeometryCollection" then loop the collection, and forEach(item) in the collection read the Geometry.
parseSingleGeometry : function(geometry, callback) {
switch (geometry.getType()) {
case 'MultiLineString':
geometry.getLineStrings().forEach(function (p) {
var newFeat = new ol.Feature({ geometry: p });
_features.push(newFeat);
});
break;
case 'MultiPolygon':
geometry.getPolygons().forEach(function (p) {
var newFeat = new ol.Feature({ geometry: p });
_features.push(newFeat);
});
break;
case 'Polygon':
case 'LineString':
case 'Point':
var newFeat = new ol.Feature({ geometry: geometry });
_features.push(newFeat);
delete newFeat;
break;
default:
break;
}
if (callback) { callback(); }
},
parseGeometryCollection: function(collection, callback) {
var ciccio = new ol.geom.GeometryCollection();
//ciccio.getType
var itemCount = ciccio.getGeometries().length;
var itemProg = 0;
ciccio.getGeometries().forEach(function (item) {
parseSingleGeometry(item, function () {
itemProg++;
if (itemProg == itemCount) {
if (callback) {
callback();
}
}
})
});
}
[...]
importGeoJson: function (geoJsonString) {
var parser = new ol.format.GeoJSON();
var projection = parser.readProjection(geoJsonString);
var usrProjCode = projection.getCode();
var convert = false;
if (usrProjCode != 'EPSG:3857') {
var msg = "The file provided is in the " + usrProjCode + " projection. The system supports the EPSG:3857 projection only.\n\rWe are converting your geometries in EPSG:3857.\n\rPlease visually check that the added shapes are what your file defines.";
alert(msg);
convert = true;
}
//if it's a feature or feature collection than I can readFeatures()
if (geoJsonString.indexOf("Feature") !== -1) {
console.log("reading feature(s)");
var arrOfFeatures = parser.readFeatures(geoJsonString);
arrOfFeatures.forEach(function (item) {
var usrGeom = item.getGeometry();
var newGeom = (convert) ? usrGeom.clone().transform(usrProjCode, 'EPSG:3857') : usrGeom;
if (newGeom.getType() == "GeometryCollection") {
_private.parseGeometryCollection(newGeom, function () { /*do something*/ })
}
else {
_private.parseSingleGeometry(newGeom, function () { /*do something*/ });
}
});
}
else { //otherwise work with geometries
var usrGeom = parser.readGeometry(geoJsonString);
var newGeom = (convert) ? usrGeom.clone().transform(usrProjCode, 'EPSG:3857') : newGeom;
if (newGeom.getType() == "GeometryCollection") {
_private.parseGeometryCollection(newGeom, function () { /*do something*/ })
}
else {
_private.parseSingleGeometry(newGeom, function () { /*do something*/ });
}
}
},
_features is the ol.Collection containing the features showed on the map.

for embedded PDFs, can PDFJS support both scrolling and jump to a page number at the same time?

This seems like it should be very standard behavior.
I can display a scrollable PDF with:
var container = document.getElementById('viewerContainer');
var pdfViewer = new PDFJS.PDFViewer({
container: container,
});
PDFJS.getDocument(DEFAULT_URL).then(function (pdfDocument) {
pdfViewer.setDocument(pdfDocument);
});
and I can display the PDF page by page with something like:
PDFJS.getDocument(URL_ANNOTATED_PDF_EXAMPLE).then(function getPdfHelloWorld(pdf) {
pdf.getPage(pageNumber).then(function getPageHelloWorld(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
But can't seem to find any reference in the API to both allow scrolling and jumping to a particular page, besides:
pdfViewer.currentPageNumber = 3;
which doesn't work...
So I found a way to make this work (mixed with a little Angular code, "$scope.$watch...") I now have other problems with font decoding. But here is a solution that might help someone else.
var me = this;
PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK;
var container = document.getElementById('capso-court-document__container');
function renderPDF(url, container) {
function renderPage(page) {
var SCALE = 1;
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: page.pageIndex + 1,
scale: SCALE,
defaultViewport: page.getViewport(SCALE),
textLayerFactory: new PDFJS.DefaultTextLayerFactory(),
annotationLayerFactory: new PDFJS.DefaultAnnotationLayerFactory()
});
pdfPageView.setPdfPage(page);
return pdfPageView.draw();
}
function renderPages(pdfDoc) {
var pageLoadPromises = [];
for (var num = 1; num <= pdfDoc.numPages; num++) {
pageLoadPromises.push(pdfDoc.getPage(num).then(renderPage));
}
return $q.all(pageLoadPromises);
}
PDFJS.disableWorker = true;
return PDFJS.getDocument(url)
.then(renderPages);
}
$scope.$watch(function() {
return {
filingUrl: me.filingUrl,
whenPageSelected: me.whenPageSelected,
};
}, function(newVal, oldVal) {
if (newVal.filingUrl) {
//newVal.filingUrl = URL_EXAMPLE_PDF_ANNOTATED;
//newVal.filingUrl = URL_EXAMPLE_PDF_ANNOTATED_2;
//newVal.filingUrl = URL_EXAMPLE_PDF_MULTI_PAGE;
if (newVal.filingUrl !== oldVal.filingUrl &&
newVal.whenPageSelected &&
newVal.whenPageSelected.page) {
scrollToPage(newVal.whenPageSelected.page);
}
//HACK - create new container for each newly displayed PDF
container.innerHTML = '';
var newContainerForNewPdfSelection = document.createElement('div');
container.appendChild(newContainerForNewPdfSelection);
renderPDF(newVal.filingUrl, newContainerForNewPdfSelection).then(function() {
if (newVal.whenPageSelected &&
newVal.whenPageSelected.page) {
scrollToPage(newVal.whenPageSelected.page);
}
});
}
}, true);
function scrollToPage(pageNumber) {
var pageContainer = document.getElementById('pageContainer' + pageNumber);
if (pageContainer) {
container.scrollTop = pageContainer.offsetTop;
} else {
console.warn('pdf pageContainer doesn\'t exist for index', pageNumber);
}
}

addEventListener error in IE8

I'm getting an error message in ie8: Object doesn't support property or method 'addEventListener'. How can I fix this? I've seen adding an else statementand changing addEventListener to attachEvent. However, I am a bit green in the land of js and not sure where that should go, I did try a few ways.
$(document).ready(function() {
// Off canvas menu
var $slider = document.querySelector('#slider');
var $toggle = document.querySelector('.toggle-nav');
var $toggle2 = document.querySelector('nav .toggle-nav');
// var $link = document.querySelector('.link > a');
$toggle.addEventListener('click', function() {
var isOpen = $slider.classList.contains('slide-in');
$slider.setAttribute('class', isOpen ? 'slide-out' : 'slide-in');
$('#slider').animate({'right': '100%'}, 400);
});
$toggle2.addEventListener('click', function() {
var isOpen = $slider.classList.contains('slide-in');
$slider.setAttribute('class', isOpen ? 'slide-out' : 'slide-in');
$('#slider').animate({'right': '0'}, 300);
});
var toggleDataAttr = function(parentElem, toggleElem, opt1, opt2, dataAttr) {
var toggleElem = parentElem.querySelector(toggleElem);
toggleElem.setAttribute(dataAttr, toggleElem.getAttribute(dataAttr) === opt1 ? opt2 : opt1);
};
var toggle_li = document.querySelectorAll('li');
for (var i = 0; i < toggle_li.length; i++) {
toggle_li[i].onclick = function() {
toggleDataAttr(this, '.toggleContent', 'closed', 'open', 'data-state');
toggleDataAttr(this, '.toggleIcon', 'down', 'up', 'data-icon');
};
}
});
addEventListener() is not supported in IE8 and lower (more info here: http://www.w3schools.com/jsref/met_document_addeventlistener.asp), instead you need to use attachEvent() in these browsers.
You can use it like this:
if(element.addEventListener()) {
element.addEventListener('click', myFunction(), true);
} else if(element.attachEvent()) {
element.attachEvent('click', myFunction());
}
This way to do it makes it multi-browser compatible.

Select2 - adding custom choice in single selection mode (tags:false)

Is it possible to setup select2 control to accept custom choice
in single selection mode (tags:false) ?
When typing your choice in the search box instead of seeing "No results found",
just see what you typing in and select it by click or pressing enter.
Well - I finally found solution myself.
I override original SelectAdapter adding new choice every time you type in
and delete previous temporary choices:
$.fn.select2.amd.require(['select2/data/select', 'select2/utils'],
function (SelectAdapter, Utils) {
function XSelectAdapter($element, options) {
XSelectAdapter.__super__.constructor.call(this, $element, options);
}
Utils.Extend(XSelectAdapter, SelectAdapter);
XSelectAdapter.prototype.query = function (params, callback) {
var data = [];
var self = this;
var $options = this.$element.children();
var have_exact_match = false;
$options.each(function () {
var $option = $(this);
if (!$option.is('option') && !$option.is('optgroup')) {
return;
}
var option = self.item($option);
if (option.xtemp === true) {
$(this).remove(); // previously typed-in choice - delete
return;
}
if (option.term === params.term) {
have_exact_match = true; // will not choice if have exact match
}
var matches = self.matches(params, option);
if (matches !== null) {
data.push(matches);
}
});
if (!have_exact_match) {
self.addOptions(this.option({selected: false, id: params.term, text: params.term, xtemp: true}));
data.push({selected: false, id: params.term, text: params.term});
}
callback({
results: data
});
};
$('#your_select2').select2({dataAdapter:XSelectAdapter});
});

Resources