Nunjucks access window screen size - templating

Is there a way to get window size parameters with Nunjucks?
Currently I am trying:
{% if window.screen.width < 800 %}
{% include 'partials/sort.html' %}
{% endif %}
As I understand, Nunjucks templating does not have access to front-end parameters, but is there any way to go around this? (for the record, i am using nodejs for server-side code)

You can store a window size in cookies and pass them values into a render res.render(templ, {cookies}): if size-cookies don't exists on a page request then return a "special" page where you store window size to cookies and redirect to the requested page.
Here example of this way (app.js requires installed express and nunjucks modules):
// app.js
var express = require ('express');
var nunjucks = require('nunjucks');
var app = express();
var env = nunjucks.configure('.', {autoescape: true, express: app});
function parseCookies(cookies) {
return cookies && cookies.split(';').reduce(function(res, cookie) {
var pair = cookie.split('=');
res[pair.shift().trim()] = decodeURI(pair.join('='));
return res;
}, {}) || {};
}
app.get('/set-cookie', function(req, res) {
res.render('setter.html');
});
app.get('*', function(req, res){
let cookies = parseCookies(req.headers.cookie) || {};
if (!cookies.width || !cookies.height)
return res.redirect('/set-cookie?' + req.url);
res.render('index.html', {width: cookies.width, height: cookies.height});
});
app.listen(3000, function() {
console.log('Example app listening on port 3000...');
});
// index.html
<html>
<body>
{{width}} x {{height}}
</body>
</html>
// setter.html
<html>
<head>
<script type="text/javascript">
// store window size to cookies and redirect to origin page
function setWindowSize () {
document.cookie = 'width=' + window.innerWidth;
document.cookie = 'height=' + window.innerHeight;
location = location.search.substring(1);
}
</script>
</head>
<body onload = "setWindowSize()">
</body>
</html>

Related

Gapi client javascript 404 Requested entity was not found

I'm using the GAPI v4 endpoint to access google sheets, I've used the example from the Google quickstart and get a 404 error of Requested entity was not found. Why would this be happening, it shouldn't be happening since I've used their example, my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Application Conversion Viewer</title>
<!-- https://electronjs.org/docs/tutorial/security#csp-meta-tag -->
<!-- <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" /> -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
<div id="app">
<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" style="display: none;">Authorize</button>
<button id="signout_button" style="display: none;">Sign Out</button>
<pre id="content" style="white-space: pre-wrap;"></pre>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-resource#1.5.1"></script>
<script src="https://apis.google.com/js/api.js"></script>
<script>
new Vue({
el: '#app',
mounted () {
var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');
/**
* On load, called to load the auth2 library and API client library.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
/**
* Initializes the API client library and sets up sign-in state
* listeners.
*/
function initClient() {
gapi.client.init({
apiKey: 'MY-KEY',
clientId: 'MY-CLIENT-ID',
discoveryDocs: 'https://sheets.googleapis.com/$discovery/rest?version=v4',
scope: 'https://www.googleapis.com/auth/spreadsheets.readonly'
}).then(function () {
console.log('SUCCESS')
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
}, function(error) {
console.log(error)
appendPre(JSON.stringify(error, null, 2));
});
}
/**
* Called when the signed in status changes, to update the UI
* appropriately. After a sign-in, the API is called.
*/
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
authorizeButton.style.display = 'none';
signoutButton.style.display = 'block';
listMajors();
} else {
authorizeButton.style.display = 'block';
signoutButton.style.display = 'none';
}
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick(event) {
gapi.auth2.getAuthInstance().signIn();
}
/**
* Sign out the user upon button click.
*/
function handleSignoutClick(event) {
gapi.auth2.getAuthInstance().signOut();
}
/**
* Append a pre element to the body containing the given message
* as its text node. Used to display the results of the API call.
*
* #param {string} message Text to be placed in pre element.
*/
function appendPre(message) {
var pre = document.getElementById('content');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
/**
* Print the names and majors of students in a sample spreadsheet:
* https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
*/
function listMajors() {
gapi.client.sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}).then(function(response) {
var range = response.result;
if (range.values.length > 0) {
appendPre('Name, Major:');
for (i = 0; i < range.values.length; i++) {
var row = range.values[i];
// Print columns A and E, which correspond to indices 0 and 4.
appendPre(row[0] + ', ' + row[4]);
}
} else {
appendPre('No data found.');
}
}, function(response) {
appendPre('Error: ' + response.result.error.message);
});
}
console.log('LOAD')
handleClientLoad()
}
})
</script>
</body>
</html>
I've tried several approaches to get this to work, neither of which work. The quickstart seems to be wrong: https://developers.google.com/sheets/api/quickstart/js

