ag grid does not react to scroll in infinite scrolling pagination - pagination

I have server side pagination. I've created a simple back-end service to mimic it. See https://gitlab.com/sunnyatticsoftware/sasw-community/sasw-pagination-tester
The back-end can be run as a docker container on port 5000 (or any other) with
docker run -p 5000:8080 registry.gitlab.com/sunnyatticsoftware/sasw-community/sasw-pagination-tester:latest
This is gonna allow to query items (i.e: tickets in this case) with pagination filtering. See its swagger http://localhost:5000/swagger for more info and/or to interact with the sample.
It works in a pretty standard way.
To retrieve the first page with pagination size of 5 items per page:
GET http://localhost:5000/api/tickets?Page=1&Count=5
To retrieve the second page with pagination size of 5 items per page:
GET http://localhost:5000/api/tickets?Page=2&Count=10
etcetera.
The result would have enough information to be able to draw any table on a UI with pagination links and info about total items and total pages:
{
"items": [
{
"id": "00000001-0000-0000-0000-000000000000",
"channel": "chat",
"subject": "one"
},
{
"id": "00000002-0000-0000-0000-000000000000",
"channel": "email",
"subject": "two"
},
{
"id": "00000003-0000-0000-0000-000000000000",
"channel": "email",
"subject": "three"
},
{
"id": "00000004-0000-0000-0000-000000000000",
"channel": "phone",
"subject": "four"
},
{
"id": "00000005-0000-0000-0000-000000000000",
"channel": "chat",
"subject": "five"
}
],
"pageNumber": 1,
"pageSize": 5,
"totalPages": 7,
"totalCount": 32
}
THE PROBLEM
I want to use agGrid for the UI, with infinite scrolling, but I am struggling with the examples, as they all seem to have a previous fetch of a json that later is sliced to show results while scrolling. It would've been useful to have a sample with real integration, but in theory that shouldn't be a problem.
I cannot have the infinite scroll working. For whatever reason, it loads the first items but then the agGrid does not trigger the getRows anymore, and I don't know what am I missing.
I would like to have a simple infinite scrolling example working with the server-side pagination already in place. The following html/js won't send a second request to the server after scrolling.
The index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>JavaScript example</title>
<meta charSet="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style media="only screen">
html, body {
height: 100%;
width: 100%;
margin: 0;
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
}
html {
position: absolute;
top: 0;
left: 0;
padding: 0;
overflow: auto;
}
body {
padding: 1rem;
overflow: auto;
}
</style>
</head>
<body>
<div id="myGrid" style="height: 100%" class="ag-theme-alpine-dark">
</div>
<script>var __basePath = './';</script>
<script src="https://unpkg.com/ag-grid-enterprise#28.1.3/dist/ag-grid-enterprise.min.js">
</script>
<script src="main.js">
</script>
</body>
</html>
and the main.js
(() => {
const gridOptions = {
columnDefs: [
{ valueGetter: 'node.rowIndex', maxWidth: 100 },
{ field: 'id', minWidth: 150 },
{ field: 'channel', minWidth: 150 },
{ field: 'subject', minWidth: 400 }
],
defaultColDef: {
flex: 1,
minWidth: 80,
},
rowModelType: 'serverSide',
serverSideInfiniteScroll: true,
cacheBlockSize: 10,
maxBlocksInCache: 2,
rowBuffer: 0,
//blockLoadDebounceMillis: 1000,
debug: true,
};
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
var datasource = createDataSource();
gridOptions.api.setServerSideDatasource(datasource);
});
function createDataSource() {
return {
//rowCount: undefined,
getRows: (params) => {
console.log(`params start row:${params.startRow}, params end row: ${params.endRow}`);
var pagination = getPagination(params.startRow, params.endRow);
console.log(`asking for page ${pagination.page} and count ${pagination.count}`);
// send new request
fetch(`http://localhost:5000/api/tickets?page=${pagination.page}&count=${pagination.count}`)
.then((response) => response.json())
.then(data => {
console.log("data received");
console.log(data);
console.log(params);
params.success({
rowData: data.items,
rowCount: pagination.page * pagination.count,
});
//gridOptions.api.setRowCount(data.totalCount);
});
},
};
}
function getPagination(startRow, endRow){
let pagination = {
page: 1,
count: 10
};
if(startRow && endRow){
let size = endRow - startRow;
let page = Math.ceil(startRow / size);
pagination = {
page: page,
count: size
};
}
return pagination;
}
})();

