function getAbstract() {
// get text from current selection
var query = getAbstractQuery();
// do the query and write it to cache
var result = Abstract.getDuck (query);
// kind of copy paste but using cache
Abstract.copy (result);
}
function insertAbstract() {
var result = Abstract.paste();
if (result) {
result.blob = Abstract.getBlob (result);
Abstract.insertAbstract(result);
}
}
/**
* will get the active selection
* create an abstract
* write it to cache
*/
function getAbstractQuery() {
// concat all the text in a range
// var c=DocumentApp.getActiveDocument().getSelection().getRangeElements()[0]
var selection = DocumentApp
.getActiveDocument()
.getSelection()
return selection ?
selection
.getRangeElements()
.reduce(function(p,c) {
return p + getElementText (
c.getElement(),
c.isPartial(),
c.getStartOffset(),
c.getEndOffsetInclusive()
);
},'') : '' ;
/**
* display elements and their children
* @param {Element} elem the element
* @param {boolean} [partial=false] element is only partial
* @param {number} [start] start of partial element
* @param {number} [finish] finish of partial element
*/
function getElementText (elem ,partial,start,finish) {
try {
// not all elements can be cast as text
return partial ?
elem.asText().getText().slice (start, finish+1) :
elem.asText().getText();
}
catch(err) {
return '';
}
}
}
/**
* gets an abstract of the active selection and stores it to cache
*/
var Abstract = Object.create(null, {
settings: {
value:{
PROVIDER:"https://api.duckduckgo.com/",
HEADING_LEVEL:DocumentApp.ParagraphHeading.HEADING3,
CACHE_KEY:'abstract-duck',
IMAGE_WIDTH:100,
COPY_CACHE:CacheService.getDocumentCache(),
DUCK_CACHE:CacheService.getScriptCache(),
MAX_SIZE:100000
}
},
paste: {
value: function () {
// kind of copy past but using cache
var result = this.settings.COPY_CACHE.get (this.settings.CACHE_KEY);
return result ? JSON.parse(result) : null;
}
},
copy: {
value: function (value) {
// kind of copy past but using cache
this.settings.COPY_CACHE.put (
this.settings.CACHE_KEY, JSON.stringify(value)
);
}
},
/**
* insert an item at current position
*/
insertAbstract: {
value:function (item) {
var doc = DocumentApp.getActiveDocument();
var cursor = doc.getCursor();
if(!cursor) return null;
var target = cursor.getElement();
var parent = target.getParent().asBody();
var body = doc.getBody();
// insert the heading
parent.insertParagraph(
parent.getChildIndex(target), item.query
)
.setHeading(this.settings.HEADING_LEVEL);
// insert the image
if (item.blob) {
var image = parent.insertImage(parent.getChildIndex(target),item.blob);
this.imageScale(image,this.settings.IMAGE_WIDTH);
if (item.url) {
image.setLinkUrl (item.url);
}
}
// insert the paragraph
parent.insertParagraph(parent.getChildIndex(target), item.abstract);
}
},
/**
* scale to given width
* @param {InlineImage} inlineImage the image
* @param {number} width the target width
* @return {InlineImage} for chaining
*/
imageScale: {
value:function (inlineImage, width) {
inlineImage.setHeight(
inlineImage.getHeight() * width / inlineImage.getWidth()
);
inlineImage.setWidth(width);
return inlineImage;
}
},
/**
* execute a duckduckgo query and return cached abstract
* @param {string} query the string to search duckduckgo.com on
* @param {boolean} [useCache=true] whether to attempt to use cache
* @return {object} the query abstract and image
*/
getDuck:{
value:function (query, useCache) {
// use cache?
var useCache = typeof useCache === typeof undefined ? true : useCache;
var package = {};
if (query) {
var url = this.settings.PROVIDER +
"?q=" + encodeURIComponent(query) +
"&format=json";
// see if its in cache
var cacheResponse = this.settings.DUCK_CACHE.get(url);
if (!cacheResponse) {
// if its not there, then do a query
var response = UrlFetchApp.fetch (url);
if (response.getResponseCode() === 200) {
var result = response.getContentText();
// convert data
var data = JSON.parse (result);
// get and clean the image url
var imageUrl = data.Image ?
data.Image.replace(/['"]+/g, '') : '';
// get the image blob
var img = imageUrl ? this.getImageFromUrl(imageUrl) : null;
// convert to b64
var b64 = img ? Utilities.base64Encode(img.getBytes()) : '';
package = {
abstract: data.Abstract ? data.Abstract : 'no abstract found',
image:imageUrl,
url:data.Results && data.Results.length && Array.isArray(data.Results) ?
data.Results[0].FirstURL : '',
imageB64: b64,
query:query
};
// cacheable
var packageString = JSON.stringify(package);
if (packageString.length > this.settings.MAX_SIZE) {
package.tooBig = true;
package.imageB64 = '';
packageString = JSON.stringify(package);
}
this.settings.DUCK_CACHE.put (url, JSON.stringify(package));
}
}
else {
package = JSON.parse(cacheResponse);
}
}
return package;
}
},
getBlob: {
value:function (package) {
// we already have it
if (!package.tooBig) {
return package.imageB64 ? Utilities.newBlob(
Utilities.base64Decode(package.imageB64)) : null;
}
else {
return package.image ? this.getImageFromUrl(package.image) : null;
}
}
},
/**
* get an image from a url
* @param {string} imageUrl the image url
* @return {Blob|null} te image
*/
getImageFromUrl: {
value:function (imageUrl) {
if (!imageUrl) return null;
return UrlFetchApp.fetch(imageUrl).getBlob()
}
}
});