Using Stored Data to Define Sub Menu Entries - google-chrome-extension

My extension should use the user's options to build submenus under the main extension context menu entry. The options are stored in a table, where each line is defining a submenu. The whole table is stored as a json string in chrome.local.storage with the key jsondata.
The manifest is:
"background": {
"persistent": true,
"scripts": [ "js/storage.js", "js/backgroundlib.js", "js/background.js" ]
},
...
"permissions": [ "storage", "contextMenus", "http://*/*", "https://*/*", "tabs", "clipboardRead", "clipboardWrite" ],
...
In the background script, I'm trying to get the data using:
window.addEventListener('load', function () {
var key = 'jsondata';
storage.area.get(key, function (items){
console.log(items[key]);
build_submenu(items[key]);});
});
function build_submenu(json) {
console.log("build_submenu: " + json);
}
and build_submenu should then call multiple chrome.contextMenus.create({... }) to add the submenus.
For now, I can't get build_submenu being called. Am I trying to do something that is not possible or am I just missing something obvious?
Thanks, F.

Replace storage.area.get with chrome.storage.local.get.
Another suggestion would be removing the outer window.onload listener, since you are using background scripts and window.onload makes no sense.

OK, I finally got this, that works:
manifest.json
"background": {
"persistent": false,
"scripts": [ "js/storage.js", "js/backgroundlib.js", "js/background.js" ]
},
in background.js, The context menu is build in the callback function when reading from storage. This reading is called when onInstalled is fired.
I use a global var that is saved onSuspend et read again onStartup. and that associate the submenu id and the corresponding row from the user's option. The onClick listener test if the global variable is defined. If not it is read again from storage.
var regex = new Object();
chrome.runtime.onInstalled.addListener( function () {
console.log("onInstalled called");
var key = 'jsondata';
storage.area.get(key, function (items){ get_jsondata(items[key]);});
function get_jsondata(value){
var data = JSON.parse(value);
var fcb =[ {fcb_context: "fcb_copy", title:"Copy filtered", context: ["selection", "link"]}, {fcb_context:"fcb_paste", context:["editable"], title:"Paste filtered"}];
for (var i=0; i<fcb.length; i++) {
var menu = fcb[i];
chrome.contextMenus.create({
//title: "Look up: %s",
title: menu.title,
id: menu.fcb_context,
contexts: menu.context,
});
var last = data.length;
//var sel = info.selectionText;
for (var j=0; j<last; j++){
chrome.contextMenus.create({
title: data[j].name,
contexts: menu.context,
id: menu.fcb_context + "_" + j,
parentId: menu.fcb_context,
//onclick: function(info, tab){ run_cmd( data[j].regex, info, menu.fcb_context ); }
});
regex[ menu.fcb_context + "_" + j] = data[j];
//console.log(regex[menu.fcb_context + "_" + j]);
}// for j
} // for i
}//get_jsondata
}); //add listener
chrome.contextMenus.onClicked.addListener(function(info, tabs){
var id = info.menuItemId;
if (typeof regex === "undefined" ){
storage.area.get("regex", function(items){
regex = JSON.parse(items["regex"]);
console.log("get " + items["regex"] + " from storage");
run_cmd( regex, info );
});
} else {
console.log("regex was defined... " + JSON.stringify(regex));
run_cmd( regex, info );
}
});
chrome.runtime.onSuspend.addListener(function() {
// Do some simple clean-up tasks.
console.log("onSuspend called saving " + JSON.stringify(regex));
storage.area.set({ "regex" : JSON.stringify(regex)}, function(){console.log("regex saved");} );
});
chrome.runtime.onStartup.addListener(function() {
console.log("onStartup called");
storage.area.get("regex", function(items){
regex = JSON.parse(items["regex"]);
console.log("get " + items["regex"] + " from storage");
});
});
function getSelectedText(info){
var sel = info.selectionText;
chrome.tabs.executeScript(null, {file:"js/script.js"});
}
function pasteFilteredText(info){
chrome.tabs.executeScript(null, {file:"js/script.js"});
}
function run_cmd(regex, info){
var id = info.menuItemId;
var data = regex[id];
var sel = info.selectionText;
var fcb_context = info.parentMenuItemId;
//console.log("run_cmd regex " + data.regex + " sel " + (sel ? sel : ""));
alert("run_cmd regex " + data.regex + " sel " + (sel ? sel : "") + " fcb_context: " + fcb_context);
}
Thanks for pointing me what is superfluous or missing.

Related

place a marker on leaflet map from mongodb coordinates