I figured it out. The problem was in the row count itself (probably) as it needs to be set to the total existing count of items that exist in back-end, so that the grid knows that needs more.
Here's a possible solution.
For ag-grid Enterprise
(() => {
const gridOptions = {
columnDefs: [
{ valueGetter: 'node.rowIndex', maxWidth: 100 },
{ field: 'id', minWidth: 150 },
{ field: 'channel', minWidth: 150 },
{ field: 'subject', minWidth: 400 }
],
defaultColDef: {
flex: 1,
minWidth: 80,
},
rowModelType: 'serverSide',
serverSideInfiniteScroll: true,
cacheBlockSize: 10, //this is the count (page size)
maxBlocksInCache: 2,
rowBuffer: 0,
debug: false
};
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
var datasource = createDataSource();
gridOptions.api.setServerSideDatasource(datasource);
});
function createDataSource() {
return {
getRows: (params) => {
let count = params.request.endRow - params.request.startRow; // or the cacheBlockSize
let page = Math.ceil(params.request.endRow / count);
console.log(`asking for page ${page} and count ${count}`);
fetch(`http://localhost:5000/api/tickets?page=${page}&count=${count}`)
.then((response) => response.json())
.then(data => {
params.success({
rowData: data.items,
rowCount: data.totalCount,
});
});
},
};
}
})();
For ag-grid Community
A few things are different,
obviously the ag-grid library is a different one.
the row model type is infinite
the grid options api method to set datasource is called setDatasource
there is no object response within the injected getRows parameter, so the startRow and endRow are retrieved directly from the params object.
at index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>JavaScript example</title>
<meta charSet="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style media="only screen">
html, body {
height: 100%;
width: 100%;
margin: 0;
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
}
html {
position: absolute;
top: 0;
left: 0;
padding: 0;
overflow: auto;
}
body {
padding: 1rem;
overflow: auto;
}
</style>
</head>
<body>
<div id="myGrid" style="height: 100%" class="ag-theme-alpine-dark">
</div>
<script>var __basePath = './';</script>
<script src="https://unpkg.com/ag-grid-community/dist/ag-grid-community.min.js">
</script>
<script src="main.js">
</script>
</body>
</html>
at main.js
(() => {
const gridOptions = {
columnDefs: [
{ valueGetter: 'node.rowIndex', maxWidth: 100 },
{ field: 'id', minWidth: 150 },
{ field: 'channel', minWidth: 150 },
{ field: 'subject', minWidth: 400 }
],
defaultColDef: {
flex: 1,
minWidth: 80,
},
rowModelType: 'infinite',
cacheBlockSize: 10, //this is the count (page size)
maxBlocksInCache: 2,
rowBuffer: 0,
debug: false
};
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
var datasource = createDataSource();
gridOptions.api.setDatasource(datasource);
});
function createDataSource() {
return {
getRows: (params) => {
console.log(params);
let count = params.endRow - params.startRow; // or the cacheBlockSize
let page = Math.ceil(params.endRow / count);
console.log(`asking for page ${page} and count ${count}`);
fetch(`http://localhost:5000/api/tickets?page=${page}&count=${count}`) // any additional filter must be set on that url as query params
.then((response) => response.json())
.then(data => {
params.successCallback(data.items, data.totalCount);
});
},
};
}
})();

Related

Vuetify v-data-table search.filter not showing any results

