antville/static/formica.html

451 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<title>Antvilles Formica Bookmarklet</title>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='icon' type='image/x-icon' href='img/favicon.png'>
<link rel='shortcut icon' type='image/x-icon' href='img/favicon.png'>
<link rel='apple-touch-icon' href='img/favicon.png'>
<script>
const global = window;
const settings = {};
let proxyUri;
(search => {
search = String(search).substr(1);
if (search.length > 0) {
let parts = search.split('&');
parts.forEach((item, index) => {
parts = item.split('=');
settings[parts[0]] = decodeURIComponent(parts[1] || '').trim();
});
}
proxyUri = settings.b + 'claustra/proxy';
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = settings.b + 'main.css';
link.onload = () => document.querySelector('main').hidden = false;
document.head.appendChild(link);
})(location.search);
</script>
<style>
body {
margin: 1em;
padding: 1em;
}
.image {
height: 192px;
}
.image::after {
content: '';
background: center/10px url('img/favicon.png') !important;
opacity: 0.2;
top: 1px;
left: 1px;
bottom: 1px;
right: 1px;
position: absolute;
z-index: -1;
}
figure.uk-panel-box {
padding: 16px;
}
</style>
<main hidden class='uk-grid' data-uk-grid-margin>
<div class='uk-width-medium-2-5 uk-hidden left'>
<figure class='uk-panel uk-panel-box uk-panel-box-secondary' onclick='onToggleImage(event)'>
<div class='image uk-panel-teaser'>
<div id='dimensions' class='uk-panel-badge uk-badge'>0×0</div>
<img id='preview' class='loading uk-responsive-width uk-responsive-height'>
</div>
<figcaption>
<div id='info' class='info uk-float-left'></div>
<div class='controls uk-button-group uk-float-right'>
<a href='javascript:' class='uk-button' onclick='onChangeImage(event, -1)'>
<i class='uk-icon-arrow-left'></i>
</a>
<a href='javascript:' class='uk-button' onclick='onChangeImage(event, +1)'>
<i class='uk-icon-arrow-right'></i>
</a>
</div>
</figcaption>
</figure>
</div>
<form class='uk-width-medium-1-1 uk-form uk-form-stacked right'>
<div class='uk-form-row'>
<div class='uk-form-controls uk-text-bold'>
<input id='title' class='uk-form uk-width-medium-1-1' type='text' placeholder='Title'>
</div>
</div>
<div class='uk-form-row'>
<div class='uk-form-controls'>
<textarea id='text' class='uk-form uk-width-medium-1-1' rows=10 placeholder='Description'></textarea>
</div>
</div>
<div class='uk-form-row uk-text-right'>
<div id='feedback' class='uk-float-left'>
<i class='uk-icon-refresh uk-icon-spin'></i> Loading…
</div>
<button id='submit' name='button' type='button' class='uk-button uk-button-primary' onclick='onPublish(event)'>
<i class='uk-icon-image uk-hidden' id='image'></i>
Publish
</button>
</div>
</form>
</main>
<script>
(() => {
const script = document.createElement('script');
script.src = settings.b + 'main.js';
document.body.appendChild(script);
let _currentIndex;
let _images;
let _siteUri;
let _sourceUri;
global.onChangeImage = (event, direction = 1) => {
event.preventDefault();
event.stopPropagation();
let index = (_currentIndex || 0) + direction;
if (index >= _images.length) {
index = 0;
} else if (index < 0) {
index = _images.length - 1;
}
_currentIndex = index;
displayImage(_images[index]);
};
global.onToggleImage = event => {
const classList = (event ? event.currentTarget : document.querySelector('figure')).classList;
classList.toggle('uk-panel-box-primary');
classList.toggle('uk-panel-box-secondary');
global.image.classList.toggle('uk-hidden');
global.image.hidden = !global.image.hidden;
};
global.onPublish = event => {
event.preventDefault();
feedback(false);
event.currentTarget.disabled = true;
const imageUri = global.image.hidden ? _images[_currentIndex].src : null;
post(_sourceUri, imageUri);
};
const main = () => {
if (!settings.s || !settings.l) return;
_siteUri = settings.s;
_sourceUri = settings.l;
if (!_siteUri || !_sourceUri) return;
_images = [];
_currentIndex = 0;
if (_sourceUri.match(new RegExp('(?:bmp|gif|jpg|jpeg|png)$', 'i'))) {
feedback();
return addImage(_sourceUri);
}
const customCookie = settings.k;
const data = {
url: _sourceUri,
cookie: document.cookie + '; ' + customCookie,
ua: 'Mozilla/5.0 (Formica Bookmarklet by Antville)',
ref: location.href
}
jsonp(proxyUri, data, (result, error) => {
const content = result.content;
let text = '';
if (settings.c) text = settings.c.trim();
if (error) {
text = `[${text}](${_sourceUri})`;
if (settings.r) text += ` _(via ${settings.r})_.`;
global.text.value = text;
return feedback(null, 'Could not fetch URL; are you logged in?');
}
if (!content) {
return feedback('URL error; no data.');
}
const parser = new DOMParser();
const sourceDocument = parser.parseFromString(content, 'text/html');
const openGraphTitle = (meta => meta ? meta.getAttribute('content') : null)(
sourceDocument.querySelector('meta[property="og:title"]')
);
const openGraphDescription = (meta => meta ? meta.getAttribute('content') : null)(
sourceDocument.querySelector('meta[property="og:description"]')
);
const openGraphImage = (meta => meta ? meta.getAttribute('content') : null)(
sourceDocument.querySelector('meta[property="og:image"]')
);
if (!text || text === openGraphTitle) text = openGraphDescription || '';
text = `[${text}](${_sourceUri})`;
if (settings.r) text += ` _(via ${settings.r})_.`;
global.title.value = openGraphTitle || sourceDocument.title;
global.text.value = text;
const baseHref = getBaseHref(sourceDocument);
sourceDocument.querySelectorAll('img').forEach(img => {
addImage(img.src, baseHref);
});
if (openGraphImage) addImage(openGraphImage, baseHref);
const regexes = [
/(?:\(|=|"|')([^\s\(\)'">]+\.(?:gif|jpg|jpeg|png|webp))/gi
];
regexes.forEach(regex => {
let match;
while (match = regex.exec(content)) {
if (match[1]) addImage(match[1], baseHref);
}
});
feedback();
});
};
const getBaseHref = doc => {
const base = doc.querySelector('base');
if (base) {
const href = base.getAttribute('href');
if (href) return href;
}
const index = _sourceUri.lastIndexOf('/');
if (index !== _sourceUri.length - 1) {
return _sourceUri.substring(0, index + 1);
}
return _sourceUri;
};
const addImage = (url, baseUri) => {
if (!url || _images[url]) return;
if (!addImage.counter) addImage.counter = 0;
_images[url] = true;
if (url.charAt(0) === '/') {
if (url.charAt(1) !== '/') {
// url starts with one single slash: prepend root of _sourceUri.
const index = _sourceUri.indexOf('/', _sourceUri.indexOf('://') + 3);
baseUri = index > -1 ? _sourceUri.substring(0, index) : _sourceUri;
url = baseUri + url;
}
} else if (url.indexOf('://') < 0) {
// url does neither start with a slash, nor contain
// a protocol: prepend _sourceUri up to the final slash.
baseUri || (baseUri = _sourceUri.substring(0, _sourceUri.lastIndexOf('/') + 1));
url = baseUri + url;
}
const image = new Image();
image.onload = function () {
if (this.width < 100 || this.height < 100) return;
_images.push(this);
displayImage(_images[0]);
addImage.counter += 1;
if (_images.length && addImage.counter) {
showImagePane();
}
};
image.src = url;
};
const displayImage = image => {
const MAX_WIDTH = 200;
const MAX_HEIGHT = 600;
let width = image.width;
let height = image.height;
let factorH = 1;
let factorV = 1;
if (width > MAX_WIDTH) factorH = MAX_WIDTH / width;
if (height > MAX_HEIGHT) factorV = MAX_HEIGHT / height;
if (factorH !== 1 || factorV !== 1) {
width = Math.ceil(width * (factorH < factorV ? factorH : factorV));
height = Math.ceil(height * (factorH < factorV ? factorH : factorV));
}
global.preview.width = width;
global.preview.height = height;
global.preview.src = image.src;
global.dimensions.innerHTML = image.width + '×' + image.height;
info.innerHTML = (_currentIndex + 1) + ' of ' + _images.length;
return;
};
const post = (url, imageUrl) => {
const postStory = imageName => {
feedback("<i class='uk-icon-refresh uk-icon-spin'></i> Posting story…");
let imageMacro = '';
if (imageName) imageMacro = `\n\n<\x25 image '${imageName}' box link='${url}' \x25>`;
const data = {
save: 1,
title: global.title.value,
text: global.text.value + imageMacro,
'og:image': imageUrl
}
return jsonp(_siteUri + '/stories/create', data, (res, error) => {
restore();
if (error) return feedback(null, 'Access error; are you logged in?');
if (!res) {
feedback(null, 'Story error; maybe try again…');
} else if (global.opener) {
feedback("<i class='uk-icon-check-circle'></i> Success!");
setTimeout(() => global.close(), 1000);
} else {
location.href = _siteUri + '/stories/' + res;
}
});
}
if (imageUrl) {
feedback("<i class='uk-icon-refresh uk-icon-spin'></i> Uploading image…");
const name = 'image-' + Date.now();
const data = {
save: 1,
file_origin: imageUrl,
name: name,
maxWidth: settings.w || '',
maxHeight: settings.h || ''
};
jsonp(_siteUri + '/images/create', data, (res, error) => {
restore();
if (error) return feedback(null, 'Access error; are you logged in?');
if (!data) {
feedback(null, 'Image error; maybe try again…');
} else {
postStory(name);
}
});
} else {
postStory();
}
return;
}
const showImagePane = () => {
const rightPaneClasses = document.querySelector('.right').classList;
if (rightPaneClasses.contains('uk-width-medium-1-1')) {
rightPaneClasses.toggle('uk-width-medium-3-5');
rightPaneClasses.toggle('uk-width-medium-1-1');
document.querySelector('.left').classList.toggle('uk-hidden');
onToggleImage();
}
};
const feedback = (message, error) => {
if (error) {
global.feedback.innerHTML = "<i class='uk-icon-warning'></i> " + error;
} else if (message) {
global.feedback.innerHTML = message;
} else {
global.feedback.innerHTML = '';
}
};
const restore = () => {
setTimeout(() => global.submit.disabled = false, 3000);
};
const jsonp = (uri, data = {}, callback = () => {}) => {
const handlerId = 'jsonpHandler' + Date.now() +
parseInt(Math.random() * Math.pow(2, 16), 10);
window[handlerId] = (data, error) => {
window[handlerId] = null;
window['script-' + handlerId].remove();
return callback(data, error);
};
const script = document.createElement('script');
let queryString = '';
if (data) {
queryString = Object.keys(data).map(key => {
const value = data[key];
return `${key}=${encodeURIComponent(value)}`;
}).join('&');
}
script.src = `${uri}?${queryString}&callback=${handlerId}`;
script.id = 'script-' + handlerId;
script.onload = () => {
setTimeout(() => {
const handler = window[handlerId];
if (handler) handler(null, 'timeout');
}, 1000);
};
document.body.append(script);
}
main();
})();
</script>