@@ -78,4 +78,22 @@ var H = { | |||||
return post; | return post; | ||||
}, | }, | ||||
getTitleStrict: function(content) { | |||||
var eol = content.indexOf("\n"); | |||||
var title = ""; | |||||
var newContent = content; | |||||
if (content.indexOf("# ") === 0) { | |||||
// Title is in the format: | |||||
// # Some title | |||||
if (eol !== -1) { | |||||
// First line should start with # and end with \n | |||||
newContent = content.substring(eol).leftTrim(); | |||||
title = content.substring("# ".length, eol); | |||||
} | |||||
} | |||||
return { | |||||
title: title, | |||||
content: newContent | |||||
}; | |||||
}, | |||||
}; | }; |
@@ -1,9 +1,9 @@ | |||||
Paste Chrome extension | |||||
====================== | |||||
Write.as Chrome extension | |||||
========================= | |||||
Paste lets you instantly share text or code from your browser. | |||||
Write.as for Chrome lets you instantly share text from your browser. | |||||
![Selecting text in Paste](https://paste.as/images/pic01.jpg) | |||||
![Selecting text in Write.as](https://paste.as/images/pic01.jpg) | |||||
# License | # License | ||||
@@ -1,9 +1,14 @@ | |||||
function publish(content, font) { | function publish(content, font) { | ||||
if (content.trim() == "") { | |||||
return; | |||||
} | |||||
var post = H.getTitleStrict(content); | |||||
var http = new XMLHttpRequest(); | var http = new XMLHttpRequest(); | ||||
var url = "https://write.as/api/"; | |||||
var url = "https://write.as/api/posts"; | |||||
var lang = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage); | var lang = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage); | ||||
lang = lang.substring(0, 2); | lang = lang.substring(0, 2); | ||||
var params = "w=" + encodeURIComponent(content) + "&font=" + font + "&lang=" + lang; | |||||
var params = "body=" + encodeURIComponent(post.content) + "&title=" + encodeURIComponent(post.title) + "&font=" + font + "&lang=" + lang + "&rtl=auto"; | |||||
http.open("POST", url, true); | http.open("POST", url, true); | ||||
//Send the proper header information along with the request | //Send the proper header information along with the request | ||||
@@ -11,18 +16,24 @@ function publish(content, font) { | |||||
http.onreadystatechange = function() { | http.onreadystatechange = function() { | ||||
if (http.readyState == 4) { | if (http.readyState == 4) { | ||||
if (http.status == 200) { | |||||
data = http.responseText.split("\n"); | |||||
if (http.status == 201) { | |||||
data = JSON.parse(http.responseText); | |||||
// Pull out data parts | // Pull out data parts | ||||
url = data[0]; | |||||
id = url.substr(url.lastIndexOf("/")+1); | |||||
editToken = data[1]; | |||||
// Save the data | |||||
posts = JSON.parse(H.get('posts', '[]')); | |||||
posts.push(H.createPost(id, editToken, content)); | |||||
H.set('posts', JSON.stringify(posts)); | |||||
id = data.data.id; | |||||
if (font == 'code' || font === 'mono') { | |||||
url = "https://paste.as/"+id; | |||||
} else { | |||||
url = "https://write.as/"+id; | |||||
} | |||||
editToken = data.data.token; | |||||
// Save the data if user wasn't logged in | |||||
if (typeof data.data.owner === 'undefined' || data.data.owner == "") { | |||||
posts = JSON.parse(H.get('posts', '[]')); | |||||
posts.push(H.createPost(id, editToken, post.content)); | |||||
H.set('posts', JSON.stringify(posts)); | |||||
} | |||||
// Launch post | // Launch post | ||||
chrome.tabs.create({ url: url }); | chrome.tabs.create({ url: url }); | ||||
} else { | } else { | ||||
@@ -0,0 +1,18 @@ | |||||
@font-face { | |||||
font-family: 'Lora'; | |||||
font-style: normal; | |||||
font-weight: 400; | |||||
src: local('Lora'), local('Lora-Regular'), url('fonts/lora.woff2') format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Lora'; | |||||
font-style: normal; | |||||
font-weight: 700; | |||||
src: local('Lora Bold'), local('Lora-Bold'), url('fonts/lora-700.woff2') format('woff2'); | |||||
} | |||||
@font-face { | |||||
font-family: 'Open Sans'; | |||||
font-style: normal; | |||||
font-weight: 400; | |||||
src: local('Open Sans'), local('OpenSans'), url('fonts/open-sans.woff2') format('woff2'); | |||||
} |
@@ -0,0 +1,4 @@ | |||||
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> | |||||
<path d="M0 0h24v24H0z" fill="none"/> | |||||
<path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/> | |||||
</svg> |
@@ -1,9 +1,10 @@ | |||||
{ | { | ||||
"manifest_version": 2, | "manifest_version": 2, | ||||
"name": "Paste by Write.as", | |||||
"description": "Effortlessly share text online.", | |||||
"version": "1.3", | |||||
"name": "Write.as for Chrome", | |||||
"short_name": "Write.as", | |||||
"description": "Publish a thought in seconds.", | |||||
"version": "2.0", | |||||
"icons": { | "icons": { | ||||
"16": "icon16.png", | "16": "icon16.png", | ||||
@@ -28,8 +29,7 @@ | |||||
"activeTab", | "activeTab", | ||||
"contextMenus", | "contextMenus", | ||||
"webRequest", | "webRequest", | ||||
"*://*.write.as/", | |||||
"https://fonts.googleapis.com/" | |||||
"*://*.write.as/" | |||||
], | ], | ||||
"externally_connectable": { | "externally_connectable": { | ||||
"matches": ["*://*.write.as/*"] | "matches": ["*://*.write.as/*"] | ||||
@@ -1,7 +1,7 @@ | |||||
<!doctype html> | <!doctype html> | ||||
<html> | <html> | ||||
<head> | <head> | ||||
<title>Paste by Write.as</title> | |||||
<title>Write.as</title> | |||||
<style> | <style> | ||||
body, input[type=submit], label[for=norm], textarea.norm { | body, input[type=submit], label[for=norm], textarea.norm { | ||||
font-family: Lora, serif; | font-family: Lora, serif; | ||||
@@ -38,8 +38,15 @@ | |||||
color: white; | color: white; | ||||
text-align: right; | text-align: right; | ||||
margin-left: 0.5em; | margin-left: 0.5em; | ||||
width: 7em; | |||||
text-align: center; | |||||
transition: all .2s ease-out; | |||||
border-radius: .25em; | |||||
} | } | ||||
input[type=submit].disabled { | |||||
input[type=submit]:hover { | |||||
background-color: #7d82c4; | |||||
} | |||||
input[type=submit].disabled, input[type=submit].disabled:hover { | |||||
background: #ddd; | background: #ddd; | ||||
color: #999; | color: #999; | ||||
border-color: #eee; | border-color: #eee; | ||||
@@ -47,23 +54,38 @@ | |||||
#publish-holder, #result-holder { | #publish-holder, #result-holder { | ||||
text-align: right; | text-align: right; | ||||
} | } | ||||
#result-holder { | |||||
#result-holder, #account-tools { | |||||
display: none; | display: none; | ||||
} | } | ||||
#url { | #url { | ||||
width: 18em; | width: 18em; | ||||
margin-right: 0.5em; | margin-right: 0.5em; | ||||
} | } | ||||
label[for=mono], label[for=code], textarea.mono, textarea.code { | |||||
label[for=mono], label[for=wrap], label[for=code], textarea.mono, textarea.wrap, textarea.code { | |||||
font-family: monospace, monospace; | font-family: monospace, monospace; | ||||
font-size: 1em; | font-size: 1em; | ||||
} | } | ||||
textarea.mono, textarea.code { | |||||
white-space: pre; | |||||
} | |||||
label, label[for=mono], label[for=wrap], label[for=code] { | |||||
font-size: 0.86em; | |||||
} | |||||
body.popout #publish-holder, body.popout #result-holder { | body.popout #publish-holder, body.popout #result-holder { | ||||
position: fixed; | position: fixed; | ||||
bottom: 1em; | bottom: 1em; | ||||
left: 1em; | left: 1em; | ||||
right: 1em; | right: 1em; | ||||
} | } | ||||
#account-tools { | |||||
margin-top: -0.5em; | |||||
margin-bottom: 0.5em; | |||||
font-size: 0.86em; | |||||
} | |||||
#account-tools a { | |||||
line-height: 24px; | |||||
margin-left: 0.5em; | |||||
} | |||||
body.popout textarea { | body.popout textarea { | ||||
position: fixed; | position: fixed; | ||||
top: 0; | top: 0; | ||||
@@ -72,10 +94,29 @@ | |||||
bottom: 4em; | bottom: 4em; | ||||
border: 0; | border: 0; | ||||
border-bottom: 1px solid #dfdfdf; | border-bottom: 1px solid #dfdfdf; | ||||
min-height: 0; | |||||
} | |||||
a#popout { | |||||
padding: 0.5em; | |||||
position: absolute; | |||||
left: 0.75em; | |||||
bottom: 0.75em; | |||||
} | |||||
a#popout img { | |||||
vertical-align: middle; | |||||
} | } | ||||
body.popout a#popout { | body.popout a#popout { | ||||
display: none; | display: none; | ||||
} | } | ||||
#username { | |||||
font-weight: bold; | |||||
} | |||||
#sync.disabled { | |||||
display: inline; | |||||
color: #999; | |||||
font-style: italic; | |||||
text-decoration: none; | |||||
} | |||||
</style> | </style> | ||||
<script src="H.js"></script> | <script src="H.js"></script> | ||||
@@ -83,23 +124,26 @@ | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<form name="postForm" method="post"> | <form name="postForm" method="post"> | ||||
<textarea id="content" placeholder="Paste..."></textarea> | |||||
<div id="account-tools"> | |||||
Writing as <span id="username">write.as</span>. <a id="sync" href="#">Sync...</a> | |||||
</div> | |||||
<textarea id="content" placeholder="Write..."></textarea> | |||||
<div id="result-holder"> | <div id="result-holder"> | ||||
<input id="url" type="url" readonly /> | <input id="url" type="url" readonly /> | ||||
<a id="url-link" target="_blank">Open</a> | <a id="url-link" target="_blank">Open</a> | ||||
</div> | </div> | ||||
<div id="publish-holder"> | <div id="publish-holder"> | ||||
<a href="#" id="popout">Pop out</a> | |||||
<input type="radio" name="font" value="sans" id="sans" checked="checked" /><label for="sans"> Sans</label> | <input type="radio" name="font" value="sans" id="sans" checked="checked" /><label for="sans"> Sans</label> | ||||
<input type="radio" name="font" value="norm" id="norm" /><label for="norm"> Serif</label> | <input type="radio" name="font" value="norm" id="norm" /><label for="norm"> Serif</label> | ||||
<input type="radio" name="font" value="mono" id="mono" /><label for="mono"> Monospace</label> | <input type="radio" name="font" value="mono" id="mono" /><label for="mono"> Monospace</label> | ||||
<input type="radio" name="font" value="wrap" id="wrap" /><label for="wrap"> Wrap</label> | |||||
<input type="radio" name="font" value="code" id="code" /><label for="code"> Code</label> | <input type="radio" name="font" value="code" id="code" /><label for="code"> Code</label> | ||||
<input id="publish" type="submit" value="Publish" /> | <input id="publish" type="submit" value="Publish" /> | ||||
<a href="#" id="popout"><img src="img/ic_launch.svg" /></a> | |||||
</div> | </div> | ||||
</form> | </form> | ||||
<link href='https://fonts.googleapis.com/css?family=Lora:400,700' rel='stylesheet' type='text/css'> | |||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'> | |||||
<link href='fonts.css' rel='stylesheet' type='text/css'> | |||||
</body> | </body> | ||||
</html> | </html> | ||||
@@ -8,14 +8,15 @@ function publish(content, font) { | |||||
} | } | ||||
$publish.classList.add('disabled'); | $publish.classList.add('disabled'); | ||||
$publish.value = "Publishing..."; | |||||
setPublishText(font, true); | |||||
$publish.disabled = true; | $publish.disabled = true; | ||||
var post = H.getTitleStrict(content); | |||||
var http = new XMLHttpRequest(); | var http = new XMLHttpRequest(); | ||||
var url = "https://write.as/api/"; | |||||
var url = "https://write.as/api/posts"; | |||||
var lang = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage); | var lang = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage); | ||||
lang = lang.substring(0, 2); | lang = lang.substring(0, 2); | ||||
var params = "w=" + encodeURIComponent(content) + "&font=" + font + "&lang=" + lang; | |||||
var params = "body=" + encodeURIComponent(post.content) + "&title=" + encodeURIComponent(post.title) + "&font=" + font + "&lang=" + lang + "&rtl=auto"; | |||||
http.open("POST", url, true); | http.open("POST", url, true); | ||||
//Send the proper header information along with the request | //Send the proper header information along with the request | ||||
@@ -24,18 +25,23 @@ function publish(content, font) { | |||||
http.onreadystatechange = function() { | http.onreadystatechange = function() { | ||||
if (http.readyState == 4) { | if (http.readyState == 4) { | ||||
$publish.classList.remove('disabled'); | $publish.classList.remove('disabled'); | ||||
$publish.value = "Publish"; | |||||
setPublishText(font, false); | |||||
$publish.disabled = false; | $publish.disabled = false; | ||||
if (http.status == 200) { | |||||
if (http.status == 201) { | |||||
$publish.style.display = 'none'; | $publish.style.display = 'none'; | ||||
data = http.responseText.split("\n"); | |||||
data = JSON.parse(http.responseText); | |||||
// Pull out data parts | // Pull out data parts | ||||
url = data[0]; | |||||
id = url.substr(url.lastIndexOf("/")+1); | |||||
editToken = data[1]; | |||||
id = data.data.id; | |||||
if (font == 'code' || font === 'mono') { | |||||
url = "https://paste.as/"+id; | |||||
} else { | |||||
url = "https://write.as/"+id; | |||||
} | |||||
editToken = data.data.token; | |||||
document.getElementById("account-tools").style.display = 'none'; | |||||
document.getElementById("publish-holder").style.display = 'none'; | document.getElementById("publish-holder").style.display = 'none'; | ||||
document.getElementById("result-holder").style.display = 'inline'; | document.getElementById("result-holder").style.display = 'inline'; | ||||
@@ -44,10 +50,12 @@ function publish(content, font) { | |||||
var $urlLink = document.getElementById("url-link"); | var $urlLink = document.getElementById("url-link"); | ||||
$urlLink.href = url; | $urlLink.href = url; | ||||
// Save the data | |||||
posts = JSON.parse(H.get('posts', '[]')); | |||||
posts.push(H.createPost(id, editToken, content)); | |||||
H.set('posts', JSON.stringify(posts)); | |||||
// Save the data if user wasn't logged in | |||||
if (typeof data.data.owner === 'undefined' || data.data.owner == "") { | |||||
posts = JSON.parse(H.get('posts', '[]')); | |||||
posts.push(H.createPost(id, editToken, post.content)); | |||||
H.set('posts', JSON.stringify(posts)); | |||||
} | |||||
} else { | } else { | ||||
alert("Failed to post. Please try again."); | alert("Failed to post. Please try again."); | ||||
} | } | ||||
@@ -56,6 +64,14 @@ function publish(content, font) { | |||||
http.send(params); | http.send(params); | ||||
} | } | ||||
function setPublishText(font, isPublishing) { | |||||
if (font === 'code' || font === 'mono') { | |||||
$publish.value = isPublishing ? 'Pasting...' : 'Paste'; | |||||
} else { | |||||
$publish.value = isPublishing ? 'Publishing...' : 'Publish'; | |||||
} | |||||
} | |||||
document.addEventListener('DOMContentLoaded', function() { | document.addEventListener('DOMContentLoaded', function() { | ||||
$content = document.getElementById("content"); | $content = document.getElementById("content"); | ||||
$publish = document.getElementById("publish"); | $publish = document.getElementById("publish"); | ||||
@@ -99,6 +115,80 @@ document.addEventListener('DOMContentLoaded', function() { | |||||
chrome.runtime.sendMessage({msg: $content.value}); | chrome.runtime.sendMessage({msg: $content.value}); | ||||
}); | }); | ||||
}); | }); | ||||
document.getElementById('sync').addEventListener('click', function(e) { | |||||
e.preventDefault(); | |||||
var posts = JSON.parse(H.get('posts', '[]')); | |||||
var p = "There "; | |||||
p += ((posts.length==1?'is ':'are ') + posts.length + " post" + (posts.length==1?'':'s')); | |||||
var thePosts = posts.length == 1 ? 'it' : 'them'; | |||||
p += " saved on this computer.\n\nSyncing "+thePosts+" to your account gives you access to "+thePosts+" from anywhere. Sync now?"; | |||||
if (!confirm(p)) { | |||||
return; | |||||
} | |||||
var $sync = this; | |||||
$sync.innerText = "Syncing now..."; | |||||
$sync.className = 'disabled'; | |||||
var http = new XMLHttpRequest(); | |||||
var params = []; | |||||
for (var i=0; i<posts.length; i++) { | |||||
params.push({id: posts[i].id, token: posts[i].token}); | |||||
} | |||||
http.open("POST", "https://write.as/api/posts/claim", true); | |||||
http.setRequestHeader("Content-type", "application/json"); | |||||
http.onreadystatechange = function() { | |||||
if (http.readyState == 4) { | |||||
$sync.innerText = 'Importing now...'; | |||||
if (http.status == 200) { | |||||
var res = JSON.parse(http.responseText); | |||||
if (res.data.length > 0) { | |||||
if (res.data.length != posts.length) { | |||||
// TODO: handle this serious situation | |||||
console.error("Request and result array length didn't match!"); | |||||
return; | |||||
} | |||||
for (var i=0; i<res.data.length; i++) { | |||||
if (res.data[i].code == 200 || res.data[i].code == 404) { | |||||
// Post successfully claimed. | |||||
for (var j=0; j<posts.length; j++) { | |||||
// Find post in local store | |||||
var id = res.data[i].id; | |||||
if (typeof res.data[i].post !== 'undefined') { | |||||
id = res.data[i].post.id; | |||||
} | |||||
if (posts[j].id == id) { | |||||
// Remove this post | |||||
posts.splice(j, 1); | |||||
break; | |||||
} | |||||
} | |||||
} else { | |||||
for (var j=0; j<posts.length; j++) { | |||||
// Find post in local store | |||||
if (posts[j].id == res.data[i].id) { | |||||
// Note the error in the local post | |||||
posts[j].error = res.data[i].error_msg; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
H.set('posts', JSON.stringify(posts)); | |||||
$sync.innerText = 'Synced.'; | |||||
} | |||||
} else { | |||||
// TODO: show error visually (option to retry) | |||||
console.error("Well that didn't work at all!"); | |||||
$sync.className = ''; | |||||
$sync.innerText = 'Sync...'; | |||||
} | |||||
} | |||||
}; | |||||
http.send(JSON.stringify(params)); | |||||
}); | |||||
} | } | ||||
// bind publish action | // bind publish action | ||||
@@ -122,13 +212,40 @@ document.addEventListener('DOMContentLoaded', function() { | |||||
// load font setting | // load font setting | ||||
H.load(fontRadios, 'font'); | H.load(fontRadios, 'font'); | ||||
$content.className = fontRadios.value; | $content.className = fontRadios.value; | ||||
setPublishText(fontRadios.value, false); | |||||
// bind font changing action | // bind font changing action | ||||
for(var i = 0; i < fontRadios.length; i++) { | for(var i = 0; i < fontRadios.length; i++) { | ||||
fontRadios[i].onclick = function() { | fontRadios[i].onclick = function() { | ||||
$content.className = this.value; | $content.className = this.value; | ||||
setPublishText(this.value, false); | |||||
H.save(fontRadios, 'font'); | H.save(fontRadios, 'font'); | ||||
}; | }; | ||||
} | } | ||||
var handleRegUser = function() { | |||||
var http = new XMLHttpRequest(); | |||||
http.open("GET", "https://write.as/api/me/", true); | |||||
http.onreadystatechange = function() { | |||||
if (http.readyState == 4) { | |||||
data = JSON.parse(http.responseText); | |||||
data = data.data; | |||||
if (typeof data.username !== 'undefined' && data.username != "") { | |||||
var $accTools = document.getElementById("account-tools") | |||||
$accTools.style.display = 'block'; | |||||
var posts = JSON.parse(H.get('posts', '[]')); | |||||
if (posts.length > 0) { | |||||
document.getElementById('sync').style.display = 'inline'; | |||||
} else { | |||||
document.getElementById('sync').style.display = 'none'; | |||||
} | |||||
//document.getElementById("sync-count").innerText = posts.length + " post" + (posts.length==1?'':'s'); | |||||
document.getElementById("username").innerText = data.username; | |||||
} | |||||
} | |||||
} | |||||
http.send(); | |||||
} | |||||
handleRegUser(); | |||||
if (H.get('updatedPostsMeta', '') == '') { | if (H.get('updatedPostsMeta', '') == '') { | ||||
// Add metadata used by Pad to all saved posts | // Add metadata used by Pad to all saved posts | ||||