Getting data back from our API but built in Vuetify search/filter is not working. I think it has to do with the data coming back being nested in an object. When typing in the search filter i get "No matching records found" after the first character, when removing the search term the full data table is displayed. Thanks in advance for any help.
<template>
<v-container
fill-height
fluid
grid-list-xl
>
<v-layout
justify-center
wrap
>
<v-flex
md6
>
<material-card
color="black"
title="Users"
>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
<v-data-table
:headers="headers"
:items="userData"
:search="search"
hide-actions
>
<template
slot="headerCell"
slot-scope="{ header }"
>
<span
class="subheading font-weight-light text-dark text--darken-3"
v-text="header.text"
/>
</template>
<template
slot="items"
slot-scope="{ item }"
>
<td>
<v-avatar slot="offset" class="mx-auto d-block" size="100">
<img v-if="item.ProfileImage==null" src="img/conectrlogo.jpg">
<img v-else v-bind:src= "item.ProfileImage">
</v-avatar></td>
<td><v-btn text-small outlined color="primary" #click= "goToUserProfile(item.Id)">{{ item.Id}}</v-btn></td>
<td>{{ item.Username}}</td>
<td>{{ item.Name}}</td>
</template>
</v-data-table>
</material-card>
</v-flex>
</v-layout>
</v-container>
</template>
Script
<script>
import axios from 'axios'
export default {
mounted()
{
console.log("got into mounted function");
this.getResults();
},
data () {
return {
customFilter: function (items, search, filter, headers) {
search = search.toString().toLowerCase()
if (search.trim() === '') return items
const props = headers.map(h => h.value)
return items.filter(item => props.some(prop => filter(getObjectValueByPath(item, prop, item[prop]), search)))
},
userData:[],
totalUsers:0,
showResults:true,
search:'',
headers:[
{
text: 'User',
value: 'profileimage',
align: 'center',
width: '50px',
sortable:false
},
{
text: 'id',
value: 'id',
align: 'center',
width: '100px',
sortable:false
},
{
text: 'Username', value: 'username',
align: 'left',
sortable: false,
width: '50px'
},
{
text: 'Name', value: 'name',
align: 'left',
sortable: true,
width: '50px'
}
]
}
},
computed:{
},
methods: {
goToUserProfile: function(Id)
{
console.log("avatar clicked:"+Id);
this.$router.push('/user-profile/'+Id)
},
getResults (){
console.log("got into the all users endpoint");
console.log(this.$baseUrl+'/admin/users');
// axios.get(this.$baseUrl+'/admin/users',
// {withCredentials: true}).then ( response => {
// this.userData=response.data.Users;
// this.totalUsers = response.data.UserCount;
// console.log("all user response:"+this.userData);
// });
//this.showResults=true;
axios.defaults.withCredentials = true;
axios(this.$baseUrl+'/admin/users', {
method: 'GET',
withCredentials: true,
crossDomain:true
}).then(res => {
console.log(res);
this.userData=res.data.Users;
this.totalUsers = res.data.UserCount;
console.log("all user response:"+this.userData);
}).catch(err => {
console.log("got an error");
console.log(err);
})
},
initialize()
{
},
}
}
</script>

Why is this "not defined"