I would like to place markers on a leaflet map but from a database,
I would like to save on mongodb the lat and long and show 'em like markers on my map, is that possible?
Create a request for example with JQuery Ajax:
$.ajax({url: "/your_data_provider.php", success: function(result){
//result = JSON.parse(result); // If your result is not a json Object.
// It depends on what your data looks like
//Example 1: Lat and Lng has a own field in the db
result.forEach(function(data){
var lat = data.lat;
var lng = data.lng;
var marker = L.marker([lat, lng]).addTo(map);
});
//Example 2: you have one geojson-data string field "geo" in db
// Before inserting in db create a featuregroup `var fg = L.featureGroup();`
// and add all markers to the group `marker.addto(fg);`.
// Then you can call `var datageo = fg.toGeoJSON();` and add this datageo to the db in the field "geo"
result.forEach(function(data){
var geo = data.geo;
L.geoJSON(geo).addTo(map);
});
//https://leafletjs.com/examples/geojson/
},
error: function(xhr){
alert("An error occured: " + xhr.status + " " + xhr.statusText);
}});
});
Also you need a data provider. You can create a REST-Api www.url.com/data/postions/ or calling directly for example a php file.
php file:
// I have never used MongoDB, you have to code your own request. I copied it.
<?php
header('Content-type:application/json;charset=utf-8');
try {
$mng = new MongoDB\Driver\Manager("mongodb://localhost:27017");
$listdatabases = new MongoDB\Driver\Command(["listDatabases" => 1]);
$res = $mng->executeCommand("admin", $listdatabases);
$databases = current($res->toArray());
$result = new array();
$x = 0;
foreach ($databases->databases as $el) {
$result[$x]['id'] = $el->id;
//Example 1:
$result[$x]['lat'] = $el->lat;
$result[$x]['lng'] = $el->lng;
//Example 2:
$result[$x]['geo'] = $el->geo;
$x++;
}
echo json_encode($result);
} catch (MongoDB\Driver\Exception\Exception $e) {
$error = new array();
$error['exception'] = $e->getMessage();
$error['line'] = $e->getLine();
echo json_encode($error);
}
?>

I'm sending a generated pdf from my server to the client as a base64 pdf string. I'm trying to print it on the client side using printJS

