@ -0,0 +1,144 @@ |
||||
package writefreely |
||||
|
||||
import ( |
||||
"github.com/gorilla/mux" |
||||
"github.com/writeas/impart" |
||||
"github.com/writeas/web-core/log" |
||||
"github.com/writeas/writefreely/page" |
||||
"net/http" |
||||
"strings" |
||||
) |
||||
|
||||
func handleViewPad(app *app, w http.ResponseWriter, r *http.Request) error { |
||||
vars := mux.Vars(r) |
||||
action := vars["action"] |
||||
slug := vars["slug"] |
||||
collAlias := vars["collection"] |
||||
appData := &struct { |
||||
page.StaticPage |
||||
Post *RawPost |
||||
User *User |
||||
Blogs *[]Collection |
||||
|
||||
Editing bool // True if we're modifying an existing post
|
||||
EditCollection *Collection // Collection of the post we're editing, if any
|
||||
}{ |
||||
StaticPage: pageForReq(app, r), |
||||
Post: &RawPost{Font: "norm"}, |
||||
User: getUserSession(app, r), |
||||
} |
||||
var err error |
||||
if appData.User != nil { |
||||
appData.Blogs, err = app.db.GetPublishableCollections(appData.User) |
||||
if err != nil { |
||||
log.Error("Unable to get user's blogs for Pad: %v", err) |
||||
} |
||||
} |
||||
|
||||
padTmpl := "pad" |
||||
|
||||
if action == "" && slug == "" { |
||||
// Not editing any post; simply render the Pad
|
||||
if err = templates[padTmpl].ExecuteTemplate(w, "pad", appData); err != nil { |
||||
log.Error("Unable to execute template: %v", err) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Retrieve post information for editing
|
||||
appData.Editing = true |
||||
// Make sure this isn't cached, so user doesn't accidentally lose data
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") |
||||
w.Header().Set("Expires", "Thu, 04 Oct 1990 20:00:00 GMT") |
||||
if slug != "" { |
||||
appData.Post = getRawCollectionPost(app, slug, collAlias) |
||||
if appData.Post.OwnerID != appData.User.ID { |
||||
// TODO: add ErrForbiddenEditPost message to flashes
|
||||
return impart.HTTPError{http.StatusFound, r.URL.Path[:strings.LastIndex(r.URL.Path, "/edit")]} |
||||
} |
||||
appData.EditCollection, err = app.db.GetCollectionForPad(collAlias) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} else { |
||||
// Editing a floating article
|
||||
appData.Post = getRawPost(app, action) |
||||
appData.Post.Id = action |
||||
} |
||||
|
||||
if appData.Post.Gone { |
||||
return ErrPostUnpublished |
||||
} else if appData.Post.Found && appData.Post.Content != "" { |
||||
// Got the post
|
||||
} else if appData.Post.Found { |
||||
return ErrPostFetchError |
||||
} else { |
||||
return ErrPostNotFound |
||||
} |
||||
|
||||
if err = templates[padTmpl].ExecuteTemplate(w, "pad", appData); err != nil { |
||||
log.Error("Unable to execute template: %v", err) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func handleViewMeta(app *app, w http.ResponseWriter, r *http.Request) error { |
||||
vars := mux.Vars(r) |
||||
action := vars["action"] |
||||
slug := vars["slug"] |
||||
collAlias := vars["collection"] |
||||
appData := &struct { |
||||
page.StaticPage |
||||
Post *RawPost |
||||
User *User |
||||
EditCollection *Collection // Collection of the post we're editing, if any
|
||||
Flashes []string |
||||
NeedsToken bool |
||||
}{ |
||||
StaticPage: pageForReq(app, r), |
||||
Post: &RawPost{Font: "norm"}, |
||||
User: getUserSession(app, r), |
||||
} |
||||
var err error |
||||
|
||||
if action == "" && slug == "" { |
||||
return ErrPostNotFound |
||||
} |
||||
|
||||
// Make sure this isn't cached, so user doesn't accidentally lose data
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") |
||||
w.Header().Set("Expires", "Thu, 28 Jul 1989 12:00:00 GMT") |
||||
if slug != "" { |
||||
appData.Post = getRawCollectionPost(app, slug, collAlias) |
||||
if appData.Post.OwnerID != appData.User.ID { |
||||
// TODO: add ErrForbiddenEditPost message to flashes
|
||||
return impart.HTTPError{http.StatusFound, r.URL.Path[:strings.LastIndex(r.URL.Path, "/meta")]} |
||||
} |
||||
appData.EditCollection, err = app.db.GetCollectionForPad(collAlias) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} else { |
||||
// Editing a floating article
|
||||
appData.Post = getRawPost(app, action) |
||||
appData.Post.Id = action |
||||
} |
||||
appData.NeedsToken = appData.User == nil || appData.User.ID != appData.Post.OwnerID |
||||
|
||||
if appData.Post.Gone { |
||||
return ErrPostUnpublished |
||||
} else if appData.Post.Found && appData.Post.Content != "" { |
||||
// Got the post
|
||||
} else if appData.Post.Found { |
||||
return ErrPostFetchError |
||||
} else { |
||||
return ErrPostNotFound |
||||
} |
||||
appData.Flashes, _ = getSessionFlashes(app, w, r, nil) |
||||
|
||||
if err = templates["edit-meta"].ExecuteTemplate(w, "edit-meta", appData); err != nil { |
||||
log.Error("Unable to execute template: %v", err) |
||||
} |
||||
return nil |
||||
} |
After Width: | Height: | Size: 199 B |
After Width: | Height: | Size: 195 B |
After Width: | Height: | Size: 485 B |
After Width: | Height: | Size: 439 B |
After Width: | Height: | Size: 168 B |
After Width: | Height: | Size: 160 B |
After Width: | Height: | Size: 239 B |
After Width: | Height: | Size: 222 B |
After Width: | Height: | Size: 105 B |
After Width: | Height: | Size: 110 B |
After Width: | Height: | Size: 655 B |
After Width: | Height: | Size: 640 B |
After Width: | Height: | Size: 94 B |
After Width: | Height: | Size: 106 B |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 432 B |
@ -0,0 +1,257 @@ |
||||
/** |
||||
* H.js |
||||
*
|
||||
* Lightweight, extremely bare-bones library for manipulating the DOM and |
||||
* saving some typing. |
||||
*/ |
||||
|
||||
var Element = function(domElement) { |
||||
this.el = domElement; |
||||
}; |
||||
|
||||
/** |
||||
* Creates a toggle button that adds / removes the given class name from the |
||||
* given element. |
||||
* |
||||
* @param {Element} $el - The element to modify. |
||||
* @param {string} onClass - The class to add to the given element. |
||||
* @param {function} onFunc - Additional actions when toggling on. |
||||
* @param {function} offFunc - Additional actions when toggling off. |
||||
*/ |
||||
Element.prototype.createToggle = function($el, onClass, onFunc, offFunc) { |
||||
this.on('click', function(e) { |
||||
if ($el.el.className === '') { |
||||
$el.el.className = onClass; |
||||
onFunc(new Element(this), e); |
||||
} else { |
||||
$el.el.className = ''; |
||||
offFunc(new Element(this), e); |
||||
} |
||||
e.preventDefault(); |
||||
}, false); |
||||
}; |
||||
Element.prototype.on = function(event, func) { |
||||
events = event.split(' '); |
||||
var el = this.el; |
||||
if (el == null) { |
||||
console.error("Error: element for event is null"); |
||||
return; |
||||
} |
||||
var addEvent = function(e) { |
||||
if (el.addEventListener) { |
||||
el.addEventListener(e, func, false); |
||||
} else if (el.attachEvent) { |
||||
el.attachEvent(e, func); |
||||
} |
||||
}; |
||||
if (events.length === 1) { |
||||
addEvent(event); |
||||
} else { |
||||
for(var i=0; i<events.length; i++) { |
||||
addEvent(events[i]); |
||||
} |
||||
} |
||||
}; |
||||
Element.prototype.setClass = function(className) { |
||||
if (this.el == null) { |
||||
console.error("Error: element to set class on is null"); |
||||
return; |
||||
} |
||||
this.el.className = className; |
||||
}; |
||||
Element.prototype.removeClass = function(className) { |
||||
if (this.el == null) { |
||||
console.error("Error: element to remove class on is null"); |
||||
return; |
||||
} |
||||
var regex = new RegExp(' ?' + className, 'g'); |
||||
this.el.className = this.el.className.replace(regex, ''); |
||||
}; |
||||
Element.prototype.text = function(text, className) { |
||||
if (this.el == null) { |
||||
console.error("Error: element for setting text is null"); |
||||
return; |
||||
} |
||||
if (this.el.textContent !== text) { |
||||
this.el.textContent = text; |
||||
if (typeof className !== 'undefined') { |
||||
this.el.className = this.el.className + ' ' + className; |
||||
} |
||||
} |
||||
}; |
||||
Element.prototype.insertAfter = function(newNode) { |
||||
if (this.el == null) { |
||||
console.error("Error: element for insertAfter is null"); |
||||
return; |
||||
} |
||||
this.el.parentNode.insertBefore(newNode, this.el.nextSibling); |
||||
}; |
||||
Element.prototype.remove = function() { |
||||
if (this.el == null) { |
||||
console.error("Didn't remove element"); |
||||
return; |
||||
} |
||||
this.el.parentNode.removeChild(this.el); |
||||
}; |
||||
Element.prototype.hide = function() { |
||||
if (this.el == null) { |
||||
console.error("Didn't hide element"); |
||||
return; |
||||
} |
||||
this.el.className += ' effect fade-out'; |
||||
}; |
||||
Element.prototype.show = function() { |
||||
if (this.el == null) { |
||||
console.error("Didn't show element"); |
||||
return; |
||||
} |
||||
this.el.className += ' effect'; |
||||
}; |
||||
|
||||
|
||||
var H = { |
||||
getEl: function(elementId) { |
||||
return new Element(document.getElementById(elementId)); |
||||
}, |
||||
save: function($el, key) { |
||||
localStorage.setItem(key, $el.el.value); |
||||
}, |
||||
load: function($el, key, onlyLoadPopulated) { |
||||
var val = localStorage.getItem(key); |
||||
if (onlyLoadPopulated && val == null) { |
||||
// Do nothing
|
||||
return; |
||||
} |
||||
$el.el.value = val; |
||||
}, |
||||
set: function(key, value) { |
||||
localStorage.setItem(key, value); |
||||
}, |
||||
get: function(key, defaultValue) { |
||||
var val = localStorage.getItem(key); |
||||
if (val == null) { |
||||
val = defaultValue; |
||||
} |
||||
return val; |
||||
}, |
||||
remove: function(key) { |
||||
localStorage.removeItem(key); |
||||
}, |
||||
exists: function(key) { |
||||
return localStorage.getItem(key) !== null; |
||||
}, |
||||
createPost: function(id, editToken, content, created) { |
||||
var summaryLen = 200; |
||||
var titleLen = 80; |
||||
var getPostMeta = function(content) { |
||||
var eol = content.indexOf("\n"); |
||||
if (content.indexOf("# ") === 0) { |
||||
// Title is in the format:
|
||||
//
|
||||
// # Some title
|
||||
var summary = content.substring(eol).trim(); |
||||
if (summary.length > summaryLen) { |
||||
summary = summary.substring(0, summaryLen) + "..."; |
||||
} |
||||
return { |
||||
title: content.substring("# ".length, eol), |
||||
summary: summary, |
||||
}; |
||||
} |
||||
|
||||
var blankLine = content.indexOf("\n\n"); |
||||
if (blankLine !== -1 && blankLine <= eol && blankLine <= titleLen) { |
||||
// Title is in the format:
|
||||
//
|
||||
// Some title
|
||||
//
|
||||
// The body starts after that blank line above it.
|
||||
var summary = content.substring(blankLine).trim(); |
||||
if (summary.length > summaryLen) { |
||||
summary = summary.substring(0, summaryLen) + "..."; |
||||
} |
||||
return { |
||||
title: content.substring(0, blankLine), |
||||
summary: summary, |
||||
}; |
||||
} |
||||
|
||||
// TODO: move this to the beginning
|
||||
var title = content.trim(); |
||||
var summary = ""; |
||||
if (title.length > titleLen) { |
||||
// Content can't fit in the title, so figure out the summary
|
||||
summary = title; |
||||
title = ""; |
||||
if (summary.length > summaryLen) { |
||||
summary = summary.substring(0, summaryLen) + "..."; |
||||
} |
||||
} else if (eol > 0) { |
||||
summary = title.substring(eol+1); |
||||
title = title.substring(0, eol); |
||||
} |
||||
return { |
||||
title: title, |
||||
summary: summary |
||||
}; |
||||
}; |
||||
|
||||
var post = getPostMeta(content); |
||||
post.id = id; |
||||
post.token = editToken; |
||||
post.created = created ? new Date(created) : new Date(); |
||||
post.client = "Pad"; |
||||
|
||||
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 |
||||
}; |
||||
}, |
||||
}; |
||||
|
||||
var He = { |
||||
create: function(name) { |
||||
return document.createElement(name); |
||||
}, |
||||
get: function(id) { |
||||
return document.getElementById(id); |
||||
}, |
||||
$: function(selector) { |
||||
var els = document.querySelectorAll(selector); |
||||
return els; |
||||
}, |
||||
postJSON: function(url, params, callback) { |
||||
var http = new XMLHttpRequest(); |
||||
|
||||
http.open("POST", url, true); |
||||
|
||||
// Send the proper header information along with the request
|
||||
http.setRequestHeader("Content-type", "application/json"); |
||||
|
||||
http.onreadystatechange = function() { |
||||
if (http.readyState == 4) { |
||||
callback(http.status, JSON.parse(http.responseText)); |
||||
} |
||||
} |
||||
http.send(JSON.stringify(params)); |
||||
}, |
||||
}; |
||||
|
||||
String.prototype.leftTrim = function() { |
||||
return this.replace(/^\s+/,""); |
||||
}; |
@ -0,0 +1,358 @@ |
||||
{{define "pad"}}<!DOCTYPE HTML> |
||||
<html> |
||||
<head> |
||||
|
||||
<title>{{if .Editing}}Editing {{if .Post.Title}}{{.Post.Title}}{{else}}{{.Post.Id}}{{end}}{{else}}New Post{{end}} — {{.SiteName}}</title> |
||||
|
||||
<link rel="stylesheet" type="text/css" href="/css/write.css" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
|
||||
<meta name="google" value="notranslate"> |
||||
</head> |
||||
<body id="pad" class="light"> |
||||
|
||||
<div id="overlay"></div> |
||||
|
||||
<textarea id="writer" placeholder="Write..." class="{{.Post.Font}}" autofocus>{{if .Post.Title}}# {{.Post.Title}} |
||||
|
||||
{{end}}{{.Post.Content}}</textarea> |
||||
|
||||
<header id="tools"> |
||||
<div id="clip"> |
||||
<h1>{{if .User}}<a href="/me/c/" title="View blogs"><img class="ic-24dp" src="/img/ic_blogs_dark@2x.png" /></a>{{else}}<a href="/">w<span class="if-room">rite.as</span></a>{{end}} |
||||
</h1> |
||||
<nav id="target" class=""><ul> |
||||
{{if .Editing}}<li>{{if .EditCollection}}<a href="{{.EditCollection.CanonicalURL}}">{{.EditCollection.Title}}</a>{{else}}<a>Draft</a>{{end}}</li> |
||||
{{else}}<li><a id="publish-to"><span id="target-name">Draft</span> <img class="ic-18dp" src="/img/ic_down_arrow_dark@2x.png" /></a> |
||||
<ul> |
||||
<li class="menu-heading">Publish to...</li> |
||||
<li class="target selected" id="anonymous"><a href="#anonymous"><i class="material-icons md-18">description</i> <em>Draft</em></a></li> |
||||
{{if .Blogs}}{{range .Blogs}} |
||||
<li class="target" id="blog-{{.Alias}}"><a href="#{{.Alias}}"><i class="material-icons md-18">public</i> {{if .Title}}{{.Title}}{{else}}{{.Alias}}{{end}}</a></li> |
||||
{{end}}{{end}} |
||||
<li id="user-separator" class="separator"><hr /></li> |
||||
<li><a href="/me/c/"><i class="material-icons md-18">library_books</i> View Blogs</a></li> |
||||
<li><a href="/me/posts/"><i class="material-icons md-18">view_list</i> View Drafts</a></li> |
||||
<li><a href="/me/logout"><i class="material-icons md-18">power_settings_new</i> Log out</a></li> |
||||
</ul> |
||||
</li>{{end}} |
||||
</ul></nav> |
||||
<nav id="font-picker" class="if-room room-3 hidden" style="margin-left:-1em"><ul> |
||||
<li><a href="#" id="" onclick="return false"><img class="ic-24dp" src="/img/ic_font_dark@2x.png" /> <img class="ic-18dp" src="/img/ic_down_arrow_dark@2x.png" /></a> |
||||
<ul style="text-align: center"> |
||||
<li class="menu-heading">Font</li> |
||||
<li class="selected"><a class="font norm" href="#norm">Serif</a></li> |
||||
<li><a class="font sans" href="#sans">Sans-serif</a></li> |
||||
<li><a class="font wrap" href="#wrap">Monospace</a></li> |
||||
</ul> |
||||
</li> |
||||
</ul></nav> |
||||
<span id="wc" class="hidden if-room room-4">0 words</span> |
||||
</div> |
||||
<noscript style="margin-left: 2em;"><strong>NOTE</strong>: for now, you'll need Javascript enabled to post.</noscript> |
||||
<div id="belt"> |
||||
{{if .Editing}}<div class="tool hidden if-room"><a href="{{if .EditCollection}}{{.EditCollection.CanonicalURL}}{{.Post.Slug}}/edit/meta{{else}}/{{.Post.Id}}/meta{{end}}" title="Edit post metadata" id="edit-meta"><img class="ic-24dp" src="/img/ic_info_dark@2x.png" /></a></div>{{end}} |
||||
<div class="tool hidden if-room room-2"><a href="#theme" title="Toggle theme" id="toggle-theme"><img class="ic-24dp" src="/img/ic_brightness_dark@2x.png" /></a></div> |
||||
<div class="tool if-room room-1"><a href="{{if not .User}}/pad/posts{{else}}/me/posts/{{end}}" title="View posts" id="view-posts"><img class="ic-24dp" src="/img/ic_list_dark@2x.png" /></a></div> |
||||
<div class="tool"><a href="#publish" title="Publish" id="publish"><img class="ic-24dp" src="/img/ic_send_dark@2x.png" /></a></div> |
||||
</div> |
||||
</header> |
||||
|
||||
<script src="/js/h.js"></script> |
||||
<script> |
||||
function toggleTheme() { |
||||
var btns = Array.prototype.slice.call(document.getElementById('tools').querySelectorAll('a img')); |
||||
var newTheme = ''; |
||||
if (document.body.classList.contains('light')) { |
||||
newTheme = 'dark'; |
||||
document.body.className = document.body.className.replace(/(?:^|\s)light(?!\S)/g, newTheme); |
||||
for (var i=0; i<btns.length; i++) { |
||||
btns[i].src = btns[i].src.replace('_dark@2x.png', '@2x.png'); |
||||
} |
||||
} else { |
||||
newTheme = 'light'; |
||||
document.body.className = document.body.className.replace(/(?:^|\s)dark(?!\S)/g, newTheme); |
||||
for (var i=0; i<btns.length; i++) { |
||||
btns[i].src = btns[i].src.replace('@2x.png', '_dark@2x.png'); |
||||
} |
||||
} |
||||
H.set('padTheme', newTheme); |
||||
} |
||||
if (H.get('padTheme', 'light') != 'light') { |
||||
toggleTheme(); |
||||
} |
||||
var $writer = H.getEl('writer'); |
||||
var $btnPublish = H.getEl('publish'); |
||||
var $wc = H.getEl("wc"); |
||||
var updateWordCount = function() { |
||||
var words = 0; |
||||
var val = $writer.el.value.trim(); |
||||
if (val != '') { |
||||
words = $writer.el.value.trim().replace(/\s+/gi, ' ').split(' ').length; |
||||
} |
||||
$wc.el.innerText = words + " word" + (words != 1 ? "s" : ""); |
||||
}; |
||||
var setButtonStates = function() { |
||||
if (!canPublish) { |
||||
$btnPublish.el.className = 'disabled'; |
||||
return; |
||||
} |
||||
if ($writer.el.value.length === 0 || (draftDoc != 'lastDoc' && $writer.el.value == origDoc)) { |
||||
$btnPublish.el.className = 'disabled'; |
||||
} else { |
||||
$btnPublish.el.className = ''; |
||||
} |
||||
}; |
||||
{{if .Post.Id}}var draftDoc = 'draft{{.Post.Id}}'; |
||||
var origDoc = '{{.Post.Content}}';{{else}}var draftDoc = 'lastDoc';{{end}} |
||||
H.load($writer, draftDoc, true); |
||||
updateWordCount(); |
||||
|
||||
var typingTimer; |
||||
var doneTypingInterval = 200; |
||||
|
||||
var posts; |
||||
{{if and .Post.Id (not .Post.Slug)}} |
||||
var token = null; |
||||
var curPostIdx; |
||||
posts = JSON.parse(H.get('posts', '[]')); |
||||
for (var i=0; i<posts.length; i++) { |
||||
if (posts[i].id == "{{.Post.Id}}") { |
||||
token = posts[i].token; |
||||
break; |
||||
} |
||||
} |
||||
var canPublish = token != null; |
||||
{{else}}var canPublish = true;{{end}} |
||||
var publishing = false; |
||||
var justPublished = false; |
||||
|
||||
var publish = function(content, font) { |
||||
{{if and (and .Post.Id (not .Post.Slug)) (not .User)}} |
||||
if (!token) { |
||||
alert("You don't have permission to update this post."); |
||||
return; |
||||
} |
||||
if ($btnPublish.el.className == 'disabled') { |
||||
return; |
||||
} |
||||
{{end}} |
||||
$btnPublish.el.children[0].textContent = 'more_horiz'; |
||||
publishing = true; |
||||
var xpostTarg = H.get('crosspostTarget', '[]'); |
||||
|
||||
var http = new XMLHttpRequest(); |
||||
var lang = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage); |
||||
lang = lang.substring(0, 2); |
||||
var post = H.getTitleStrict(content); |
||||
|
||||
var params = { |
||||
body: post.content, |
||||
title: post.title, |
||||
font: font, |
||||
lang: lang |
||||
}; |
||||
{{ if .Post.Slug }} |
||||
var url = "/api/collections/{{.EditCollection.Alias}}/posts/{{.Post.Id}}"; |
||||
{{ else if .Post.Id }} |
||||
var url = "/api/posts/{{.Post.Id}}"; |
||||
if (typeof token === 'undefined' || !token) { |
||||
token = ""; |
||||
} |
||||
params.token = token; |
||||
{{ else }} |
||||
var url = "/api/posts"; |
||||
var postTarget = H.get('postTarget', 'anonymous'); |
||||
if (postTarget != 'anonymous') { |
||||
url = "/api/collections/" + postTarget + "/posts"; |
||||
} |
||||
params.crosspost = JSON.parse(xpostTarg); |
||||
{{ end }} |
||||
|
||||
http.open("POST", url, true); |
||||
|
||||
// Send the proper header information along with the request |
||||
http.setRequestHeader("Content-type", "application/json"); |
||||
|
||||
http.onreadystatechange = function() { |
||||
if (http.readyState == 4) { |
||||
publishing = false; |
||||
if (http.status == 200 || http.status == 201) { |
||||
data = JSON.parse(http.responseText); |
||||
id = data.data.id; |
||||
nextURL = '/'+id; |
||||
|
||||
{{ if not .Post.Id }} |
||||
// Post created |
||||
if (postTarget != 'anonymous') { |
||||
nextURL = '/'+postTarget+'/'+data.data.slug; |
||||
} |
||||
editToken = data.data.token; |
||||
|
||||
{{ if not .User }}if (postTarget == 'anonymous') { |
||||
// Save the data |
||||
var posts = JSON.parse(H.get('posts', '[]')); |
||||
|
||||
{{if .Post.Id}}var newPost = H.createPost("{{.Post.Id}}", token, content); |
||||
for (var i=0; i<posts.length; i++) { |
||||
if (posts[i].id == "{{.Post.Id}}") { |
||||
posts[i].title = newPost.title; |
||||
posts[i].summary = newPost.summary; |
||||
break; |
||||
} |
||||
} |
||||
nextURL = "/pad/posts";{{else}}posts.push(H.createPost(id, editToken, content));{{end}} |
||||
|
||||
H.set('posts', JSON.stringify(posts)); |
||||
} |
||||
{{ end }} |
||||
{{ end }} |
||||
|
||||
justPublished = true; |
||||
if (draftDoc != 'lastDoc') { |
||||
H.remove(draftDoc); |
||||
{{if .Editing}}H.remove('draft{{.Post.Id}}font');{{end}} |
||||
} else { |
||||
H.set(draftDoc, ''); |
||||
} |
||||
|
||||
{{if .EditCollection}} |
||||
window.location = '{{.EditCollection.CanonicalURL}}{{.Post.Slug}}'; |
||||
{{else}} |
||||
window.location = nextURL; |
||||
{{end}} |
||||
} else { |
||||
$btnPublish.el.children[0].textContent = 'send'; |
||||
alert("Failed to post. Please try again."); |
||||
} |
||||
} |
||||
} |
||||
http.send(JSON.stringify(params)); |
||||
}; |
||||
|
||||
setButtonStates(); |
||||
$writer.on('keyup input', function() { |
||||
setButtonStates(); |
||||
clearTimeout(typingTimer); |
||||
typingTimer = setTimeout(doneTyping, doneTypingInterval); |
||||
}, false); |
||||
$writer.on('keydown', function(e) { |
||||
clearTimeout(typingTimer); |
||||
if (e.keyCode == 13 && (e.metaKey || e.ctrlKey)) { |
||||
$btnPublish.el.click(); |
||||
} |
||||
}); |
||||
$btnPublish.on('click', function(e) { |
||||
e.preventDefault(); |
||||
if (!publishing && $writer.el.value) { |
||||
var content = $writer.el.value; |
||||
publish(content, selectedFont); |
||||
} |
||||
}); |
||||
|
||||
H.getEl('toggle-theme').on('click', function(e) { |
||||
e.preventDefault(); |
||||
var newTheme = 'light'; |
||||
if (document.body.className == 'light') { |
||||
newTheme = 'dark'; |
||||
} |
||||
toggleTheme(); |
||||
}); |
||||
|
||||
var targets = document.querySelectorAll('#target li.target a'); |
||||
for (var i=0; i<targets.length; i++) { |
||||
targets[i].addEventListener('click', function(e) { |
||||
e.preventDefault(); |
||||
var targetName = this.href.substring(this.href.indexOf('#')+1); |
||||
H.set('postTarget', targetName); |
||||
|
||||
document.querySelector('#target li.target.selected').classList.remove('selected'); |
||||
this.parentElement.classList.add('selected'); |
||||
var newText = this.innerText.split(' '); |
||||
newText.shift(); |
||||
document.getElementById('target-name').innerText = newText.join(' '); |
||||
}); |
||||
} |
||||
var postTarget = H.get('postTarget', 'anonymous'); |
||||
if (location.hash != '') { |
||||
postTarget = location.hash.substring(1); |
||||
// TODO: pushState to /pad (or whatever the URL is) so we live on a clean URL |
||||
location.hash = ''; |
||||
} |
||||
var pte = document.querySelector('#target li.target#blog-'+postTarget+' a'); |
||||
if (pte != null) { |
||||
pte.click(); |
||||
} else { |
||||
postTarget = 'anonymous'; |
||||
H.set('postTarget', postTarget); |
||||
} |
||||
|
||||
var sansLoaded = false; |
||||
WebFontConfig = { |
||||
custom: { families: [ 'Lora:400,700:latin' ], urls: [ '/css/fonts.css' ] } |
||||
}; |
||||
var loadSans = function() { |
||||
if (sansLoaded) return; |
||||
sansLoaded = true; |
||||
WebFontConfig.custom.families.push('Open+Sans:400,700:latin'); |
||||
try { |
||||
(function() { |
||||
var wf=document.createElement('script'); |
||||
wf.src = '/js/webfont.js'; |
||||
wf.type='text/javascript'; |
||||
wf.async='true'; |
||||
var s=document.getElementsByTagName('script')[0]; |
||||
s.parentNode.insertBefore(wf, s); |
||||
})(); |
||||
} catch (e) {} |
||||
}; |
||||
var fonts = document.querySelectorAll('nav#font-picker a.font'); |
||||
for (var i=0; i<fonts.length; i++) { |
||||
fonts[i].addEventListener('click', function(e) { |
||||
e.preventDefault(); |
||||
selectedFont = this.href.substring(this.href.indexOf('#')+1); |
||||
$writer.el.className = selectedFont; |
||||
document.querySelector('nav#font-picker li.selected').classList.remove('selected'); |
||||
this.parentElement.classList.add('selected'); |
||||
H.set('{{if .Editing}}draft{{.Post.Id}}font{{else}}padFont{{end}}', selectedFont); |
||||
if (selectedFont == 'sans') { |
||||
loadSans(); |
||||
} |
||||
}); |
||||
} |
||||
var selectedFont = H.get('{{if .Editing}}draft{{.Post.Id}}font{{else}}padFont{{end}}', '{{.Post.Font}}'); |
||||
var sfe = document.querySelector('nav#font-picker a.font.'+selectedFont); |
||||
if (sfe != null) { |
||||
sfe.click(); |
||||
} |
||||
|
||||
var doneTyping = function() { |
||||
if (draftDoc == 'lastDoc' || $writer.el.value != origDoc) { |
||||
H.save($writer, draftDoc); |
||||
updateWordCount(); |
||||
} |
||||
}; |
||||
window.addEventListener('beforeunload', function(e) { |
||||
if (draftDoc != 'lastDoc' && $writer.el.value == origDoc) { |
||||
H.remove(draftDoc); |
||||
} else if (!justPublished) { |
||||
doneTyping(); |
||||
} |
||||
}); |
||||
|
||||
try { |
||||
(function() { |
||||
var wf=document.createElement('script'); |
||||
wf.src = '/js/webfont.js'; |
||||
wf.type='text/javascript'; |
||||
wf.async='true'; |
||||
var s=document.getElementsByTagName('script')[0]; |
||||
s.parentNode.insertBefore(wf, s); |
||||
})(); |
||||
} catch (e) { |
||||
// whatevs |
||||
} |
||||
</script> |
||||
<link href="/css/icons.css" rel="stylesheet"> |
||||
</body> |
||||
</html>{{end}} |