JSHint throwing a “possible strict violation” on this line - jshint

I'm having trouble getting the following code to pass JSHint
for (var i = 0; i < navLinks.length; i++) {
navLinks[i].addEventListener('click', loadPageContent);
}
function loadPageContent(e) {
e.preventDefault();
var pageURL = this.attributes.href.value; <-- Possible strict violation
loadContent(pageURL, function() {
var pageData = pages[pageURL.split('.')[0]];
// Create a new history item.
history.pushState(pageData, pageData.title, pageURL);
});
}
I know it's because "this" in a function declaration, but I don't know how to fix it, can anyone help? Thanks!

Related

Adobe ExtendScript Javascript: Replacing string with a function containing UI elements

New to coding in general, was wondering if its possible to change a content of a variable and update it to the UI with replace() or change()? I believe its much more harder than that but I can't wrap my head around it.
(function(thisObj) {
scriptBuildUI(thisObj)
function scriptBuildUI(thisObj) {
var win = (thisObj instanceof Panel) ? thisObj : new Window("palette", "replaceTest", undefined, 0);
win.alignChildren = ['center','top'];
var myTab = win.add("panel", undefined);
var replacebutton = myTab.add("button",undefined,"Replace");
var myTab2 = "x";
replacebutton.onClick = function(){ myTab2.replace("x",function(){hO()}) };
function hO() {myTab.add("button", undefined,"Retgf")};
win.onResizing = win.onResize = function() {
this.layout.resize();
};
win instanceof Window
? (win.center(), win.show()) : (win.layout.layout(true), win.layout.resize());
}
})(this);
I am not sure what you try to do here. myTab2 is a string variable and .replace() is a string function there. It returns a string. So you are trying to replace the "x" with the content of the function? To what end?
Also, the way i see it, that would just add another button to myTab.
If you could tell me what you try to do, maybe we can find a work-around.
Edit: To just assign your function to myTab2, you can directly do that. And if you want changes to your UI, i think you need to run win.layout.layout(true); to make them vissible. The folowing code adds a button to your tab every time it's clicked.
(function(thisObj) {
scriptBuildUI(thisObj)
function scriptBuildUI(thisObj) {
var win = (thisObj instanceof Panel) ? thisObj : new Window("palette", "replaceTest", undefined, 0);
win.alignChildren = ['center','top'];
var myTab = win.add("panel", undefined);
var replacebutton = myTab.add("button",undefined,"Replace");
var myTab2 = "x";
replacebutton.onClick = function(){ myTab2=hO() };
function hO() {
myTab.add("button", undefined,"Retgf");
win.layout.layout(true);
};
win.onResizing = win.onResize = function() {
this.layout.resize();
};
win instanceof Window
? (win.center(), win.show()) : (win.layout.layout(true), win.layout.resize());
}
})(this);

How to browse to the next page in a datasource that is loaded into table in Google AppMaker