I'm generating a report using fluentreports on my server and sending the base64 pdf string to the client on the callback. On the client once he/she receives the base64 string, I am required to print out this pdf string as a pdf which I am trying to accomplish using printJS. I also tried pdfMake but neither one wanted to work. If I console.log the base64 string and click on it, the pdf opens beautifully in the next tab but once I try to print it using printJS or pdfMake it opens a new tab and automatically closes it without doing anything. Is there any other way I could accomplish this? I've tried a lot of things already from reading up on other peoples' issues online and haven't gotten anywhere. Is there any library out there that can create a PDF document using a base64 pdf string so that I can use printJS to print out the document?
Function on the client that sends info to the server and receives back the pdf string:
submit: function () {
this.$Socket.emit('addrepair', {
CustomerID: this.$route.params.Customer.CustomerID,
Problem: this.problem,
BrandID: this.brand,
TypeID: this.type,
Model: this.model,
ColorID: this.color,
Warranty: this.convertbool(this.warranty),
Purchased: this.convertbool(this.purchase),
RushService: this.convertbool(this.rush),
DateReceived: this.datereceived,
UserID: this.UserID
}, (data) => {
if(data.authenticated==true)
{
//window.open(data.pdf)
//pdfMake.createPdf(this.convertDataURIToBinary(data.pdf)).print()
console.log(data.pdf)
printJS({printable: data.pdf, type: 'pdf'})
this.jobdialog=true
}
})
Function on the server that serves the pdf base64 string:
socket.on('addrepair', (data, callbackfn) => {
let query="INSERT INTO repair(CustomerID, Problem, BrandID, Model, ColorID, Warranty, Purchased, RushService, DateReceived, TypeID, UserID) VALUES (" + data.CustomerID + ", \'" + data.Problem + "\', " + data.BrandID + ", \'" + data.Model + "\', " + data.ColorID + ", " + data.Warranty + ", " + data.Purchased + ", " + data.RushService + ", \'" + data.DateReceived + "\', " + data.TypeID + ", " + data.UserID + ");"
con.query(query, function(err) {
if(err) {
throw err
}
else
{
query="SELECT RepairID, FirstName, LastName, Address, PhoneNumber, RushService, Purchased, DateReceived, Problem, Model, (SELECT Type from types WHERE repair.TypeID=types.TypeID) as Type, (SELECT Color from colors WHERE repair.ColorID=colors.ColorID) as Color, (SELECT Brand from brands WHERE repair.BrandID=brands.BrandID) as Brand, Warranty from repair INNER JOIN customer ON repair.CustomerID=customer.CustomerID WHERE repair.RepairID=(SELECT LAST_INSERT_ID())"
con.query(query, function(err, rows) {
if(err) {
throw err
}
else
{
var options = {
data: rows
}
//var myreport = new Report("buffer", options)
var myreport=new Report.Report("buffer", options)
.data(rows)
.pageHeader(repairheaderFunction)
.detail(repairdetailFunction)
.pageFooter(repairfooterFunction)
myreport.render(function (err, data) {
callbackfn({authenticated: true, pdf: 'data:application/pdf;base64,' + data.toString('base64')})
})
//callbackfn({authenticated: true, data: rows})
}
})
}
})
})
var repairheaderFunction = function(Report, data) {
};
var repairdetailFunction = function(Report, data) {
Report.print("#" + data.RepairID, {fontSize: 22, bold: true, underline:true, align: "center"});
Report.newLine(2);
Report.print('First Name: ' + data.FirstName + "\n")
Report.print('Last Name: ' + data.LastName + "\n")
Report.print('Address: ' + data.Address + "\n")
Report.print('Phone Number: ' + data.PhoneNumber + "\n")
Report.print('Brand: ' + data.Brand + "\n")
Report.print('Model: ' + data.Model + "\n")
Report.print('Color: ' + data.Color + "\n")
Report.print('Problem: ' + data.Problem + "\n")
Report.print('Date Received: ' + data.DateReceived.slice(15) + "\n")
/*.text('Last Name: [LastName]\n')
.text('Address: [Address]\n')
.text('Phone Number: [PhoneNumber]\n')
.text('Brand: [Brand]\n')
.text('Model: [Model]\n')
.text('Color: [Color]\n')
.text('Problem: [Problem]\n')
.text('Date Received: [DateReceived]', 1.75, 0, 1, 0.25, {
pattern: 'M/D/YY'
})*/
};
var repairfooterFunction = function(Report) {
Report.line(Report.currentX(), Report.maxY()-18, Report.maxX(), Report.maxY()-18);
Report.pageNumber({text: "Page {0} of {1}", footer: true, align: "right"});
Report.print("Printed: "+(new Date().toLocaleDateString()), {y: Report.maxY()-14, align: "left"});
};
Print.js is now supporting base64 PDF print.
Try this:
printJS({
printable: your_base64_data_string,
type: 'pdf'
base64: true
})
The documentation has been updated with an example printing a base64 PDF document.
http://printjs.crabbly.com#pdf
Was able to get this to work after a lot of playing around with the code and thinking of possible solutions. Generates the iframe, pulls up the print dialog using the autoprint property of the report and then deletes the iframe once the focus is on the document again using the 'mousemove' event
The code is below:
printIframe: function(url) {
var proxyIframe = document.createElement('iframe');
var body = document.getElementsByTagName('body')[0];
body.appendChild(proxyIframe);
proxyIframe.style.width = '100%';
proxyIframe.style.height = '100%';
proxyIframe.id='iframe'
proxyIframe.style.display = 'none';
var contentWindow = proxyIframe.contentWindow;
contentWindow.document.open();
// Set dimensions according to your needs.
// You may need to calculate the dynamically after the content has loaded
contentWindow.document.write('<iframe src="' + url + '" width="1000" height="1800" frameborder="0" marginheight="0" marginwidth="0">');
contentWindow.document.close();
var x=0
var func=function (event) {
if(x===0)
{
body.removeChild(proxyIframe)
++x
}
else
{
document.removeEventListener('mousemove', func)
}
}
contentWindow.document.body.onload=() => {
contentWindow.document.body.focus()
setTimeout(()=>{
document.addEventListener('mousemove', func)
}, 5000)
}
},

jqgrid, autocomplete under several input boxes

For jqGrid, how to do autocomplete under several input boxes, namely A, B, C. After the input A, autocomplete values provided by B need to have a reference from input A.
For the dataInit at input B, I can only get the original content of input A, not the current input one.
Any idea or link so that I can pay attention to. Thanks
B/R
Gene Leung
Here is the code:
...
{ name:'order_no',
index:'order_no',
align:"center",
width:80,
editable:true,
editoptions:
{
dataInit: function (elem) {
myAutocomplete(elem, "./autoComplete.php?id=sales_no");
},
dataEvents: [
{ type: 'change',
fn: function(e) {
savedSalesNo = $(this).val();
//console.log( savedSalesNo );
}
}
]
}
},
{ name:'item_no',
index:'item_no',
width:120,
editable:true,
editoptions:
{
dataInit: function (elem) {
myAutocomplete(elem, "./autoComplete.php?id=sales_items&vchr_no=" + savedSalesNo);
}
}
},
... php code: ...
if isset($_GET["term"]))
$maskTP = $_GET['term'];
else
$maskTP = "";
$sWhere = "WHERE 1=1";
if($maskTP!='') {
switch ($_GET["id"]) {
case "sales_no":
$sWhere.= " AND name LIKE '%$maskTP%'";
$sSQL = "SELECT name AS order_no FROM sale_order ". $sWhere ." ORDER BY name";
break;
case "sales_items":
$sWhere.= " AND name LIKE '%$maskTP%'";
$sSQL = "SELECT name AS order_no FROM sale_order ". $sWhere ." ORDER BY name";
break;
}
}
$result = $db->Execute( $sSQL );
Can you post some code snippet it will be helpful.
But looking at your question what i understand is you need to autocomplete in B based on A and so on.
So what you can do is while making ajax request for the B autocomplete check the value A and pass it in your call and perform your business logic.