Actions on Google account linking

Following documentation described here, I have account linking set up with implicit grants and find that it works well when testing with the browser / actions console, and also with the Google Home app for Android. Unfortunately on the iphone version of the app, user auth hangs most of the time. Feedback from actions on google support is that the problem is that google sign in flow is implemented in separate browser tab (window). On iphone you can't open 2 windows in SfariViewController, thus they are re-writing address of the first page and can’t finish sign in flow. This is known issue and they are not planning to change this. The solution is to implement sign in flow all in one browser window. I'm unclear how to do this and am looking for someone to share code behind the authorization URL you set up that works consistently on iphone. Below is the core of what I am using:
.html snippet:
<!DOCTYPE html>
<html>
<head>
<title>Authorization Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="google-signin-client_id" content="948762963734-2kbegoe3i9ieqc6vjmabh0rqqkmxxxxx.apps.googleusercontent.com">
<!-- <meta name="google-signin-ux_mode" content="redirect"> INCLUDING THIS META TAG BREAKS THE AUTH FLOW -->
<script src="js/googleAuth.js"></script>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<link rel="stylesheet" href="css/googleAuth.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<header class="bgimg-1 w3-display-container w3-grayscale-min" id="loginScreen">
<div class="w3-display-topleft w3-padding-xxlarge w3-text-yellow" style="top:5px">
<span class="w3-text-white">Google Sign In</span><br>
<span class="w3-large">Sign in with your Google account</span><br><br>
<div class="g-signin2" data-onsuccess="onSignIn"></div><br><br>
</div>
</header>
</body>
</html>
.js code snippet:
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
var id = profile.getId()
var name = profile.getName()
var email = profile.getEmail()
var token = googleUser.getAuthResponse().id_token;
var client_id = getQueryVariable('client_id')
// vital-code-16xxx1 is the project ID of the google app
var redirect_uri = 'https://oauth-redirect.googleusercontent.com/r/vital-code-16xxx1'
var state = getQueryVariable('state')
var response_type = getQueryVariable('response_type')
// store the user's name, ID and access token and then sign out
storeOwnerID (email, name, id, token, function() {
// sign out
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('signed out')
});
// if this page was loaded by Actions On Google, redirect to complete authorization flow
typeof redirect_uri != 'undefined' ? window.location = redirectURL : void 0
})
}
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) == variable) {
return decodeURIComponent(pair[1]);
}
}
console.log('Query variable %s not found', variable);
}
#dana Have you tried adding meta tag?
<meta name="google-signin-ux_mode" content="redirect">
With help from Google support & engineering, this is now resolved:
As noted above, I had to include this meta tag: <meta name="google-signin-ux_mode" content="redirect">
I needed to have https://my-auth-endpoint.com/ in my project's authorized redirect URI. It is not enough to have it only in Authorized javascript origins. The other key thing is to include the trailing slash, I hadn't originally and it will not work without it.
Below is the simple code foundation you can use to get a working version of an authorization endpoint for actions on google account linking:
.html:
<!DOCTYPE html>
<html>
<head>
<title>Authorization Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="google-signin-client_id" content="948762963734-2kbegoe3i9ieqc6vjmabh0rqqkmxxxxx.apps.googleusercontent.com">
<meta name="google-signin-ux_mode" content="redirect">
<script src="js/googleAuth.js"></script>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<link rel="stylesheet" href="css/googleAuth.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script>
sessionStorage['jsonData'] == null ? storeQueryVariables() : void 0
</script>
</head>
<body>
<header class="bgimg-1 w3-display-container w3-grayscale-min" id="loginScreen">
<div class="w3-display-topleft w3-padding-xxlarge w3-text-yellow" style="top:5px">
<span class="w3-text-white">Google Sign In</span><br>
<span class="w3-large">Sign in with your Google account</span><br><br>
<div class="g-signin2" data-onsuccess="onSignIn"></div><br><br>
</div>
</header>
</body>
</html>
.js:
// Retrieve user data, store to DynamoDB and complete the redirect process to finish account linking
function onSignIn(googleUser) {
let profile = googleUser.getBasicProfile(),
id = profile.getId(),
name = profile.getName(),
email = profile.getEmail(),
token = googleUser.getAuthResponse().id_token,
redirect_uri = 'https://oauth-redirect.googleusercontent.com/r/vital-code-16xxxx',
jsonData = JSON.parse(sessionStorage['jsonData']),
redirectURL = redirect_uri + '#access_token=' + token + '&token_type=bearer&state=' + jsonData.state
// store the user's name, ID and access token
storeUserData(email, name, id, token, function() {
// sign out of google for this app
let auth2 = gapi.auth2.getAuthInstance();
auth2.signOut()
// if this page was loaded by Actions On Google, redirect to complete authorization flow
typeof redirect_uri != 'undefined' ? window.location = redirectURL : void 0
})
}
// Store the user data to db
function storeUserData (email, name, id, token, callback) {
// removed for simplicity
}
// Store URI query variable 'state' to browser cache
function storeQueryVariables() {
let qvar = {
'state': getQueryVariable('state')
}
storeLocally(qvar)
}
// Get any variable from incoming URI
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) == variable) {
return decodeURIComponent(pair[1]);
}
}
console.log('Query variable %s not found', variable);
}
// Store JSON object input to local browser cache
function storeLocally (jsonData) {
if (typeof(Storage) !== 'undefined') {
sessionStorage['jsonData'] = JSON.stringify(jsonData)
} else {
console.log('Problem: local web storage not available')
}
}