I'm working on a requirement where I have a datasource named 'emailSearchResults' where I search for email messages metadata and load the results in the datasource.
The fields in the datasource are not relevant, however I set the datasource to have 50 records per page as per the below screenshot:
The script I used to load the datasource is shown in the query field, that call the following script:
function getMessageDetails(userId, msgID)
{
var messageDetails = [];
var messageData;
var msgID_,subject_,from_,date_;
messageData=Gmail.Users.Messages.get(userId,msgID,{format:"metadata", metadataHeaders:["Message-ID", "Subject", "From", "Date"]});
console.log(messageData.payload.headers);
//console.log(msgID);
//console.log(messageData.payload.headers[3].value);
date_="<na>";
from_="<na>";
subject_="<na>";
msgID_="<na>";
for (var counter =0;counter<4;counter++)
{
if (messageData.payload.headers[counter].name=="Message-ID")
{
msgID_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="Subject")
{
subject_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="From")
{
from_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="Date")
{
date_=messageData.payload.headers[counter].value;
}
}
messageDetails.push(date_);
messageDetails.push(from_);
messageDetails.push(subject_);
messageDetails.push(msgID_);
return messageDetails;
}
function searchMessages(userId,condition)
{
//
// first we build the conditions
// we can make it fixed
// or we can make it dynamic
var searchResult;
var deleteResult;
var currentMessage;
var results = [];
var pageToken;
var params = {};
var _stat;
var options = {
includeSpamTrash: "true",
pageToken: pageToken
};
var msgRecord = [];
do
{
searchResult=Gmail.Users.Messages.list(userId,options);
for (var i = 0; i < searchResult.messages.length; i++)
{
var record=app.models.emailSearchResults.newRecord();
msgRecord=getMessageDetails(userId,searchResult.messages[i].id);
record.msgMainID=searchResult.messages[i].id;
record.msgID=msgRecord[3];
record.subject=msgRecord[2];
record.senderAddress=msgRecord[1];
record.msgDate=msgRecord[0];
/*console.log(searchResult.messages[i].id);
console.log(msgRecord[3]);
console.log(msgRecord[2]);
console.log(msgRecord[1]);
console.log(msgRecord[0]);
return;*/
results.push(record);
msgRecord=null;
}
if (searchResult.nextPageToken) {
options.pageToken = searchResult.nextPageToken;
}
} while (searchResult.pageToken);
searchResult=null;
return results;
}
On the main page I put a table and linked it to the datasource, and I enabled pagination on the table, so I get the pager buttons at the bottom of the table as below:
When I execute the app and the datasource is filled, I see the first page results in a correct way, however when I want to move to the next page, I click the next page button and once the loading is complete I find out that I still see the same results from the first page on the table.
I am not familiar with how to make the table show the results of the second page then the third page, and I am going in circles on this...
Hope the explanation is clear and addresses the issue..
I would really appreciate any help on this!
Regards
Currently pagination isn't working as expected with calculated datasources. You can, however, build your own. There are several changes you'll need to make to accomplish this. First you'll want to refactor your searchMessages function to something like this:
function searchMessages(userId, pageToken){
var results = [];
var options = {
includeSpamTrash: "true",
pageToken: pageToken,
maxResults: 50
};
var searchResult = Gmail.Users.Messages.list(userId, options);
for (var i = 0; i < searchResult.messages.length; i++){
var record = app.models.emailSearchResults.newRecord();
var msgRecord = getMessageDetails(userId,searchResult.messages[i].id);
record.msgMainID = searchResult.messages[i].id;
record.msgID = msgRecord[3];
record.subject = msgRecord[2];
record.senderAddress = msgRecord[1];
record.msgDate = msgRecord[0];
results.push(record);
}
return {records: results, nextPageToken: searchResult.nextPageToken};
}
Then you'll want to change your datasource query. You'll need to add a number parameter called page.
var cache = CacheService.getUserCache();
var page = query.parameters.page || 1;
var pageToken;
if(page > 1){
pageToken = cache.get('pageToken' + page.toString());
}
var results = searchMessages('me', pageToken);
var nextPage = (page + 1).toString();
cache.put('pageToken' + nextPage, results.nextPageToken);
return results.records;
You'll need to modify the pagination widget's various attributes. Here are the previous/next click functions:
Previous:
widget.datasource.query.pageIndex--;
widget.datasource.query.parameters.page = widget.datasource.query.pageIndex;
widget.datasource.load();
Next:
widget.datasource.query.pageIndex++;
widget.datasource.query.parameters.page = widget.datasource.query.pageIndex;
widget.datasource.load();
You should be able to take it from there.

Meteor parsing a list of arrays with forEach

I have this helper in Meteor:
itemMenuAddOns: function(_id){
console.log(_id); //OK
var addOns = RestMenuItems.find({_id:_id}, {addOns: 1});
if(addOns){
console.log(addOns); //OK, an object where in the collection/map I see the expected array.
addOns.forEach( function(addOn) { console.log(addOn.title); } )// displays nothing
}
}
The problem is that the forEach console.log displays nothing. In both cases, if I use addOn.title or just even 123, to display.
thanks.
Well I had passed a wrong id and used find instead of findOne.
Now it works. for anyone interested:
itemMenuAddOns: function(_id){
var list = '';
var cursor = RestMenuItems.findOne({_id:_id}, {addOns: 1});
if(cursor){
cursor.addOns.forEach( function(addOn) {
var addOn = RestMenuSideItems.findOne({_id: addOn});
list += Spacebars.SafeString("<div>" + addOn.title + "</div>")});
return list;
}
}

creating a Placemarks that can be hidden