How to efficiently store/retrieve data to/from chrome.storage.sync?

So, I'm writing an extension to allow people to fine and save colors from images found on the web. It's going well but now I'm trying to conceptualize how I'll actually store them, and list stored items.
As far as I can tell, chrome.storage.sync() only allows for objects. Which means I'd have to do something like this:
{colors: [{colorName: 'white', colorHex: '#ffffff'}, {colorName: 'black', colorHex: '#000000'}]}
Which seems wildly inefficient, since every time I want to add or subtract a color from the favorite list, I will need to get the entire array, change the one item I want, and then store the array back. Not to mention scanning an array for a color to see if it exists or not could be very intensive on a large array.
Ultimately, I'd like to be able to do something along the lines of
colors['#fff'].name = white;
However, that doesn't seem possible.
I'd love to hear some other ideas as to what the best way to accomplish this might be.
The beauty of Javascript is that everything is loosely considered an object. Functions, arrays, and even variables can be accessed as objects.
You could create an array like this,
var colors {}
colors["#FFF"] = "white";
colors["#000"] = "black";
Or perhaps use an array of empty functions,
function color(name, hex /* ... other properties */ ) { }
var colors {
color1: color("white", "#FFF");
color2: color("black", "#000");
}
Then these colors can be accessed by
color1.name
or
color1.hex
Although, because you should use a specific 'key' value for each object in storage, perhaps that is a better way to go.
For instance,
function save_color() {
var white = "#FFF";
//key value callback
chrome.storage.sync.set({"white": white}, function() {
console.log("The value stored was: " + white);
});
}
Or, for multiple colors
function save_colors() {
var white = "#FFF";
var black = "#000";
chrome.storage.sync.set([{"white": white}, {"black": black}], function() {
console.log("The values stored are: " + white + " and " + black);
});
}
I think that may work, i haven't tried storing multiple objects using one api call before, but you should get the point. A good way to implement this may be to have an empty array that gets added to every time the user finds a color they would like to add, then periodically the extension can push the data to sync.
Once you have done a ton of testing and your sync storage is cluttered, keep track of the keys you used during development and remember to run a batch data removal. It would look something like this:
function clear_data() {
var keys = { "white", "black" };
chrome.storage.sync.remove(keys, function() {
for(var i = 0; i < keys.length; i++)
console.log("Removed Data for Key: " + key[i]);
});
}
By the way, to retrieve the value stored in sync,
function load_color() {
var color = "white";
//key callback
chrome.storage.sync.get(color, function(val) {
console.log("The value returned was: " + val);
});
}
I was unsure about this as well, so I made a small example.
manifest.json:
{
"manifest_version": 2,
"name": "Test",
"description": "Test.",
"version": "1.0",
"permissions": [
"storage"
],
"content_scripts": [
{
"matches": ["https://www.google.com/*"],
"js": ["content-script.js"]
}
]
}
content-script.js:
console.log("content script loaded")
function modifyObject() {
chrome.storage.sync.get(null, function(storageData3) {
storageData3.object.property2 = false;
chrome.storage.sync.set(storageData3, function() {
chrome.storage.sync.get(null, function(storageData4) {
console.log("after setting *only* object: " + JSON.stringify(storageData4));
});
});
});
}
// Dumb attempt at setting only property2 of "object"; will add a new top level object "property2".
function attemptToModifyProperty2() {
var toSave = { "property2": false };
chrome.storage.sync.set(toSave, function() {
chrome.storage.sync.get(null, function(storageData2) {
console.log("after attemping to set *only* property2: " + JSON.stringify(storageData2));
modifyObject();
});
});
}
function addArray() {
var toSave = { "array": [1, 2, 3] };
chrome.storage.sync.set(toSave, function() {
chrome.storage.sync.get(null, function(storageData1) {
console.log("after setting *only* array: " + JSON.stringify(storageData1));
attemptToModifyProperty2();
});
});
}
function addObject() {
var toSave = { "object": { "property1": true, "property2": true } };
chrome.storage.sync.set(toSave, function() {
chrome.storage.sync.get(null, function(storageData) {
console.log("after setting *only* object: " + JSON.stringify(storageData));
addArray();
});
});
}
chrome.storage.sync.clear();
addObject();
If you go to google.com (and log in, or change the matches in manifest.json to http), and then open the console, you'll see this output:
content script loaded
content-script.js:42 after setting *only* object: {"object":{"property1":true,"property2":true}}
content-script.js:31 after setting *only* array: {"array":[1,2,3],"object":{"property1":true,"property2":true}}
content-script.js:20 after attemping to set *only* property2: {"array":[1,2,3],"object":{"property1":true,"property2":true},"property2":false}
content-script.js:9 after setting *only* object: {"array":[1,2,3],"object":{"property1":true,"property2":false},"property2":false}
My conclusions from this were that it's only possible to set top-level objects. Even if you want to change only one property that is nested deeply within a top-level object, you will have to pass the entire object to set().