Custom JQuery Mobile Button

I'm using jQuery Mobile 1.3, and attempted to follow a tutorial in order to create my own custom widget.
I'm getting stuck on the very first step, and I'm not sure if I'm following the right example for the right version of jQuery mobile.
There is no error on the page, my element is just never enriched.
(function($) {
$.widget("mobile.target", $.mobile.button, {
/** Available options for the widget are specified here, along with default values. */
options: {
inline: false,
mode: "default",
height: 200
},
/** Mandatory method - automatically called by jQuery Mobile to initialise the widget. */
_create: function() {
var inputElement = this.element;
var opts = $.extend(this.options, inputElement.data("options"));
$(document).trigger("targetcreate");
inputElement.after("<button>" + inputElement.val() + "</button>");
},
/** Custom method to handle updates. */
_update: function() {
var inputElement = this.element;
var opts = $.extend(this.options, inputElement.data("options"));
$(document).trigger("targetupdate");
inputElement.siblings("button").text(inputElement.val());
},
/* Externally callable method to force a refresh of the widget. */
refresh: function() {
return this._update();
}
});
/* Handler which initialises all widget instances during page creation. */
$(document).bind("pagecreate", function(e) {
$(document).trigger("targetbeforecreate");
return $(":jqmData(role='target')", e.target).target();
});
})(jQuery);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
</head>
<body>
<input type="button" data-role="target" value="inp1">
<input type="button" value="inp2">
<div data-role="target">div1</div>
<div type="button">div2</div>
</body>
</html>
(function($) {
$.widget("mobile.target", $.mobile.button, {
/** Available options for the widget are specified here, along with default values. */
options: {
inline: false,
mode: "default",
height: 200
},
/** Mandatory method - automatically called by jQuery Mobile to initialise the widget. */
_create: function() {
var inputElement = this.element;
var opts = $.extend(this.options, inputElement.data("options"));
$(document).trigger("targetcreate");
inputElement.after("<button>" + inputElement.val() + "</button>");
},
/** Custom method to handle updates. */
_update: function() {
var inputElement = this.element;
var opts = $.extend(this.options, inputElement.data("options"));
$(document).trigger("targetupdate");
inputElement.siblings("button").text(inputElement.val());
},
/* Externally callable method to force a refresh of the widget. */
refresh: function() {
return this._update();
}
});
/* Handler which initialises all widget instances during page creation. */
$(document).bind("pagecreate", function(e) {
$(document).trigger("targetbeforecreate");
return $(":jqmData(role='target')", e.target).target();
});
})(jQuery);
Expected output was a custom JQueryMobile button that inherits from the JQueryMobile button. Not just a tiny html5 button in a div. e.g. Div1 rendering the same as Div2 but with customized changes made.
You are trying to get an input element in jQuery code. But you defined a div element in the HTML page.
That is the problem. Change it to input.
Check the below code
The tutorial you followed states that only use the :jqmData if you didn't use the HTML5 data attribute. Remove it
(function($) {
$.widget("mobile.target", $.mobile.widget, {
options: {
inline: false,
mode: "default",
height: 200
},
_create: function() {
var inputElement = this.element;
var opts = $.extend(this.options, inputElement.data("options"));
$(document).trigger("targetcreate");
inputElement.after("<button>"+inputElement.val()+"</button>");
},
_update: function() {
var inputElement = this.element;
var opts = $.extend(this.options, inputElement.data("options"));
$(document).trigger("targetupdate");
inputElement.siblings("button").text(inputElement.val());
},
refresh: function() {
return this._update();
}
});
$(document).bind("pagecreate", function(e) {
$(document).trigger("mywidgetbeforecreate");
return $("[role='target']", e.target).target();
});
})(jQuery);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
</head>
<body>
<input type="button" data-role="target" value="inp1">
<input type="button" value="inp2">
<div type="button" data-role="target">div1</div>
<div type="button">div2</div>
</body>
</html>