I have been trying to create a Placemark that I can hide and show (like turning visibility on and off) on demand (on click)... I am using this to make the placemark:
function placemark(lat, long, name, url, iconsrc){
var placemark = ge.createPlacemark(name);
ge.getFeatures().appendChild(placemark);
placemark.setName(name);
// Create style map for placemark
var icon = ge.createIcon('');
if(iconsrc == "0")
icon.setHref('http://maps.google.com/mapfiles/kml/paddle/red-circle.png');
else{
icon.setHref(iconsrc);
}
var style = ge.createStyle('');
style.getIconStyle().setIcon(icon);
if(iconsrc != "0")
style.getIconStyle().setScale(2.5);
placemark.setStyleSelector(style);
// Create point
var point = ge.createPoint('');
point.setLatitude(lat);
point.setLongitude(long);
//point.setAltitudeMode(1500);
placemark.setGeometry(point);
google.earth.addEventListener(placemark, 'click', function(event) {
// Prevent the default balloon from popping up.
event.preventDefault();
var balloon = ge.createHtmlStringBalloon('');
balloon.setFeature(placemark); // optional
balloon.setContentString(
'<iframe src="'+ url +'" frameborder="0"></iframe>');
ge.setBalloon(balloon);
});
}
I have tried everything... from this:
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
to using this ge.getFeatures().removeChild(child);
can anyone point me to the right direction on creating a function that will allow me to turn the visibility on/off on demand please.
Your hidePlacemark function is missing some {} in your final IF statement
if(child.getId()== name)
you have
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
make it
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name) {
child.setVisibility(false);
}
}
}
}
HOWEVER ------- you are better off doing this as it is much faster as you don't need to loop through ALL your placemarks
function hidePlacemark(name) {
var placemark = ge.getElementById(name);
placemark.setVisibility(false);
}
I think the plain ge.getFeatures().removeChild(placemark); works.
I played with this GooglePlayground, and just added the following code to line 8 (that is empty in this GooglePlayground Sample):
addSampleButton('Hide Placemark', function(){
ge.getFeatures().removeChild(placemark);
});
Clicking the button Hide Placemark hides the placemark like a charm here. Any chances your problem is somewhere else in your code?

Contentflow and Lightbox 2

Does anyone out there know how to integrate ContentFlow (http://www.jacksasylum.eu/ContentFlow) and Lightbox2 (http://lokeshdhakar.com/projects/lightbox2/)?
I need the ability for the image to not only open in a lightbox, but when opened, also have the user be able to the next and previous images.
Right now, I'm using ContentFlow with the Lightbox Addon (you can find this on the ContentFlow website), but that only uses the original Lightbox, so I can't make a gallery (or at least I can't figure out how to).
ContentFlow seems to be a pretty fickle product, and it doesn't accept lots of things.
Thank you all for your help and please comment!
On official site of ContentFlow there is AJAX section,
and example of how it should be used : http://www.jacksasylum.eu/ContentFlow/ajax_example.inc.php.
Main idea is that all those images are processed in a single place.
var ajax_cf = new ContentFlow('ajax_cf',
...
function addPictures(t){
var ic = document.getElementById('itemcontainer');
var is = ic.getElementsByTagName('img');
for (var i=0; i< is.length; i++) {
ajax_cf.addItem(is[i], 'last');
}
}
appPictures is callback function to be called when images are done loading.
You can group them in a hidded div according to the structure lightbox would expect.
I am using it with the callback from jquery preloader
jQuery.preloadImages = function () {
if (typeof arguments[arguments.length - 1] == 'function') {
var callback = arguments[arguments.length - 1];
} else {
var callback = false;
}
if (typeof arguments[0] == 'object') {
var images = arguments[0];
var n = images.length;
} else {
var images = arguments;
var n = images.length - 1;
}
var not_loaded = n;
for (var i = 0; i < n; i++) {
jQuery(new Image()).attr('src', images[i]).load(function () {
if (--not_loaded < 1 && typeof callback == 'function') {
callback();
}
});
}
}
Usage :
$.preloadImages(imagesArray, function () {
addPictures();
});
imagesArray in my case is array of relative path's
Note that ContentFlow is catchy with:
clearing and repopulating the flow
showing only two images
circular view
pushing more than 10 images at once

Resources