-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 91b8cd0
Showing
5 changed files
with
1,097 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/** | ||
* @name Preso extension | ||
* | ||
* This script, as part of a Chrome extension, allows the refreshing and looping | ||
* of Google Slides documents (without having to resort to "Publishing to web"). | ||
* | ||
* See: https://plemont.github.io for details. | ||
*/ | ||
const PRESO_REGEX = /^https:\/\/docs\.google\.com\/presentation\/d\/[^/]+\/present(.*)$/; | ||
|
||
function fullscreenAndLoop(tab) { | ||
chrome.windows.getCurrent(win => | ||
chrome.windows.update(win.id, {state: 'fullscreen'})); | ||
let nextUrl = calculateNextSlideUrl(tab.url); | ||
setTimeout(createReload(tab.id, nextUrl), 10000); | ||
} | ||
|
||
function calculateNextSlideUrl(url) { | ||
let [hostPath, parts] = url.split('?'); | ||
let params = extractParamsDictionary(parts); | ||
let slideId = params.slide; | ||
let matches; | ||
if (slideId) { | ||
let idRegex = /^(id\..*)_(\d+)_(\d+)$/; | ||
if ((matches = idRegex.exec(slideId)) !== null) { | ||
let currentPage = +matches[2]; | ||
let totalPages = +matches[3]; | ||
let newSlide = [matches[1], (currentPage + 1) % totalPages, | ||
totalPages].join('_'); | ||
return hostPath + '?loop=1&slide=' + newSlide; | ||
} | ||
} | ||
return url; | ||
} | ||
|
||
function createReload(tabId, nextUrl) { | ||
return function() { | ||
chrome.tabs.query({ | ||
active: true, | ||
lastFocusedWindow: true | ||
}, function(tabs) { | ||
let url = tabs[0].url; | ||
let matches = PRESO_REGEX.exec(url); | ||
if (matches) { | ||
chrome.tabs.update(tabId, {url: nextUrl}); | ||
} | ||
}); | ||
}; | ||
} | ||
|
||
function extractParamsDictionary(parts) { | ||
let params = {}; | ||
parts.split('&').forEach(part => { | ||
let [key, value] = part.split('='); | ||
params[key] = value; | ||
}); | ||
return params; | ||
} | ||
|
||
function checkForValidUrl(tabId, changeInfo, tab) { | ||
// Only process events that are completions, not loading events. | ||
if (changeInfo.status !== 'complete') { | ||
return; | ||
} | ||
let matches; | ||
if ((matches = PRESO_REGEX.exec(tab.url)) !== null) { | ||
let args = matches[1]; | ||
if (args.startsWith('?')) { | ||
let parts = args.substr(1); | ||
let params = extractParamsDictionary(parts); | ||
|
||
// If there is a loop parameter, then just prepare for the next page. | ||
if (params.loop) { | ||
fullscreenAndLoop(tab); | ||
} else { | ||
// if no loop parameter, highlight the pageAction button. | ||
chrome.pageAction.show(tab.id); | ||
} | ||
} | ||
} | ||
} | ||
|
||
chrome.pageAction.onClicked.addListener(() => { | ||
chrome.tabs.query({ | ||
active: true, | ||
lastFocusedWindow: true | ||
}, function(tabs) { | ||
let url = tabs[0].url; | ||
let matches = PRESO_REGEX.exec(url); | ||
if (matches) { | ||
fullscreenAndLoop(tabs[0]); | ||
} | ||
}); | ||
}); | ||
|
||
// Listen for any changes to the URL of any tab. | ||
chrome.tabs.onUpdated.addListener(checkForValidUrl); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "Preso", | ||
"version": "0.0.1", | ||
"manifest_version": 2, | ||
"description": "Refresh'n'loop presentations", | ||
"homepage_url": "https://plemont.github.io", | ||
"background": { | ||
"scripts": [ | ||
"background.js" | ||
], | ||
"persistent": true | ||
}, | ||
"page_action": { | ||
"default_title": "Present'n'loop" | ||
}, | ||
"permissions": [ | ||
"tabs", | ||
"webNavigation", | ||
"activeTab" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
var CLIENT_ID = 'INSERT_CLIENT_ID'; | ||
var CLIENT_SECRET = 'INSERT_CLIENT_SECRET'; | ||
|
||
/** | ||
* This script allows the stepping through of the Authorization Code Grant in | ||
* order to obtain a refresh token. | ||
* | ||
* This script uses the out-of-band redirect URI, which is not part of the | ||
* OAuth2 standard, to allow not redirecting the user. If this does not work | ||
* with your API, try instead the OAuth playground: | ||
* https://developers.google.com/oauthplayground/ | ||
* | ||
* Execute script twice: | ||
* Execution 1, will result in a URL, which when placed in the browser will | ||
* issue a code. | ||
* Execution 2: place the code in "CODE" below and execute. If successful a | ||
* refresh token will be printed to the console. | ||
*/ | ||
|
||
// Enter required scopes, e.g. ['https://www.googleapis.com/auth/drive'] | ||
var SCOPES = ['https://www.googleapis.com/auth/presentations', | ||
'https://www.googleapis.com/auth/drive']; | ||
|
||
// Auth URL, e.g. https://accounts.google.com/o/oauth2/auth | ||
var AUTH_URL = 'https://accounts.google.com/o/oauth2/auth'; | ||
// Token URL, e.g. https://accounts.google.com/o/oauth2/token | ||
var TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'; | ||
|
||
var CODE = ''; | ||
|
||
function main() { | ||
if (CODE) { | ||
generateRefreshToken(); | ||
} else { | ||
generateAuthUrl(); | ||
} | ||
} | ||
|
||
/** | ||
* Creates the URL for pasting in the browser, which will generate the code | ||
* to be placed in the CODE variable. | ||
*/ | ||
function generateAuthUrl() { | ||
var payload = { | ||
scope: SCOPES.join(' '), | ||
// Specify that no redirection should take place | ||
// This is Google-specific and not part of the OAuth2 specification. | ||
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', | ||
response_type: 'code', | ||
access_type: 'offline', | ||
client_id: CLIENT_ID | ||
}; | ||
var options = {payload: payload}; | ||
var request = UrlFetchApp.getRequest(AUTH_URL, options); | ||
Logger.log( | ||
'Browse to the following URL: ' + AUTH_URL + '?' + request.payload); | ||
} | ||
|
||
/** | ||
* Generates a refresh token given the authorization code. | ||
*/ | ||
function generateRefreshToken() { | ||
var payload = { | ||
code: CODE, | ||
client_id: CLIENT_ID, | ||
client_secret: CLIENT_SECRET, | ||
// Specify that no redirection should take place | ||
// This is Google-specific and not part of the OAuth2 specification. | ||
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', | ||
grant_type: 'authorization_code' | ||
}; | ||
var options = {method: 'POST', payload: payload}; | ||
var response = UrlFetchApp.fetch(TOKEN_URL, options); | ||
var data = JSON.parse(response.getContentText()); | ||
if (data.refresh_token) { | ||
var msg = 'Success! Refresh token: ' + data.refresh_token + | ||
'\n\nThe following may also be a useful format for pasting into your script:\n\n' + | ||
'var CLIENT_ID = \'' + CLIENT_ID + '\';\n' + | ||
'var CLIENT_SECRET = \'' + CLIENT_SECRET + '\';\n' + | ||
'var REFRESH_TOKEN = \'' + data.refresh_token + '\';\n'; | ||
Logger.log(msg); | ||
} else { | ||
Logger.log( | ||
'Error, failed to generate Refresh token: ' + | ||
response.getContentText()); | ||
} | ||
} |
Oops, something went wrong.