TFS Code Coverage on startup screen?

In TFS it is possible to get build historical data on start screen. So when you log into TFS you immediately see the status of your builds. Can the same be achieved for displaying Code Coverage? This is something that SonarCube definitely does nicely.
There isn’t the feature of include Code Coverage result in start screen. But you can custom dashboard widget with test REST API to achieve that.
A simple sample to custom dashboard:
<!DOCTYPE html>
<html>
<head>
<title>Custom widget</title>
<meta charset="utf-8" />
<script src="node_modules/vss-web-extension-sdk/lib/VSS.SDK.js"></script>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles:true
});
VSS.require(["TFS/Dashboards/WidgetHelpers","TFS/TestManagement/RestClient"], function (WidgetHelpers,TFS_Test_WebApi) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("WidgetStarain", function () {
var projectId = VSS.getWebContext().project.id;
var getCodeCoverage = function (widgetSettings) {
return TFS_Test_WebApi.getClient().getBuildCodeCoverage(projectId, 252)
.then(function (buildCoverage) {
var $codeCoverageResult = $('div.codeCoverage');
var $codeCoverageObject = buildCoverage.coverageData[0].coverageStats;
var $detailResult = $codeCoverageObject[0].label + ": Total:" + $codeCoverageObject[0].total + ";covered:" + $codeCoverageObject[0].covered;
$codeCoverageResult.text($detailResult);
//$codeCoverageResult.text(JSON.stringify(buildCoverage))
return WidgetHelpers.WidgetStatusHelper.Success();
}, function (error) {
return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
});
}
return {
load: function (widgetSettings) {
var $title = $('h2.title');
$title.text('starain widget custom');
return getCodeCoverage(widgetSettings);
}
}
//return {
// load: function (widgetSettings) {
// var $title = $('h2.title');
// $title.text('starain widget custom');
// return WidgetHelpers.WidgetStatusHelper.Success();
// }
//}
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title">widgets starain</h2>
<div class="codeCoverage">non code coverage</div>
</div>
</body>
</html>
After that, you can add that widget to the dashboard and check code coverage.

update Google fusiontable using oauth token?

I already have the token and can access my token whose scopes are till fusiontable.
http://www.udayan2k12.com/token.html
<script type="text/javascript">
(function() {
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/auth";
var GOOGLE_CLIENT_ID = "365219651081-7onk7h52kas6cs5m17t1api72ur5tcrh.apps.googleusercontent.com";
var PLUS_ME_SCOPE = "https://www.googleapis.com/auth/fusiontables";
var button = document.createElement("button");
button.innerText = "Authenticate with Google";
button.onclick = function() {
var req = {
"authUrl" : GOOGLE_AUTH_URL,
"clientId" : GOOGLE_CLIENT_ID,
"scopes" : [ PLUS_ME_SCOPE ],
};
oauth2.login(req, function(token) {
alert("Got an OAuth token:\n" + token + "\n"
+ "Token expires in " + oauth2.expiresIn(req) + " ms\n");
document.getElementById('token').innerHTML = token;
}, function(error) {
alert("Error:\n" + error);
});
};
document.body.appendChild(button);
var clearTokens = document.createElement("button");
clearTokens.innerText = "Clear all tokens";
clearTokens.onclick = oauth2.clearAllTokens;
document.body.appendChild(clearTokens);
})();
</script>
But the problem is that i am unable to use that token to update the fusion table.
I want to update it specifically by using JavaScript.
can some one provide me the code to use this token to update fusiontable using the fusion table sql
You should use the JavaScript Google API client, then it is very easy to make authenticated requests to a Google API (see their "Samples" page):
gapi.client.setApiKey('YOUR API KEY');
var query = "INSERT INTO tableId (id, name) VALUES (1, 'test')";
gapi.client.load('fusiontables', 'v1', function(){
gapi.client.fusiontables.query.sql({sql:query}).execute(function(response){console.log(response);});
});
I know most of you are suffering for google auth and inserting and updating fusion table.
I am providing the entire code how to use the gauth lib to insert in a simple manner
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Authorization Request</title>
<script src="https://apis.google.com/js/client.js"></script>
<script type="text/javascript">
function auth() {
var config = {
'client_id': '365219651081-7onk7h52kas6cs5m17t1api72ur5tcrh.apps.googleusercontent.com',
'scope': 'https://www.googleapis.com/auth/fusiontables'
};
gapi.auth.authorize(config, function() {
console.log('login complete');
console.log(gapi.auth.getToken());
});
}
function insert_row(){
alert("insert called");
gapi.client.setApiKey('AIzaSyA0FVy-lEr_MPGk1p_lHSrxGZDcxy6wH4o');
var query = "INSERT INTO 1T_qE-o-EtX24VZASFDn6p3mMoPcWQ_GyErJpPIc(Name, Age) VALUES ('Trial', 100)";
gapi.client.load('fusiontables', 'v1', function(){
gapi.client.fusiontables.query.sql({sql:query}).execute(function(response){console.log(response);});
});
}
</script>
</head>
<body>
<button onclick="auth();">Authorize</button>
<p> </p>
<button onclick="insert_row();">Insert Data</button>
</body>
</html>

Resources