YUI: get all data of row when click cell

I try to make example with datatable of YUI-2.8.2.
YAHOO.example.DynamicData = function() {
// Column definitions
myColumnDefs = [ // sortable:true enables sorting
{key:"id", label:"id", sortable:true},
{key:"date", label:"date", sortable:true},
{key:"price", label:"price", sortable:true},
{key:"number", label:"number", sortable:true}
];
// Custom parser
var stringToDate = function(sData) {
var array = sData.split("-");
return new Date(array[1] + " " + array[0] + ", " + array[2]);
};
var appendTestBtn = function(sData){
return "<input type=\"button\" value=\"Cancel\" onClick=\"javascript:onGetRowData();\">";
};
// DataSource instance
myDataSource = new YAHOO.util.DataSource("http://developer.yahoo.com/yui/examples/datatable/assets/php/json_proxy.php?");
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
myDataSource.responseSchema = {
resultsList: "records",
fields: [
{key:"id"},
{key:"date"},
{key:"price"},
{key:"number",parser:appendTestBtn}
],
metaFields: {
totalRecords: "totalRecords" // Access to value in the server response
}
};
// DataTable configuration
myConfigs = {
initialRequest: "sort=id&dir=asc&startIndex=0&results=25", // Initial request for first page of data
dynamicData: true, // Enables dynamic server-driven data
sortedBy : {key:"id", dir:YAHOO.widget.DataTable.CLASS_ASC}, // Sets UI initial sort arrow
paginator: new YAHOO.widget.Paginator({ rowsPerPage:5 }) // Enables pagination
};
// DataTable instance
myDataTable = new YAHOO.widget.DataTable("dynamicdata", myColumnDefs, myDataSource, myConfigs);
// Update totalRecords on the fly with value from server
myDataTable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
oPayload.totalRecords = oResponse.meta.totalRecords;
return oPayload;
}
return {
ds: myDataSource,
dt: myDataTable
};
}();
In source, i add a button at "number" column (using appendTestBtn method).
I want: when i click that button i can get all data of that row (Currently i only get data of that cell).
Please help me.
Thank you.
You can use a formatter instead:
var buttonFormatter = function (elCell, oRecord, oColumn, oData) {
return "<input type=\"button\" value=\"Cancel\" />"
};
in your column definition:
myColumnDefs = [ // sortable:true enables sorting
{key:"id", label:"id", sortable:true},
{key:"date", label:"date", sortable:true},
{key:"price", label:"price", sortable:true},
{key:"number", label:"number", formatter: buttonFormatter ,sortable:true}
];
and in your schema don't use parser:
fields: [
{key:"id"},
{key:"date"},
{key:"price"},
{key:"number"}
],
Now in your buttonFormatter method you have access to the row like this:
oRecord.getData("id")
oRecord.getData("date")
oRecord.getData("price")
Hope this helps

Resources