I am using Express and an API to get some data which I then pass into my view. I can loop through that data and print it from within my EJS template, so I know It's there is some capacity. However when I try to use that data in a chart.js chart (all in the same template file) it says it is "not defined"... Why is this happening?
App.js:
app.get('/search', function(req, res) {
var query = req.query.search;
endPoint = 'https://api.iextrading.com/1.0/stock/' + query + '/chart/1d';
request(endPoint, function(error, response, body) {
if(!error && response.statusCode == 200) {
stockData = JSON.parse(body);
console.log(stockData);
res.render('search.ejs', {stockData : stockData});
} else {
console.log(error);
}
});
});
EJS Template file
<% stockData.forEach(function(minute) { %>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [minute['minute']],
datasets: [{
label: '# of Votes',
data: minute['open'],
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
<% }) %>
EDIT
If I change it to be like this it then says that "stockData" is undefined:
<% stockData.forEach(function(minute) { %>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [stockData['open']],
datasets: [{
label: '# of Votes',
data: stockData['open'],
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
<% }) %>
Your stockData in chart.js is generate by javascript on browser. But stockData that really hold value that not undefine is generate by Nodejs on backend. If you wanna use like this. First, you need to render your ejs page, then send an ajax to server, get the response data. Then use that data you just receive to draw your chart. Somethings like this:
axios.get('/search')
.then(function (response) {
let data = response.data;
new Chart(document.getElementById('line-chart'), {
type: 'line',
data: {
labels: [],
datasets: [{
data: [your_response_data_from_nodejs.open],
label: 'Blabla',
borderColor: '#52D0C4',
fill: false
}
]
},
options: {
title: {
display: true,
text: 'Blala '
}
}
});
})
.catch(function (error) {
throw new error;
});

js grid and autocomplete

I am able to create a custom field with jsGrid and jquery autocomplete. All ajax CRUD calls are working for all other fields. The below code activates autocomplete and shows the available options in the input field as expected.
var tags = ["tag1", "tag2", "tag3"];
MyDescriptionField.prototype = new jsGrid.Field({
insertTemplate: function(value) {
return this._editPicker = $("<input>").autocomplete({source : tags});
},
editTemplate: function(value) {
return this._editPicker = $("<input>").autocomplete({source : tags});
},
........... (more code)
So far so good. However to actually capture the value so it can be inserted into the db, I also need to define insertValue and editValue.
The code below is NOT working
insertValue: function(){
return this._insertPicker = $("<input>").val();
},
...........(more code)
this one is not working eiter:
insertValue: function(){
return this._insertPicker.autocomplete({
select: function(event, ui) {
$("<input>").val(ui.item.value);
}
});
},
reference: jsGrid. http://js-grid.com/demos/
autocomplete: https://jqueryui.com/autocomplete/
Try this snippet:
$(function() {
var myTagField = function(config) {
jsGrid.Field.call(this, config);
};
myTagField.prototype = new jsGrid.Field({
sorter: function(tag1, tag2) {
return tag1.localeCompare(tag2);
},
itemTemplate: function(value) {
return value;
},
insertTemplate: function(value) {
return this._insertAuto = $("<input>").autocomplete({source : tags});
},
editTemplate: function(value) {
return this._editAuto = $("<input>").autocomplete({source : tags}).val(value);
},
insertValue: function() {
return this._insertAuto.val();
},
editValue: function() {
return this._editAuto.val();
}
});
jsGrid.fields.myTagField = myTagField;
$("#jsGrid").jsGrid({
width: "100%",
inserting: true,
editing: true,
sorting: true,
paging: true,
fields: [
{ name: "Name", type: "text" },
{ name: "Tag", type: "myTagField", width: 100, align: "center" },
{ type: "control", editButton: false, modeSwitchButton: false }
],
data: db.users
});
});
var tags = ["tag1", "tag2", "tag3"];
var db = {};
db.users = [
{
"Name": "Carson Kelley",
"Tag": ""
},
{
"Name": "Prescott Griffin",
"Tag": "tag1"
},
{
"Name": "Amir Saunders",
"Tag": "tag3"
},
{
"Name": "Derek Thornton",
"Tag": "tag2"
},
{
"Name": "Fletcher Romero",
"Tag": ""
}];
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script src="//rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.js"></script>
<link href="//code.jquery.com/ui/1.11.2/themes/cupertino/jquery-ui.css" rel="stylesheet"/>
<link href="//rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.css" rel="stylesheet"/>
<link href="//rawgit.com/tabalinas/jsgrid/master/dist/jsgrid-theme.css" rel="stylesheet"/>
<div id="jsGrid"></div>
or this codepen: https://codepen.io/beaver71/pen/rpaLEo
Thanks #beaver. Your pen helped my understand custom fields better. I extended it a bit to add filtering with autocomplete. https://codepen.io/obrienje/pen/aQKNry
$(function() {
var myTagField = function(config) {
jsGrid.Field.call(this, config);
};
myTagField.prototype = new jsGrid.Field({
autosearch: true,
sorter: function(tag1, tag2) {
return tag1.localeCompare(tag2);
},
itemTemplate: function(value) {
return '<span class="label label-primary">' + value + '</span>';
},
insertTemplate: function(value) {
return this._insertAuto = $("<input>").autocomplete({
source: tags
});
},
filterTemplate: function(value) {
if (!this.filtering)
return "";
var grid = this._grid,
$result = this._filterAuto = $("<input>").autocomplete({
source: tags
});
if (this.autosearch) {
$result.on("change", function(e) {
grid.search();
});
}
return $result;
},
editTemplate: function(value) {
return this._editAuto = $("<input>").autocomplete({
source: tags
}).val(value);
},
insertValue: function() {
return this._insertAuto.val();
},
filterValue: function() {
return this._filterAuto.val();
},
editValue: function() {
return this._editAuto.val();
}
});
jsGrid.fields.myTagField = myTagField;
$("#jsGrid").jsGrid({
width: "100%",
filtering: true,
inserting: true,
editing: true,
sorting: true,
paging: true,
fields: [{
name: "Name",
type: "text"
},
{
name: "Tag",
type: "myTagField",
width: 100,
align: "center"
},
{
type: "control",
editButton: false,
modeSwitchButton: false
}
],
data: db.users,
controller: {
loadData: function(filter) {
return $.grep(db.users, function(item) {
return (!filter.Tag || item.Tag.toLowerCase().indexOf(filter.Tag.toLowerCase()) > -1);
});
}
}
});
});
var tags = ["tag1", "tag2", "tag3"];
var db = {};
db.users = [{
"Name": "Carson Kelley",
"Tag": ""
},
{
"Name": "Prescott Griffin",
"Tag": "tag1"
},
{
"Name": "Amir Saunders",
"Tag": "tag3"
},
{
"Name": "Derek Thornton",
"Tag": "tag2"
},
{
"Name": "Fletcher Romero",
"Tag": ""
}
];
<html>
<head>
<link href="https://rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.css" rel="stylesheet"/>
<link href="https://rawgit.com/tabalinas/jsgrid/master/dist/jsgrid-theme.css" rel="stylesheet"/>
<link href="//code.jquery.com/ui/1.11.2/themes/cupertino/jquery-ui.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css"/>
</head>
<body>
<h1>Custom Grid DateField filtering with autocomplete</h1>
<div id="jsGrid"></div>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script src="https://rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.js"></script>
</body>
</html>
Thanks #beaver. Your pen helped my understand custom fields better. I extended it a bit to add filtering with autocomplete. https://codepen.io/obrienje/pen/aQKNry

How to change of web page content from a Google Chrome extension

I am writing extension which will underline accent in English words on web pages.
I've got stuck after I click "Search" button on popup, nothing seems to happen.
Here's the scenario:
user double click a word on a web page.
the whole word is marked.
user clicks extension icon on chrome browser bar.
a popup is shown. Input field in popup is filled in with the marked word.
user adds accent. Ie. if marked word is 'boundary', in popup's input field there will be: 'boudary' displayed. Then user modify the input value to: 'boudary,bo' (without quotes).
user click "Search" button on popup.
letters "bo" in "boundary" word on page are being underlined.
manifest.json
{
"content_scripts": [ {
"js": [ "jquery.js", "jquery.highlight-4.js", "selection.js" ],
"matches": [ "\u003Call_urls\u003E" ]
} ],
"name": "Mark accent",
"version": "1.0",
"manifest_version": 2,
"options_page": "options.html",
"description": "Marks accent in english words for selected word on page",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"icons": {
"128": "icon.png"
},
"permissions": [ "tabs", "http://*/*", "https://*/*", "storage", "file:///*" ]
}
app.js
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getSelection"}, function (response) {
$("#t1").val(response.data);
console.log('input t1 value: ' + $("#t1").val(response.data));
});
});
$("#t1").keypress(function(event) {
if ( event.which == 13 ) {
$("#search_btn").click();
}
});
$("#t1").focus();
function search(that) {
var token = new String (t1.value);
chrome.tabs.executeScript(null,
{code:"$(document.body).highlight('"+token+"','text-decoration:underline')"});
window.close();
}
selection.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getSelection")
sendResponse({data: window.getSelection().toString()});
else
sendResponse({}); // snub them.
});
popup.html
<style>
body {
overflow: hidden; margin: 5px; padding: 0px; background: black;color: white;
width: 300px; font-family: 'Droid Sans', arial, sans-serif;
}
</style>
Please enter the word to highlight :
<input type="text" id="t1"/>
<button onclick="search(this)" id="search_btn">Search</button>
<button onclick="hl_clear(this)" id="clear_btn">Clear all highlights</button>
<script src="jquery.js"></script>
<script src="jquery.highlight-4.js"></script>
<script src="app.js"></script>
jquery.highlight-4.js
jQuery.fn.highlight = function(pat, fbgcolor) {
function innerHighlight(node, pat, fbgcolor) {
var skip = 0;
var array = pat.split(',');
var sWord = array[0];
var accent = array[1];
if (node.nodeType == 3) {
var pos = node.data.toUpperCase().indexOf(sWord);
if (pos >= 0) {
var middlebit = node.splitText(pos);
var endbit = middlebit.splitText(sWord.length);
var pos2 = middlebit.data.toUpperCase().indexOf(accent);
if (pos2 >= 0) {
var spannode = document.createElement('span');
spannode.className = 'highlight';
fbgcolor += ";padding: 0px; margin: 0px;";
spannode.setAttribute('style', fbgcolor);
var middlebit2 = middlebit.splitText(pos2);
var endbit2 = middlebit2.splitText(accent.length);
var middleclone2 = middlebit2.cloneNode(true);
spannode.appendChild(middleclone2);
middlebit2.parentNode.replaceChild(spannode, middlebit2);
}
skip = 1;
}
}
else if (node.nodeType == 1 && node.childNodes &&
!/(script|style)/i.test(node.tagName)) {
for (var i = 0; i < node.childNodes.length; ++i) {
i += innerHighlight(node.childNodes[i], pat, fbgcolor);
}
}
return skip;
}
return this.each(function() {
innerHighlight(this, pat.toUpperCase(), fbgcolor);
});
};
It is working after so many modifications and eliminated non-chrome extension related content. You can add your content to this skeleton.
Do not add Inline scripts in html
<button onclick="search(this)" id="search_btn">Search</button>
Basic Skeleton of your code:
manifest.json
{
"content_scripts": [ {
"js": [ "jquery.js", "jquery.highlight-4.js", "selection.js" ],
"matches": [ "<all_urls>" ]
} ],
"name": "Mark accent",
"version": "1.0",
"manifest_version": 2,
"description": "Marks accent in english words for selected word on page",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"icons": {
"128": "icon.png"
},
"permissions": [ "tabs", "<all_urls>" ]
}
app.js
function search(that) {
console.log("Search is clicked");
var token = document.getElementById("t1").value;
console.log(token);
chrome.tabs.executeScript(null,
{code:"highlight();"});
//window.close();
}
window.onload=function (){
document.getElementById("search_btn").onclick=search;
};
selection.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getSelection")
sendResponse({data: window.getSelection().toString()});
else
sendResponse({}); // snub them.
});
popup.html
<html>
<head>
<style>
body {
overflow: hidden; margin: 5px; padding: 0px; background: black;color: white;
width: 300px; font-family: 'Droid Sans', arial, sans-serif;
}
</style>
<script src="jquery.js"></script>
<script src="app.js"></script>
<body>
Please enter the word to highlight :
<input type="text" id="t1"/>
<button id="search_btn">Search</button>
<button id="">Clear all highlights</button>
</body>
</html>
jquery.highlight-4.js
function highlight(){
console.log("Highlight is called");
}
Let me know if it is still failing.

Google Fusion Tables: Two layers, one with style

I'm trying to create a map using Google Fusion Tables with two layers, one of which I want to add a style to. I also have multiple columns, and I want to switch between these columns using a drop-down menu. So far I managed to do the latter, but I'm stuck trying to add a second layer.
The map I have now shows the ratio of girls:boys in schools in the 170 districts in Ghana. With the drop down menu I can switch between primary school and junior high. Now I want to add a layer with the regional borders.
In the documentation I saw this:
You can use the Maps API to add up to five Fusion Tables layers to a
map, one of which can be styled with up to five styling rules.
Which is pretty much exactly what I want, but I also keep the drop-down menu. I've recently started using Fusion Tables and was hoping someone could help me.
The ID of the layer I want to add: 1_0rcifQnnNpLV1VjTPyzDZSF3LHp-7rowzrXM78
And the code of the working map:
PS: I'm a total newbie and used this map made by the Guardian as a basis. I got rid of everything I didn't think I needed, but there might be some unnecessary stuff left in the code.
<!DOCTYPE html>
<html lang="en" itemscope itemtype="http://schema.org/Article">
<head>
<title>Gender Parity Index | Education | 2011</title>
<meta charset="utf-8" />
<style type="text/css">
body { font-family: Arial, sans-serif; }
#map_canvas { height: 600px; width:575px; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" id="script">
var center = new google.maps.LatLng(7.972198, -0.716284);
var zoom = 7;
var legend_width = '150px';
var tableid = '12GLQaH4wvwByxBk4W7UHkJTr99vsxymCTYHmkXs';
var location_column = 'geometry';
var colors = ['#CA0020','#F4A582','#F7F7F7','#92C5DE','#0571B0'];
var columns = {
'Gender parity index at primary education': [
{
'min': 0.6,
'max': 0.8,
'color': '#CA0020'
},
{
'min': 0.8,
'max': 0.95,
'color': '#F4A582'
},
{
'min': 0.95,
'max': 1.05,
'color': '#F7F7F7'
},
{
'min': 1.05,
'max': 1.2,
'color': '#92C5DE'
}
],
'Gender parity index at junior high school education': [
{
'min': 0.6,
'max': 0.8,
'color': '#CA0020'
},
{
'min': 0.8,
'max': 0.95,
'color': '#F4A582'
},
{
'min': 0.95,
'max': 1.05,
'color': '#F7F7F7'
},
{
'min': 1.05,
'max': 1.2,
'color': '#92C5DE'
},
{
'min': 1.2,
'max': 1.6,
'color': '#0571B0'
}
]
}
var map, layer, geocoder;
function initialize() {
map = new google.maps.Map(document.getElementById('map_canvas'), {
center: center,
zoom: zoom,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var style = [ { stylers: [ { invert_lightness: true } ] },{ featureType: "road.highway", stylers: [ { hue: "#00e5ff" } ] },{ featureType: "poi.park", stylers: [ { visibility: "off" } ] },{ featureType: "landscape.natural", stylers: [ { visibility: "on" } ] },{ featureType: "water", stylers: [ { color: "#080808" } ] },{ featureType: "landscape.natural", stylers: [ { color: "#202020" } ] },{ featureType: "administrative.province", elementType: "labels", stylers: [ { visibility: "on" } ] },{ featureType: "administrative.locality", elementType: "labels", stylers: [ { visibility: "on" } ] },{ featureType: "administrative.country", elementType: "labels", stylers: [ { visibility: "off" } ] },{
featureType: 'road',
elementType: 'all',
stylers: [
{ saturation: -99 }
]
} ];
geocoder = new google.maps.Geocoder();
var styledMapType = new google.maps.StyledMapType(style, {
map: map,
name: 'Styled Map'
});
map.mapTypes.set('map-style', styledMapType);
map.setMapTypeId('map-style');
layer = new google.maps.FusionTablesLayer({
query: {
select: location_column,
from: tableid
}
});
layer.setMap(map);
init_selectmenu();
addStyle(getKey());
}
function getKey() {
for(key in columns) {
return key;
}
}
// Initialize the drop-down menu
function init_selectmenu() {
var selectmenu = document.getElementById('selector');
for(column in columns) {
var option = document.createElement('option');
option.setAttribute('value', column);
option.innerHTML = column;
selectmenu.appendChild(option);
}
}
function addStyle(column) {
var defined_styles = columns[column];
var styles = new Array();
for(defined_style in defined_styles) {
var style = defined_styles[defined_style];
styles.push({
where: generateWhere(column, style.min, style.max),
polygonOptions: {
fillColor: style.color,
fillOpacity: 0.9,
strokeOpacity: 0.50,
strokeColor: "#f3f3f3"
}
});
}
layer.set('styles', styles);
updateLegend(column);
}
// Create the where clause
function generateWhere(column_name, low, high) {
var whereClause = new Array();
whereClause.push("'");
whereClause.push(column_name);
whereClause.push("' >= ");
whereClause.push(low);
whereClause.push(" AND '");
whereClause.push(column_name);
whereClause.push("' < ");
whereClause.push(high);
return whereClause.join('');
}
// Create the legend with the corresponding colors
function updateLegend(column) {
var legendDiv = document.createElement('div');
var legend = new Legend(legendDiv, column);
legendDiv.index = 1;
map.controls[google.maps.ControlPosition.RIGHT_TOP].pop();
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legendDiv);
}
// Generate the content for the legend
function Legend(controlDiv, column) {
controlDiv.style.padding = '10px';
var controlUI = document.createElement('div');
controlUI.style.backgroundColor = 'white';
controlUI.style.borderStyle = 'solid';
controlUI.style.borderWidth = '1px';
controlUI.style.width = legend_width;
controlUI.title = 'Legend';
controlDiv.appendChild(controlUI);
var controlText = document.createElement('div');
controlText.style.fontFamily = 'Arial,sans-serif';
controlText.style.fontSize = '12px';
controlText.style.paddingLeft = '4px';
controlText.style.paddingRight = '4px';
controlText.innerHTML = legendContent(column);
controlUI.appendChild(controlText);
}
function legendContent(column) {
var defined_styles = columns[column];
// Generate the content for the legend using colors from object
var controlTextList = new Array();
controlTextList.push('<p><b>');
controlTextList.push(column);
controlTextList.push('</b></p>');
for(defined_style in defined_styles) {
var style = defined_styles[defined_style];
controlTextList.push('<div style="background-color: ');
controlTextList.push(style.color);
controlTextList.push('; height: 20px; width: 20px; margin: 3px; float: left;"></div>');
controlTextList.push(style.min);
controlTextList.push(' to ');
controlTextList.push(style.max);
controlTextList.push('<br style="clear: both;"/>');
}
controlTextList.push('<br />');
return controlTextList.join('');
}
</script>
</head>
<body onload="initialize();">
<select onchange="addStyle(this.value);" id="selector" style="font-size: 16px"></select>
<div id="map_canvas"></div>
</div>
<script>
// send the query string to the iFrame
(function() {
var interactive = jQ('#interactive iframe');
if (interactive.length > 0) {
var qs = window.location.search;
interactive[0].src = interactive[0].src + qs;
}
})();
</script>
</div>
<div id="related">
</script>
</script>
</body>
</html>
Just add the second layer at the end of your initialize() method like that:
function initialize() {
// ... all the other stuff ...
borderLayer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '1_0rcifQnnNpLV1VjTPyzDZSF3LHp-7rowzrXM78'
}
});
borderLayer.setMap(map);
}
See the working example of your code with the second layer on jsFiddle.

Resources