mirror of
https://github.com/TryGhost/Ghost.git
synced 2023-12-13 21:00:40 +01:00
commit
12496918de
22 changed files with 971 additions and 556 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -23,6 +23,7 @@ projectFilesBackup
|
||||||
# Ghost DB file
|
# Ghost DB file
|
||||||
*.db
|
*.db
|
||||||
|
|
||||||
|
/core/admin/assets/tmpl/hbs-tmpl.js
|
||||||
/core/admin/assets/css
|
/core/admin/assets/css
|
||||||
/core/admin/assets/sass/modules/bourbon
|
/core/admin/assets/sass/modules/bourbon
|
||||||
.sass-cache/
|
.sass-cache/
|
||||||
|
|
39
Gruntfile.js
39
Gruntfile.js
|
@ -49,16 +49,50 @@
|
||||||
bourbon: {
|
bourbon: {
|
||||||
command: 'bourbon install --path core/admin/assets/sass/modules/'
|
command: 'bourbon install --path core/admin/assets/sass/modules/'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handlebars: {
|
||||||
|
|
||||||
|
core: {
|
||||||
|
|
||||||
|
options: {
|
||||||
|
|
||||||
|
namespace: "JST",
|
||||||
|
|
||||||
|
processName: function (filename) {
|
||||||
|
filename = filename.replace('./core/admin/assets/tmpl/', '');
|
||||||
|
return filename.replace('.hbs', '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
files: {
|
||||||
|
"./core/admin/assets/tmpl/hbs-tmpl.js": "./core/admin/assets/tmpl/**/*.hbs"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
handlebars: {
|
||||||
|
files: './core/admin/assets/tmpl/**/*.hbs',
|
||||||
|
tasks: ['handlebars']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
grunt.initConfig(cfg);
|
grunt.initConfig(cfg);
|
||||||
|
|
||||||
grunt.loadNpmTasks("grunt-jslint");
|
grunt.loadNpmTasks("grunt-jslint");
|
||||||
grunt.loadNpmTasks("grunt-mocha-test");
|
grunt.loadNpmTasks("grunt-mocha-test");
|
||||||
grunt.loadNpmTasks("grunt-contrib-sass");
|
|
||||||
grunt.loadNpmTasks("grunt-shell");
|
grunt.loadNpmTasks("grunt-shell");
|
||||||
|
|
||||||
|
grunt.loadNpmTasks("grunt-contrib-watch");
|
||||||
|
grunt.loadNpmTasks("grunt-contrib-sass");
|
||||||
|
grunt.loadNpmTasks("grunt-contrib-handlebars");
|
||||||
|
|
||||||
|
|
||||||
// Prepare the project for development
|
// Prepare the project for development
|
||||||
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
|
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
|
||||||
grunt.registerTask("init", ["shell:bourbon", "sass:admin"]);
|
grunt.registerTask("init", ["shell:bourbon", "sass:admin"]);
|
||||||
|
@ -68,6 +102,9 @@
|
||||||
|
|
||||||
// Run tests and lint code
|
// Run tests and lint code
|
||||||
grunt.registerTask("validate", ["jslint", "mochaTest:all"]);
|
grunt.registerTask("validate", ["jslint", "mochaTest:all"]);
|
||||||
|
|
||||||
|
// When you just say "grunt"
|
||||||
|
grunt.registerTask("default", ['sass:admin', 'handlebars', 'watch']);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = configureGrunt;
|
module.exports = configureGrunt;
|
||||||
|
|
|
@ -48,10 +48,5 @@
|
||||||
$(this).next("ul").fadeToggle(200);
|
$(this).next("ul").fadeToggle(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.editor-options').on('click', 'li', function (e) {
|
|
||||||
$('.button-save').data("state", $(this).data("title")).attr('data-state', $(this).data("title")).text($(this).text());
|
|
||||||
$('.editor-options .active').removeClass('active');
|
|
||||||
$(this).addClass('active');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}(jQuery));
|
}(jQuery));
|
|
@ -1,242 +0,0 @@
|
||||||
// # Article Editor
|
|
||||||
|
|
||||||
/*global window, document, history, jQuery, Showdown, CodeMirror, shortcut, Countable */
|
|
||||||
(function ($, ShowDown, CodeMirror, shortcut, Countable) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var wordCount = $('.entry-word-count'),
|
|
||||||
charCount = $('.entry-character-count'),
|
|
||||||
paragraphCount = $('.entry-paragraph-count'),
|
|
||||||
|
|
||||||
// ## Converter Initialisation
|
|
||||||
/**
|
|
||||||
* @property converter
|
|
||||||
* @type {ShowDown.converter}
|
|
||||||
*/
|
|
||||||
// Initialise the Showdown converter for Markdown.
|
|
||||||
// var delay;
|
|
||||||
converter = new ShowDown.converter({extensions: ['ghostdown']}),
|
|
||||||
editor = CodeMirror.fromTextArea(document.getElementById('entry-markdown'), {
|
|
||||||
mode: 'markdown',
|
|
||||||
tabMode: 'indent',
|
|
||||||
lineWrapping: true
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// ## Functions
|
|
||||||
/**
|
|
||||||
* @method Update word count
|
|
||||||
* @todo Really not the best way to do things as it includes Markdown formatting along with words
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
// This updates the word count on the editor preview panel.
|
|
||||||
// function updateWordCount() {
|
|
||||||
// var wordCount = document.getElementsByClassName('entry-word-count')[0],
|
|
||||||
// editorValue = editor.getValue();
|
|
||||||
|
|
||||||
// if (editorValue.length) {
|
|
||||||
// wordCount.innerHTML = editorValue.match(/\S+/g).length + ' words';
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @method updatePreview
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
// This updates the editor preview panel.
|
|
||||||
// Currently gets called on every key press.
|
|
||||||
// Also trigger word count update
|
|
||||||
function updatePreview() {
|
|
||||||
var preview = document.getElementsByClassName('rendered-markdown')[0];
|
|
||||||
preview.innerHTML = converter.makeHtml(editor.getValue());
|
|
||||||
console.log(preview);
|
|
||||||
Countable.once(preview, function (counter) {
|
|
||||||
// updateWordCount(counter);
|
|
||||||
wordCount.text(counter.words + ' words');
|
|
||||||
charCount.text(counter.characters + ' characters');
|
|
||||||
paragraphCount.text(counter.paragraphs + ' paragraphs');
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @method Save
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
// This method saves a post
|
|
||||||
function save() {
|
|
||||||
var entry = {
|
|
||||||
title: document.getElementById('entry-title').value,
|
|
||||||
content: editor.getValue()
|
|
||||||
},
|
|
||||||
urlSegments = window.location.pathname.split('/'),
|
|
||||||
id;
|
|
||||||
|
|
||||||
if (urlSegments[2] === 'editor' && urlSegments[3] && /^[a-zA-Z0-9]+$/.test(urlSegments[2])) {
|
|
||||||
id = urlSegments[3];
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/v0.1/posts/' + id,
|
|
||||||
method: 'PUT',
|
|
||||||
data: entry,
|
|
||||||
success: function (data) {
|
|
||||||
console.log('response', data);
|
|
||||||
},
|
|
||||||
error: function (error) {
|
|
||||||
console.log('error', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/v0.1/posts',
|
|
||||||
method: 'POST',
|
|
||||||
data: entry,
|
|
||||||
success: function (data) {
|
|
||||||
console.log('response', data);
|
|
||||||
history.pushState(data, '', '/ghost/editor/' + data.id);
|
|
||||||
},
|
|
||||||
error: function (jqXHR, status, error) {
|
|
||||||
var errors = JSON.parse(jqXHR.responseText);
|
|
||||||
console.log('FAILED', errors);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ## Main Initialisation
|
|
||||||
$(document).ready(function () {
|
|
||||||
|
|
||||||
$('.entry-markdown header, .entry-preview header').click(function (e) {
|
|
||||||
$('.entry-markdown, .entry-preview').removeClass('active');
|
|
||||||
$(e.target).closest('section').addClass('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.on("change", function () {
|
|
||||||
//clearTimeout(delay);
|
|
||||||
//delay = setTimeout(updatePreview, 50);
|
|
||||||
updatePreview();
|
|
||||||
});
|
|
||||||
|
|
||||||
updatePreview();
|
|
||||||
|
|
||||||
$('.button-save').on('click', function () {
|
|
||||||
save();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sync scrolling
|
|
||||||
function syncScroll(e) {
|
|
||||||
// vars
|
|
||||||
var $codeViewport = $(e.target),
|
|
||||||
$previewViewport = $('.entry-preview-content'),
|
|
||||||
$codeContent = $('.CodeMirror-sizer'),
|
|
||||||
$previewContent = $('.rendered-markdown'),
|
|
||||||
|
|
||||||
// calc position
|
|
||||||
codeHeight = $codeContent.height() - $codeViewport.height(),
|
|
||||||
previewHeight = $previewContent.height() - $previewViewport.height(),
|
|
||||||
ratio = previewHeight / codeHeight,
|
|
||||||
previewPostition = $codeViewport.scrollTop() * ratio;
|
|
||||||
|
|
||||||
// apply new scroll
|
|
||||||
$previewViewport.scrollTop(previewPostition);
|
|
||||||
|
|
||||||
}
|
|
||||||
// TODO: Debounce
|
|
||||||
$('.CodeMirror-scroll').on('scroll', syncScroll);
|
|
||||||
|
|
||||||
// Shadow on Markdown if scrolled
|
|
||||||
$('.CodeMirror-scroll').on('scroll', function (e) {
|
|
||||||
if ($('.CodeMirror-scroll').scrollTop() > 10) {
|
|
||||||
$('.entry-markdown').addClass('scrolling');
|
|
||||||
} else {
|
|
||||||
$('.entry-markdown').removeClass('scrolling');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Shadow on Preview if scrolled
|
|
||||||
$('.entry-preview-content').on('scroll', function (e) {
|
|
||||||
if ($('.entry-preview-content').scrollTop() > 10) {
|
|
||||||
$('.entry-preview').addClass('scrolling');
|
|
||||||
} else {
|
|
||||||
$('.entry-preview').removeClass('scrolling');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ## Shortcuts
|
|
||||||
// Zen writing mode
|
|
||||||
shortcut.add("Alt+Shift+Z", function () {
|
|
||||||
$('body').toggleClass('zen');
|
|
||||||
});
|
|
||||||
|
|
||||||
var MarkdownShortcuts = [
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+B',
|
|
||||||
'style': 'bold'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Meta+B',
|
|
||||||
'style': 'bold'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+I',
|
|
||||||
'style': 'italic'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Meta+I',
|
|
||||||
'style': 'italic'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+Alt+U',
|
|
||||||
'style': 'strike'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+Shift+K',
|
|
||||||
'style': 'code'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Alt+1',
|
|
||||||
'style': 'h1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Alt+2',
|
|
||||||
'style': 'h2'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Alt+3',
|
|
||||||
'style': 'h3'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Alt+4',
|
|
||||||
'style': 'h4'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Alt+5',
|
|
||||||
'style': 'h5'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Alt+6',
|
|
||||||
'style': 'h6'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+Shift+L',
|
|
||||||
'style': 'link'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+Shift+I',
|
|
||||||
'style': 'image'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+Q',
|
|
||||||
'style': 'blockquote'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'key': 'Ctrl+Shift+1',
|
|
||||||
'style': 'currentdate'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
$.each(MarkdownShortcuts, function (index, short) {
|
|
||||||
shortcut.add(short.key, function () {
|
|
||||||
return editor.addMarkdown({style: short.style});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}(jQuery, Showdown, CodeMirror, shortcut, Countable));
|
|
|
@ -1,20 +1,66 @@
|
||||||
/*globals window, Backbone */
|
/*globals window, _, $, Backbone */
|
||||||
(function ($) {
|
(function ($) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var Ghost = {
|
var Ghost = {
|
||||||
Layout : {},
|
Layout : {},
|
||||||
View : {},
|
Views : {},
|
||||||
Collection : {},
|
Collections : {},
|
||||||
Model : {},
|
Models : {},
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
baseUrl: '/api/v0.1'
|
apiRoot: '/api/v0.1'
|
||||||
},
|
},
|
||||||
|
|
||||||
currentView: null
|
// This is a helper object to denote legacy things in the
|
||||||
|
// middle of being transitioned.
|
||||||
|
temporary: {},
|
||||||
|
|
||||||
|
currentView: null,
|
||||||
|
router: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ghost.View = Backbone.View.extend({
|
||||||
|
|
||||||
|
// Adds a subview to the current view, which will
|
||||||
|
// ensure its removal when this view is removed,
|
||||||
|
// or when view.removeSubviews is called
|
||||||
|
addSubview: function (view) {
|
||||||
|
if (!(view instanceof Backbone.View)) {
|
||||||
|
throw new Error("Subview must be a Backbone.View");
|
||||||
|
}
|
||||||
|
this.subviews = this.subviews || [];
|
||||||
|
this.subviews.push(view);
|
||||||
|
return view;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Removes any subviews associated with this view
|
||||||
|
// by `addSubview`, which will in-turn remove any
|
||||||
|
// children of those views, and so on.
|
||||||
|
removeSubviews: function () {
|
||||||
|
var i, l, children = this.subviews;
|
||||||
|
if (!children) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
for (i = 0, l = children.length; i < l; i += 1) {
|
||||||
|
children[i].remove();
|
||||||
|
}
|
||||||
|
this.subviews = [];
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Extends the view's remove, by calling `removeSubviews`
|
||||||
|
// if any subviews exist.
|
||||||
|
remove: function () {
|
||||||
|
if (this.subviews) {
|
||||||
|
this.removeSubviews();
|
||||||
|
}
|
||||||
|
return Backbone.View.prototype.remove.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
window.Ghost = Ghost;
|
window.Ghost = Ghost;
|
||||||
|
|
||||||
}());
|
}());
|
|
@ -2,16 +2,40 @@
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Ghost.Model.Post = Backbone.Model.extend({
|
Ghost.Models.Post = Backbone.Model.extend({
|
||||||
urlRoot: '/api/v0.1/posts/',
|
|
||||||
defaults: {
|
defaults: {
|
||||||
status: 'draft'
|
status: 'draft'
|
||||||
|
},
|
||||||
|
|
||||||
|
parse: function (resp) {
|
||||||
|
if (resp.tags) {
|
||||||
|
// TODO: parse tags into it's own collection on the model (this.tags)
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
},
|
||||||
|
|
||||||
|
validate: function (attrs) {
|
||||||
|
if (_.isEmpty(attrs.title)) {
|
||||||
|
return 'You must specify a title for the post.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ghost.Collection.Posts = Backbone.Collection.extend({
|
Ghost.Collections.Posts = Backbone.Collection.extend({
|
||||||
url: Ghost.settings.baseURL + '/posts',
|
url: Ghost.settings.apiRoot + '/posts',
|
||||||
model: Ghost.Model.Post
|
model: Ghost.Models.Post,
|
||||||
|
parse: function (resp) {
|
||||||
|
if (_.isArray(resp.posts)) {
|
||||||
|
this.limit = resp.limit;
|
||||||
|
this.currentPage = resp.page;
|
||||||
|
this.totalPages = resp.pages;
|
||||||
|
this.totalPosts = resp.total;
|
||||||
|
return resp.posts;
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}());
|
}());
|
38
core/admin/assets/js/router.js
Normal file
38
core/admin/assets/js/router.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*global window, document, Ghost, Backbone, $, _ */
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
Ghost.Router = Backbone.Router.extend({
|
||||||
|
|
||||||
|
routes: {
|
||||||
|
'content/': 'blog',
|
||||||
|
'editor': 'editor',
|
||||||
|
'editor/': 'editor',
|
||||||
|
'editor/:id': 'editor'
|
||||||
|
},
|
||||||
|
|
||||||
|
blog: function () {
|
||||||
|
var posts = new Ghost.Collections.Posts();
|
||||||
|
posts.fetch({data: {status: 'all'}}).then(function () {
|
||||||
|
Ghost.currentView = new Ghost.Views.Blog({ el: '#main', collection: posts });
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
editor: function (id) {
|
||||||
|
var post = new Ghost.Models.Post();
|
||||||
|
post.urlRoot = Ghost.settings.apiRoot + '/posts';
|
||||||
|
if (id) {
|
||||||
|
post.id = id;
|
||||||
|
post.fetch().then(function () {
|
||||||
|
Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
15
core/admin/assets/js/starter.js
Normal file
15
core/admin/assets/js/starter.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/*global window, document, Ghost, Backbone, $, _ */
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
Ghost.router = new Ghost.Router();
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
Backbone.history.start({pushState: true, hashChange: false, root: '/ghost'});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}());
|
|
@ -1,8 +1,26 @@
|
||||||
// # Toggle Support
|
// # Toggle Support
|
||||||
|
|
||||||
/*global document, jQuery */
|
/*global document, jQuery, Ghost */
|
||||||
(function ($) {
|
(function ($) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
Ghost.temporary.initToggles = function ($el) {
|
||||||
|
|
||||||
|
$el.find('[data-toggle]').each(function () {
|
||||||
|
var toggle = $(this).data('toggle');
|
||||||
|
$(this).parent().children(toggle).hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
$el.find('[data-toggle]').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).toggleClass('active');
|
||||||
|
var toggle = $(this).data('toggle');
|
||||||
|
$(this).parent().children(toggle).fadeToggle(100).toggleClass('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
// ## Toggle Up In Your Grill
|
// ## Toggle Up In Your Grill
|
||||||
|
@ -14,17 +32,7 @@
|
||||||
// <li>Toggled yo</li>
|
// <li>Toggled yo</li>
|
||||||
// </ul>
|
// </ul>
|
||||||
// </nav>
|
// </nav>
|
||||||
$('[data-toggle]').each(function () {
|
Ghost.temporary.initToggles($(document));
|
||||||
var toggle = $(this).data('toggle');
|
|
||||||
$(this).parent().children(toggle).hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('[data-toggle]').on('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$(this).toggleClass('active');
|
|
||||||
var toggle = $(this).data('toggle');
|
|
||||||
$(this).parent().children(toggle).fadeToggle(100).toggleClass('open');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}(jQuery));
|
}(jQuery));
|
|
@ -1,83 +1,153 @@
|
||||||
/*global window, document, Ghost, Backbone, $, _ */
|
/*global window, document, Ghost, Backbone, confirm, JST, $, _ */
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var ContentList,
|
||||||
|
ContentItem,
|
||||||
|
PreviewContainer,
|
||||||
|
|
||||||
|
// Add shadow during scrolling
|
||||||
|
scrollShadow = function (target, e) {
|
||||||
|
if ($(e.currentTarget).scrollTop() > 10) {
|
||||||
|
$(target).addClass('scrolling');
|
||||||
|
} else {
|
||||||
|
$(target).removeClass('scrolling');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Base view
|
// Base view
|
||||||
// ----------
|
// ----------
|
||||||
Ghost.Layout.Blog = Backbone.Layout.extend({
|
Ghost.Views.Blog = Ghost.View.extend({
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
this.addViews({
|
this.addSubview(new PreviewContainer({ el: '.js-content-preview', collection: this.collection })).render();
|
||||||
list : new Ghost.View.ContentList({ el: '.content-list' }),
|
this.addSubview(new ContentList({ el: '.js-content-list', collection: this.collection })).render();
|
||||||
preview : new Ghost.View.ContentPreview({ el: '.content-preview' })
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: render templates on the client
|
|
||||||
// this.render()
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add shadow during scrolling
|
|
||||||
var scrollShadow = function (target, e) {
|
|
||||||
if ($(e.currentTarget).scrollTop() > 10) {
|
|
||||||
$(target).addClass('scrolling');
|
|
||||||
} else {
|
|
||||||
$(target).removeClass('scrolling');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Content list (sidebar)
|
// Content list (sidebar)
|
||||||
// -----------------------
|
// -----------------------
|
||||||
Ghost.View.ContentList = Backbone.View.extend({
|
ContentList = Ghost.View.extend({
|
||||||
initialize: function (options) {
|
|
||||||
this.$('.content-list-content').on('scroll', _.bind(scrollShadow, null, '.content-list'));
|
|
||||||
// Select first item
|
|
||||||
_.defer(function (el) {
|
|
||||||
el.find('.content-list-content li:first').trigger('click');
|
|
||||||
}, this.$el);
|
|
||||||
},
|
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click .content-list-content' : 'scrollHandler',
|
'click .content-list-content' : 'scrollHandler'
|
||||||
'click .content-list-content li' : 'showPreview'
|
},
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
this.$('.content-list-content').on('scroll', _.bind(scrollShadow, null, '.content-list'));
|
||||||
|
this.listenTo(this.collection, 'remove', this.showNext);
|
||||||
|
},
|
||||||
|
|
||||||
|
showNext: function () {
|
||||||
|
var id = this.collection.at(0).id;
|
||||||
|
if (id) {
|
||||||
|
Backbone.trigger('blog:activeItem', id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
this.collection.each(function (model) {
|
||||||
|
this.$('ol').append(this.addSubview(new ContentItem({model: model})).render().el);
|
||||||
|
}, this);
|
||||||
|
this.showNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Content Item
|
||||||
|
// -----------------------
|
||||||
|
ContentItem = Ghost.View.extend({
|
||||||
|
|
||||||
|
tagName: 'li',
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'click a': 'setActiveItem'
|
||||||
|
},
|
||||||
|
|
||||||
|
active: false,
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.listenTo(Backbone, 'blog:activeItem', this.checkActive);
|
||||||
|
this.listenTo(this.model, 'destroy', this.removeItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeItem: function () {
|
||||||
|
var view = this;
|
||||||
|
$.when(this.$el.slideUp()).then(function () {
|
||||||
|
view.remove();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// If the current item isn't active, we trigger the event to
|
||||||
|
// notify a change in which item we're viewing.
|
||||||
|
setActiveItem: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.active !== true) {
|
||||||
|
Backbone.trigger('blog:activeItem', this.model.id);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Checks whether this item is active and doesn't match the current id.
|
||||||
|
checkActive: function (id) {
|
||||||
|
if (this.model.id !== id) {
|
||||||
|
if (this.active) {
|
||||||
|
this.active = false;
|
||||||
|
this.$el.removeClass('active');
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.active = true;
|
||||||
|
this.$el.addClass('active');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
showPreview: function (e) {
|
showPreview: function (e) {
|
||||||
var item = $(e.currentTarget);
|
var item = $(e.currentTarget);
|
||||||
this.$('.content-list-content li').removeClass('active');
|
this.$('.content-list-content li').removeClass('active');
|
||||||
item.addClass('active');
|
item.addClass('active');
|
||||||
Backbone.trigger("blog:showPreview", item.data('id'));
|
Backbone.trigger('blog:activeItem', item.data('id'));
|
||||||
|
},
|
||||||
|
|
||||||
|
template: JST['content/list-item'],
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
this.$el.html(this.template(_.extend({active: this.active}, this.model.toJSON())));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Content preview
|
// Content preview
|
||||||
// ----------------
|
// ----------------
|
||||||
Ghost.View.ContentPreview = Backbone.View.extend({
|
PreviewContainer = Ghost.View.extend({
|
||||||
initialize: function (options) {
|
|
||||||
this.listenTo(Backbone, "blog:showPreview", this.showPost);
|
activeId: null,
|
||||||
this.$('.content-preview-content').on('scroll', _.bind(scrollShadow, null, '.content-preview'));
|
|
||||||
},
|
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click .post-controls .delete' : 'deletePost',
|
'click .post-controls .delete' : 'deletePost',
|
||||||
'click .post-controls .post-edit' : 'editPost'
|
'click .post-controls .post-edit' : 'editPost'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
initialize: function (options) {
|
||||||
|
this.listenTo(Backbone, 'blog:activeItem', this.setActivePreview);
|
||||||
|
this.$('.content-preview-content').on('scroll', _.bind(scrollShadow, null, '.content-preview'));
|
||||||
|
},
|
||||||
|
|
||||||
|
setActivePreview: function (id) {
|
||||||
|
if (this.activeId !== id) {
|
||||||
|
this.activeId = id;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
deletePost: function (e) {
|
deletePost: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.model.destroy({
|
if (confirm('Are you sure you want to delete this post?')) {
|
||||||
success: function (model) {
|
this.model.destroy({
|
||||||
// here the ContentList would pick up the change in the Posts collection automatically
|
wait: true
|
||||||
// after client-side rendering is implemented
|
});
|
||||||
var item = $('.content-list-content li[data-id=' + model.get('id') + ']');
|
}
|
||||||
item.next().add(item.prev()).eq(0).trigger('click');
|
|
||||||
item.remove();
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
// TODO: decent error handling
|
|
||||||
console.error('Error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
editPost: function (e) {
|
editPost: function (e) {
|
||||||
|
@ -87,19 +157,20 @@
|
||||||
window.location = '/ghost/editor/' + this.model.get('id');
|
window.location = '/ghost/editor/' + this.model.get('id');
|
||||||
},
|
},
|
||||||
|
|
||||||
showPost: function (id) {
|
template: JST['content/preview'],
|
||||||
this.model = new Ghost.Model.Post({ id: id });
|
|
||||||
this.model.once('change', this.render, this);
|
|
||||||
this.model.fetch();
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function () {
|
render: function () {
|
||||||
this.$('.wrapper').html(this.model.get('content_html'));
|
if (this.activeId) {
|
||||||
|
this.model = this.collection.get(this.activeId);
|
||||||
|
this.$el.html(this.template(this.model.toJSON()));
|
||||||
|
}
|
||||||
|
this.$('.wrapper').on('click', 'a', function (e) {
|
||||||
|
$(e.currentTarget).attr('target', '_blank');
|
||||||
|
});
|
||||||
|
Ghost.temporary.initToggles(this.$el);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize views.
|
|
||||||
// TODO: move to a `Backbone.Router`
|
|
||||||
Ghost.currentView = new Ghost.Layout.Blog({ el: '#main' });
|
|
||||||
|
|
||||||
}());
|
}());
|
219
core/admin/assets/js/views/editor.js
Normal file
219
core/admin/assets/js/views/editor.js
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
// # Article Editor
|
||||||
|
|
||||||
|
/*global window, alert, document, history, Backbone, Ghost, $, _, Showdown, CodeMirror, shortcut, Countable */
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var PublishBar,
|
||||||
|
TagWidget,
|
||||||
|
ActionsWidget,
|
||||||
|
MarkdownShortcuts = [
|
||||||
|
{'key': 'Ctrl+B', 'style': 'bold'},
|
||||||
|
{'key': 'Meta+B', 'style': 'bold'},
|
||||||
|
{'key': 'Ctrl+I', 'style': 'italic'},
|
||||||
|
{'key': 'Meta+I', 'style': 'italic'},
|
||||||
|
{'key': 'Ctrl+Alt+U', 'style': 'strike'},
|
||||||
|
{'key': 'Ctrl+Shift+K', 'style': 'code'},
|
||||||
|
{'key': 'Alt+1', 'style': 'h1'},
|
||||||
|
{'key': 'Alt+2', 'style': 'h2'},
|
||||||
|
{'key': 'Alt+3', 'style': 'h3'},
|
||||||
|
{'key': 'Alt+4', 'style': 'h4'},
|
||||||
|
{'key': 'Alt+5', 'style': 'h5'},
|
||||||
|
{'key': 'Alt+6', 'style': 'h6'},
|
||||||
|
{'key': 'Ctrl+Shift+L', 'style': 'link'},
|
||||||
|
{'key': 'Ctrl+Shift+I', 'style': 'image'},
|
||||||
|
{'key': 'Ctrl+Q', 'style': 'blockquote'},
|
||||||
|
{'key': 'Ctrl+Shift+1', 'style': 'currentdate'}
|
||||||
|
];
|
||||||
|
|
||||||
|
// The publish bar associated with a post, which has the TagWidget and
|
||||||
|
// Save button and options and such.
|
||||||
|
// ----------------------------------------
|
||||||
|
PublishBar = Ghost.View.extend({
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.addSubview(new TagWidget({el: this.$('#entry-categories'), model: this.model})).render();
|
||||||
|
this.addSubview(new ActionsWidget({el: this.$('#entry-actions'), model: this.model})).render();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// The Tag UI area associated with a post
|
||||||
|
// ----------------------------------------
|
||||||
|
TagWidget = Ghost.View.extend({
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// The Publish, Queue, Publish Now buttons
|
||||||
|
// ----------------------------------------
|
||||||
|
ActionsWidget = Ghost.View.extend({
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'click [data-set-status]': 'handleStatus',
|
||||||
|
'click .js-post-button': 'updatePost'
|
||||||
|
},
|
||||||
|
|
||||||
|
statusMap: {
|
||||||
|
'draft' : 'Save Draft',
|
||||||
|
'published': 'Update Post',
|
||||||
|
'scheduled' : 'Save Schedued Post'
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.listenTo(this.model, 'change:status', this.render);
|
||||||
|
this.model.on('change:id', function (m) {
|
||||||
|
Backbone.history.navigate('/editor/' + m.id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleStatus: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var status = $(e.currentTarget).attr('data-set-status'),
|
||||||
|
model = this.model;
|
||||||
|
|
||||||
|
if (status === 'publish-on') {
|
||||||
|
return alert('Scheduled publishing not supported yet.');
|
||||||
|
}
|
||||||
|
if (status === 'queue') {
|
||||||
|
return alert('Scheduled publishing not supported yet.');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.savePost({
|
||||||
|
status: status
|
||||||
|
}).then(function () {
|
||||||
|
alert('Your post: ' + model.get('title') + ' has been ' + status);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePost: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var model = this.model;
|
||||||
|
this.savePost().then(function () {
|
||||||
|
alert('Your post was saved as ' + model.get('status'));
|
||||||
|
}, function () {
|
||||||
|
alert(model.validationError);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
savePost: function (data) {
|
||||||
|
// TODO: The content getter here isn't great, shouldn't rely on currentView.
|
||||||
|
var saved = this.model.save(_.extend({
|
||||||
|
title: $('#entry-title').val(),
|
||||||
|
content: Ghost.currentView.editor.getValue()
|
||||||
|
}, data));
|
||||||
|
|
||||||
|
// TODO: Take this out if #2489 gets merged in Backbone. Or patch Backbone
|
||||||
|
// ourselves for more consistent promises.
|
||||||
|
if (saved) {
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
return $.Deferred().reject();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
this.$('.js-post-button').text(this.statusMap[this.model.get('status')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// The entire /editor page's route (TODO: move all views to client side templates)
|
||||||
|
// ----------------------------------------
|
||||||
|
Ghost.Views.Editor = Ghost.View.extend({
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
|
||||||
|
// Add the container view for the Publish Bar
|
||||||
|
this.addSubview(new PublishBar({el: "#publish-bar", model: this.model})).render();
|
||||||
|
|
||||||
|
this.$('#entry-markdown').html(this.model.get('content'));
|
||||||
|
|
||||||
|
this.initMarkdown();
|
||||||
|
this.renderPreview();
|
||||||
|
|
||||||
|
// TODO: Debounce
|
||||||
|
this.$('.CodeMirror-scroll').on('scroll', this.syncScroll);
|
||||||
|
|
||||||
|
// Shadow on Markdown if scrolled
|
||||||
|
this.$('.CodeMirror-scroll').on('scroll', function (e) {
|
||||||
|
if ($('.CodeMirror-scroll').scrollTop() > 10) {
|
||||||
|
$('.entry-markdown').addClass('scrolling');
|
||||||
|
} else {
|
||||||
|
$('.entry-markdown').removeClass('scrolling');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Shadow on Preview if scrolled
|
||||||
|
this.$('.entry-preview-content').on('scroll', function (e) {
|
||||||
|
if ($('.entry-preview-content').scrollTop() > 10) {
|
||||||
|
$('.entry-preview').addClass('scrolling');
|
||||||
|
} else {
|
||||||
|
$('.entry-preview').removeClass('scrolling');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Zen writing mode shortcut
|
||||||
|
shortcut.add("Alt+Shift+Z", function () {
|
||||||
|
$('body').toggleClass('zen');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.entry-markdown header, .entry-preview header').click(function (e) {
|
||||||
|
$('.entry-markdown, .entry-preview').removeClass('active');
|
||||||
|
$(e.target).closest('section').addClass('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
syncScroll: function (e) {
|
||||||
|
var $codeViewport = $(e.target),
|
||||||
|
$previewViewport = $('.entry-preview-content'),
|
||||||
|
$codeContent = $('.CodeMirror-sizer'),
|
||||||
|
$previewContent = $('.rendered-markdown'),
|
||||||
|
|
||||||
|
// calc position
|
||||||
|
codeHeight = $codeContent.height() - $codeViewport.height(),
|
||||||
|
previewHeight = $previewContent.height() - $previewViewport.height(),
|
||||||
|
ratio = previewHeight / codeHeight,
|
||||||
|
previewPostition = $codeViewport.scrollTop() * ratio;
|
||||||
|
|
||||||
|
// apply new scroll
|
||||||
|
$previewViewport.scrollTop(previewPostition);
|
||||||
|
},
|
||||||
|
|
||||||
|
// This updates the editor preview panel.
|
||||||
|
// Currently gets called on every key press.
|
||||||
|
// Also trigger word count update
|
||||||
|
renderPreview: function () {
|
||||||
|
var view = this,
|
||||||
|
preview = document.getElementsByClassName('rendered-markdown')[0];
|
||||||
|
preview.innerHTML = this.converter.makeHtml(this.editor.getValue());
|
||||||
|
Countable.once(preview, function (counter) {
|
||||||
|
view.$('.entry-word-count').text(counter.words + ' words');
|
||||||
|
view.$('.entry-character-count').text(counter.characters + ' characters');
|
||||||
|
view.$('.entry-paragraph-count').text(counter.paragraphs + ' paragraphs');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Markdown converter & markdown shortcut initialization.
|
||||||
|
initMarkdown: function () {
|
||||||
|
this.converter = new Showdown.converter({extensions: ['ghostdown']});
|
||||||
|
this.editor = CodeMirror.fromTextArea(document.getElementById('entry-markdown'), {
|
||||||
|
mode: 'markdown',
|
||||||
|
tabMode: 'indent',
|
||||||
|
lineWrapping: true
|
||||||
|
});
|
||||||
|
|
||||||
|
_.each(MarkdownShortcuts, function (combo) {
|
||||||
|
shortcut.add(combo.key, function () {
|
||||||
|
return this.editor.addMarkdown({style: combo.style});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var view = this;
|
||||||
|
this.editor.on('change', function () {
|
||||||
|
view.renderPreview();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -1,56 +0,0 @@
|
||||||
// Layout manager
|
|
||||||
// --------------
|
|
||||||
/*
|
|
||||||
* .addChild('sidebar', App.View.Sidebar)
|
|
||||||
* .childViews.sidebar.$('blah')
|
|
||||||
*/
|
|
||||||
|
|
||||||
Backbone.Layout = Backbone.View.extend({
|
|
||||||
// default to loading state, reverted on render()
|
|
||||||
loading: true,
|
|
||||||
|
|
||||||
addViews: function (views) {
|
|
||||||
if (!this.views) this.views = {}
|
|
||||||
|
|
||||||
_.each(views, function(view, name){
|
|
||||||
if (typeof view.model === 'undefined'){
|
|
||||||
view.model = this.model
|
|
||||||
}
|
|
||||||
this.views[name] = view
|
|
||||||
}, this)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
renderViews: function (data) {
|
|
||||||
_.invoke(this.views, 'render', data)
|
|
||||||
this.trigger('render')
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
appendViews: function (target) {
|
|
||||||
_.each(this.views, function(view){
|
|
||||||
this.$el.append(view.el)
|
|
||||||
}, this)
|
|
||||||
this.trigger('append')
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyViews: function () {
|
|
||||||
_.each(this.views, function(view){
|
|
||||||
view.model = null
|
|
||||||
view.remove()
|
|
||||||
})
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function () {
|
|
||||||
this.loading = false
|
|
||||||
this.renderViews()
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function () {
|
|
||||||
this.destroyViews()
|
|
||||||
Backbone.View.prototype.remove.call(this)
|
|
||||||
}
|
|
||||||
})
|
|
362
core/admin/assets/lib/handlebars/handlebars-runtime.js
Normal file
362
core/admin/assets/lib/handlebars/handlebars-runtime.js
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (C) 2011 by Yehuda Katz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// lib/handlebars/browser-prefix.js
|
||||||
|
var Handlebars = {};
|
||||||
|
|
||||||
|
(function(Handlebars, undefined) {
|
||||||
|
;
|
||||||
|
// lib/handlebars/base.js
|
||||||
|
|
||||||
|
Handlebars.VERSION = "1.0.0";
|
||||||
|
Handlebars.COMPILER_REVISION = 4;
|
||||||
|
|
||||||
|
Handlebars.REVISION_CHANGES = {
|
||||||
|
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
|
||||||
|
2: '== 1.0.0-rc.3',
|
||||||
|
3: '== 1.0.0-rc.4',
|
||||||
|
4: '>= 1.0.0'
|
||||||
|
};
|
||||||
|
|
||||||
|
Handlebars.helpers = {};
|
||||||
|
Handlebars.partials = {};
|
||||||
|
|
||||||
|
var toString = Object.prototype.toString,
|
||||||
|
functionType = '[object Function]',
|
||||||
|
objectType = '[object Object]';
|
||||||
|
|
||||||
|
Handlebars.registerHelper = function(name, fn, inverse) {
|
||||||
|
if (toString.call(name) === objectType) {
|
||||||
|
if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
|
||||||
|
Handlebars.Utils.extend(this.helpers, name);
|
||||||
|
} else {
|
||||||
|
if (inverse) { fn.not = inverse; }
|
||||||
|
this.helpers[name] = fn;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Handlebars.registerPartial = function(name, str) {
|
||||||
|
if (toString.call(name) === objectType) {
|
||||||
|
Handlebars.Utils.extend(this.partials, name);
|
||||||
|
} else {
|
||||||
|
this.partials[name] = str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Handlebars.registerHelper('helperMissing', function(arg) {
|
||||||
|
if(arguments.length === 2) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
throw new Error("Missing helper: '" + arg + "'");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
|
||||||
|
var inverse = options.inverse || function() {}, fn = options.fn;
|
||||||
|
|
||||||
|
var type = toString.call(context);
|
||||||
|
|
||||||
|
if(type === functionType) { context = context.call(this); }
|
||||||
|
|
||||||
|
if(context === true) {
|
||||||
|
return fn(this);
|
||||||
|
} else if(context === false || context == null) {
|
||||||
|
return inverse(this);
|
||||||
|
} else if(type === "[object Array]") {
|
||||||
|
if(context.length > 0) {
|
||||||
|
return Handlebars.helpers.each(context, options);
|
||||||
|
} else {
|
||||||
|
return inverse(this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fn(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.K = function() {};
|
||||||
|
|
||||||
|
Handlebars.createFrame = Object.create || function(object) {
|
||||||
|
Handlebars.K.prototype = object;
|
||||||
|
var obj = new Handlebars.K();
|
||||||
|
Handlebars.K.prototype = null;
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
Handlebars.logger = {
|
||||||
|
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
|
||||||
|
|
||||||
|
methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
|
||||||
|
|
||||||
|
// can be overridden in the host environment
|
||||||
|
log: function(level, obj) {
|
||||||
|
if (Handlebars.logger.level <= level) {
|
||||||
|
var method = Handlebars.logger.methodMap[level];
|
||||||
|
if (typeof console !== 'undefined' && console[method]) {
|
||||||
|
console[method].call(console, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
|
||||||
|
|
||||||
|
Handlebars.registerHelper('each', function(context, options) {
|
||||||
|
var fn = options.fn, inverse = options.inverse;
|
||||||
|
var i = 0, ret = "", data;
|
||||||
|
|
||||||
|
var type = toString.call(context);
|
||||||
|
if(type === functionType) { context = context.call(this); }
|
||||||
|
|
||||||
|
if (options.data) {
|
||||||
|
data = Handlebars.createFrame(options.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(context && typeof context === 'object') {
|
||||||
|
if(context instanceof Array){
|
||||||
|
for(var j = context.length; i<j; i++) {
|
||||||
|
if (data) { data.index = i; }
|
||||||
|
ret = ret + fn(context[i], { data: data });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(var key in context) {
|
||||||
|
if(context.hasOwnProperty(key)) {
|
||||||
|
if(data) { data.key = key; }
|
||||||
|
ret = ret + fn(context[key], {data: data});
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i === 0){
|
||||||
|
ret = inverse(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('if', function(conditional, options) {
|
||||||
|
var type = toString.call(conditional);
|
||||||
|
if(type === functionType) { conditional = conditional.call(this); }
|
||||||
|
|
||||||
|
if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
|
||||||
|
return options.inverse(this);
|
||||||
|
} else {
|
||||||
|
return options.fn(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('unless', function(conditional, options) {
|
||||||
|
return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('with', function(context, options) {
|
||||||
|
var type = toString.call(context);
|
||||||
|
if(type === functionType) { context = context.call(this); }
|
||||||
|
|
||||||
|
if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
|
||||||
|
});
|
||||||
|
|
||||||
|
Handlebars.registerHelper('log', function(context, options) {
|
||||||
|
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
|
||||||
|
Handlebars.log(level, context);
|
||||||
|
});
|
||||||
|
;
|
||||||
|
// lib/handlebars/utils.js
|
||||||
|
|
||||||
|
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
||||||
|
|
||||||
|
Handlebars.Exception = function(message) {
|
||||||
|
var tmp = Error.prototype.constructor.apply(this, arguments);
|
||||||
|
|
||||||
|
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
|
||||||
|
for (var idx = 0; idx < errorProps.length; idx++) {
|
||||||
|
this[errorProps[idx]] = tmp[errorProps[idx]];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Handlebars.Exception.prototype = new Error();
|
||||||
|
|
||||||
|
// Build out our basic SafeString type
|
||||||
|
Handlebars.SafeString = function(string) {
|
||||||
|
this.string = string;
|
||||||
|
};
|
||||||
|
Handlebars.SafeString.prototype.toString = function() {
|
||||||
|
return this.string.toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
var escape = {
|
||||||
|
"&": "&",
|
||||||
|
"<": "<",
|
||||||
|
">": ">",
|
||||||
|
'"': """,
|
||||||
|
"'": "'",
|
||||||
|
"`": "`"
|
||||||
|
};
|
||||||
|
|
||||||
|
var badChars = /[&<>"'`]/g;
|
||||||
|
var possible = /[&<>"'`]/;
|
||||||
|
|
||||||
|
var escapeChar = function(chr) {
|
||||||
|
return escape[chr] || "&";
|
||||||
|
};
|
||||||
|
|
||||||
|
Handlebars.Utils = {
|
||||||
|
extend: function(obj, value) {
|
||||||
|
for(var key in value) {
|
||||||
|
if(value.hasOwnProperty(key)) {
|
||||||
|
obj[key] = value[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
escapeExpression: function(string) {
|
||||||
|
// don't escape SafeStrings, since they're already safe
|
||||||
|
if (string instanceof Handlebars.SafeString) {
|
||||||
|
return string.toString();
|
||||||
|
} else if (string == null || string === false) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a string conversion as this will be done by the append regardless and
|
||||||
|
// the regex test will do this transparently behind the scenes, causing issues if
|
||||||
|
// an object's to string has escaped characters in it.
|
||||||
|
string = string.toString();
|
||||||
|
|
||||||
|
if(!possible.test(string)) { return string; }
|
||||||
|
return string.replace(badChars, escapeChar);
|
||||||
|
},
|
||||||
|
|
||||||
|
isEmpty: function(value) {
|
||||||
|
if (!value && value !== 0) {
|
||||||
|
return true;
|
||||||
|
} else if(toString.call(value) === "[object Array]" && value.length === 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
;
|
||||||
|
// lib/handlebars/runtime.js
|
||||||
|
|
||||||
|
Handlebars.VM = {
|
||||||
|
template: function(templateSpec) {
|
||||||
|
// Just add water
|
||||||
|
var container = {
|
||||||
|
escapeExpression: Handlebars.Utils.escapeExpression,
|
||||||
|
invokePartial: Handlebars.VM.invokePartial,
|
||||||
|
programs: [],
|
||||||
|
program: function(i, fn, data) {
|
||||||
|
var programWrapper = this.programs[i];
|
||||||
|
if(data) {
|
||||||
|
programWrapper = Handlebars.VM.program(i, fn, data);
|
||||||
|
} else if (!programWrapper) {
|
||||||
|
programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
|
||||||
|
}
|
||||||
|
return programWrapper;
|
||||||
|
},
|
||||||
|
merge: function(param, common) {
|
||||||
|
var ret = param || common;
|
||||||
|
|
||||||
|
if (param && common) {
|
||||||
|
ret = {};
|
||||||
|
Handlebars.Utils.extend(ret, common);
|
||||||
|
Handlebars.Utils.extend(ret, param);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
programWithDepth: Handlebars.VM.programWithDepth,
|
||||||
|
noop: Handlebars.VM.noop,
|
||||||
|
compilerInfo: null
|
||||||
|
};
|
||||||
|
|
||||||
|
return function(context, options) {
|
||||||
|
options = options || {};
|
||||||
|
var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
|
||||||
|
|
||||||
|
var compilerInfo = container.compilerInfo || [],
|
||||||
|
compilerRevision = compilerInfo[0] || 1,
|
||||||
|
currentRevision = Handlebars.COMPILER_REVISION;
|
||||||
|
|
||||||
|
if (compilerRevision !== currentRevision) {
|
||||||
|
if (compilerRevision < currentRevision) {
|
||||||
|
var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
|
||||||
|
compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
|
||||||
|
throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
|
||||||
|
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
|
||||||
|
} else {
|
||||||
|
// Use the embedded version info since the runtime doesn't know about this revision yet
|
||||||
|
throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
|
||||||
|
"Please update your runtime to a newer version ("+compilerInfo[1]+").";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
programWithDepth: function(i, fn, data /*, $depth */) {
|
||||||
|
var args = Array.prototype.slice.call(arguments, 3);
|
||||||
|
|
||||||
|
var program = function(context, options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
return fn.apply(this, [context, options.data || data].concat(args));
|
||||||
|
};
|
||||||
|
program.program = i;
|
||||||
|
program.depth = args.length;
|
||||||
|
return program;
|
||||||
|
},
|
||||||
|
program: function(i, fn, data) {
|
||||||
|
var program = function(context, options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
return fn(context, options.data || data);
|
||||||
|
};
|
||||||
|
program.program = i;
|
||||||
|
program.depth = 0;
|
||||||
|
return program;
|
||||||
|
},
|
||||||
|
noop: function() { return ""; },
|
||||||
|
invokePartial: function(partial, name, context, helpers, partials, data) {
|
||||||
|
var options = { helpers: helpers, partials: partials, data: data };
|
||||||
|
|
||||||
|
if(partial === undefined) {
|
||||||
|
throw new Handlebars.Exception("The partial " + name + " could not be found");
|
||||||
|
} else if(partial instanceof Function) {
|
||||||
|
return partial(context, options);
|
||||||
|
} else if (!Handlebars.compile) {
|
||||||
|
throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
|
||||||
|
} else {
|
||||||
|
partials[name] = Handlebars.compile(partial, {data: data !== undefined});
|
||||||
|
return partials[name](context, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Handlebars.template = Handlebars.VM.template;
|
||||||
|
;
|
||||||
|
// lib/handlebars/browser-suffix.js
|
||||||
|
})(Handlebars);
|
||||||
|
;
|
7
core/admin/assets/tmpl/content/list-item.hbs
Normal file
7
core/admin/assets/tmpl/content/list-item.hbs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<a class="permalink{{#if featured}} featured{{/if}}" href="#">
|
||||||
|
<h3 class="entry-title">{{title}}</h3>
|
||||||
|
<section class="entry-meta">
|
||||||
|
<time datetime="2013-01-04" class="date">5 minutes ago</time>
|
||||||
|
{{!<span class="views">1,934</span>}}
|
||||||
|
</section>
|
||||||
|
</a>
|
21
core/admin/assets/tmpl/content/preview.hbs
Normal file
21
core/admin/assets/tmpl/content/preview.hbs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<header class="floatingheader">
|
||||||
|
<a class="{{#if featured}}featured{{else}}unfeatured{{/if}}" href="#">
|
||||||
|
<span class="hidden">Star</span>
|
||||||
|
</a>
|
||||||
|
{{! TODO: JavaScript toggle featured/unfeatured}}
|
||||||
|
<span class="status">Published</span>
|
||||||
|
<span class="normal">by</span>
|
||||||
|
<span class="author">John O'Nolan</span>
|
||||||
|
<section class="post-controls">
|
||||||
|
<a class="post-edit" href="#"><span class="hidden">Edit Post</span></a>
|
||||||
|
<a class="post-settings" href="#" data-toggle=".menu-drop-right"><span class="hidden">Post Settings</span></a>
|
||||||
|
<ul class="menu-drop-right">
|
||||||
|
<li><a href="#" class="url">URL</a></li>
|
||||||
|
<li><a href="#" class="something">Something</a></li>
|
||||||
|
<li><a href="#" class="delete">Delete</a></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</header>
|
||||||
|
<section class="content-preview-content">
|
||||||
|
<div class="wrapper">{{{content_html}}}</div>
|
||||||
|
</section>
|
|
@ -1,9 +1,9 @@
|
||||||
{{!< default}}
|
{{!< default}}
|
||||||
<section class="content-list">
|
<section class="content-list js-content-list">
|
||||||
<header class="floatingheader">
|
<header class="floatingheader">
|
||||||
<section class="content-filter">
|
<section class="content-filter">
|
||||||
<a class="dropdown" href="#" data-toggle=".menu-drop">All Posts</a>
|
<a class="dropdown" href="#" data-toggle=".menu-drop">All Posts</a>
|
||||||
<ul class="menu-drop">
|
<ul class="menu-drop" style="display:none;">
|
||||||
<li class="active"><a href="#">All Posts</a></li>
|
<li class="active"><a href="#">All Posts</a></li>
|
||||||
<li><a href="#">Recently Edited</a></li>
|
<li><a href="#">Recently Edited</a></li>
|
||||||
<li><a href="#">By Author...</a></li>
|
<li><a href="#">By Author...</a></li>
|
||||||
|
@ -13,43 +13,9 @@
|
||||||
<a href="/ghost/editor" class="button button-add"><span class="hidden">New Post</span></a>
|
<a href="/ghost/editor" class="button button-add"><span class="hidden">New Post</span></a>
|
||||||
</header>
|
</header>
|
||||||
<section class="content-list-content">
|
<section class="content-list-content">
|
||||||
<ol>
|
<ol></ol>
|
||||||
{{#each posts}}
|
|
||||||
{{! #if featured class="featured"{{/if}}
|
|
||||||
<li data-id="{{id}}">
|
|
||||||
<a class="permalink" href="#">
|
|
||||||
<h3 class="entry-title">{{title}}</h3>
|
|
||||||
<section class="entry-meta">
|
|
||||||
<time datetime="2013-01-04" class="date">5 minutes ago</time>
|
|
||||||
{{!<span class="views">1,934</span>}}
|
|
||||||
</section>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ol>
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="content-preview">
|
<section class="content-preview js-content-preview">
|
||||||
<header class="floatingheader">
|
|
||||||
|
|
||||||
<a class="unfeatured" href="#"><span class="hidden">Star</span></a>
|
|
||||||
{{! TODO: JavaScript toggle featured/unfeatured}}
|
|
||||||
|
|
||||||
<span class="status">Published</span>
|
|
||||||
<span class="normal">by</span>
|
|
||||||
<span class="author">John O'Nolan</span>
|
|
||||||
<section class="post-controls">
|
|
||||||
<a class="post-edit" href="#"><span class="hidden">Edit Post</span></a>
|
|
||||||
<a class="post-settings" href="#" data-toggle=".menu-drop-right"><span class="hidden">Post Settings</span></a>
|
|
||||||
<ul class="menu-drop-right">
|
|
||||||
<li><a href="#" class="url">URL</a></li>
|
|
||||||
<li><a href="#" class="something">Something</a></li>
|
|
||||||
<li><a href="#" class="delete">Delete</a></li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</header>
|
|
||||||
<section class="content-preview-content">
|
|
||||||
<div class="wrapper"></div>
|
|
||||||
</section>
|
|
||||||
</section>
|
</section>
|
|
@ -2,8 +2,6 @@
|
||||||
<script src="/core/admin/assets/lib/chart.min.js"></script>
|
<script src="/core/admin/assets/lib/chart.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
//$('body').click(function(){
|
|
||||||
$('.widget-time').fadeIn(1000);
|
$('.widget-time').fadeIn(1000);
|
||||||
$('.widget-image').delay(300).fadeIn(1000);
|
$('.widget-image').delay(300).fadeIn(1000);
|
||||||
$('.widget-posts').delay(600).fadeIn(900, function(){
|
$('.widget-posts').delay(600).fadeIn(900, function(){
|
||||||
|
@ -48,7 +46,6 @@
|
||||||
$('.widget-ideas').delay(3200).fadeIn(1000);
|
$('.widget-ideas').delay(3200).fadeIn(1000);
|
||||||
$('.widget-tweets').delay(3400).fadeIn(1000);
|
$('.widget-tweets').delay(3400).fadeIn(1000);
|
||||||
$('.widget-backups').delay(3600).fadeIn(1000);
|
$('.widget-backups').delay(3600).fadeIn(1000);
|
||||||
//});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{{/contentFor}}
|
{{/contentFor}}
|
||||||
|
|
|
@ -22,17 +22,6 @@
|
||||||
<link rel="stylesheet" type="text/css" href="/core/admin/assets/lib/codemirror/codemirror.css"> <!-- TODO: Kill this - #29 -->
|
<link rel="stylesheet" type="text/css" href="/core/admin/assets/lib/codemirror/codemirror.css"> <!-- TODO: Kill this - #29 -->
|
||||||
<link rel="stylesheet" type="text/css" href="/core/admin/assets/lib/icheck/css/icheck.css"> <!-- TODO: Kill this - #29 -->
|
<link rel="stylesheet" type="text/css" href="/core/admin/assets/lib/icheck/css/icheck.css"> <!-- TODO: Kill this - #29 -->
|
||||||
{{{block "pageStyles"}}}
|
{{{block "pageStyles"}}}
|
||||||
|
|
||||||
<!-- TODO: move all scripts to the end of body -->
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/lib/jquery/jquery.min.js"></script>
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/lib/icheck/jquery.icheck.min.js"></script>
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/lib/underscore.js"></script>
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/lib/backbone/backbone.js"></script>
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/lib/backbone/backbone-layout.js"></script>
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/lib/countable.js"></script>
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/js/toggle.js"></script>
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/js/admin-ui-temp.js"></script>
|
|
||||||
{{{block "headScripts"}}}
|
|
||||||
</head>
|
</head>
|
||||||
<body class="{{bodyClass}}">
|
<body class="{{bodyClass}}">
|
||||||
{{#unless hideNavbar}}
|
{{#unless hideNavbar}}
|
||||||
|
@ -45,11 +34,32 @@
|
||||||
{{{body}}}
|
{{{body}}}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script type="text/javascript" src="/core/admin/assets/js/init.js"></script>
|
<script src="/core/admin/assets/lib/jquery/jquery.min.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/icheck/jquery.icheck.min.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/underscore.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/backbone/backbone.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/handlebars/handlebars-runtime.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/codemirror/codemirror.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/codemirror/mode/markdown/markdown.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/showdown/showdown.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/showdown/extensions/ghostdown.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/shortcuts.js"></script>
|
||||||
|
<script src="/core/admin/assets/lib/countable.js"></script>
|
||||||
|
|
||||||
|
<script src="/core/admin/assets/js/init.js"></script>
|
||||||
|
<script src="/core/admin/assets/tmpl/hbs-tmpl.js"></script>
|
||||||
|
<script src="/core/admin/assets/js/toggle.js"></script>
|
||||||
|
<script src="/core/admin/assets/js/admin-ui-temp.js"></script>
|
||||||
|
<script src="/core/admin/assets/js/markdown-actions.js"></script>
|
||||||
|
<script src="/core/admin/assets/js/tagui.js"></script>
|
||||||
|
|
||||||
<!-- // require '/core/admin/assets/js/models/*' -->
|
<!-- // require '/core/admin/assets/js/models/*' -->
|
||||||
<script type="text/javascript" src="/core/admin/assets/js/models/post.js"></script>
|
<script src="/core/admin/assets/js/models/post.js"></script>
|
||||||
<!-- // require '/core/admin/assets/js/views/*' -->
|
<!-- // require '/core/admin/assets/js/views/*' -->
|
||||||
<script type="text/javascript" src="/core/admin/assets/js/views/blog.js"></script>
|
<script src="/core/admin/assets/js/views/blog.js"></script>
|
||||||
|
<script src="/core/admin/assets/js/views/editor.js"></script>
|
||||||
|
<script src="/core/admin/assets/js/router.js"></script>
|
||||||
|
<script src="/core/admin/assets/js/starter.js"></script>
|
||||||
{{{block "bodyScripts"}}}
|
{{{block "bodyScripts"}}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,14 +1,3 @@
|
||||||
{{#contentFor 'bodyScripts'}}
|
|
||||||
<script src="/core/admin/assets/lib/codemirror/codemirror.js"></script>
|
|
||||||
<script src="/core/admin/assets/lib/codemirror/mode/markdown/markdown.js"></script>
|
|
||||||
<script src="/core/admin/assets/lib/showdown/showdown.js"></script>
|
|
||||||
<script src="/core/admin/assets/lib/showdown/extensions/ghostdown.js"></script>
|
|
||||||
<script src="/core/admin/assets/lib/shortcuts.js"></script>
|
|
||||||
<script src="/core/admin/assets/js/markdown-actions.js"></script>
|
|
||||||
<script src="/core/admin/assets/js/editor.js"></script>
|
|
||||||
<script src="/core/admin/assets/js/tagui.js"></script>
|
|
||||||
{{/contentFor}}
|
|
||||||
|
|
||||||
{{!< default}}
|
{{!< default}}
|
||||||
{{! TODO: Add "scrolling" class only when one of the panels is scrolled down by 5px or more }}
|
{{! TODO: Add "scrolling" class only when one of the panels is scrolled down by 5px or more }}
|
||||||
<header>
|
<header>
|
||||||
|
@ -25,13 +14,13 @@
|
||||||
<a class="markdown-help" href="#"><span class="hidden">What is Markdown?</span></a>
|
<a class="markdown-help" href="#"><span class="hidden">What is Markdown?</span></a>
|
||||||
</header>
|
</header>
|
||||||
<section class="entry-markdown-content">
|
<section class="entry-markdown-content">
|
||||||
<textarea id="entry-markdown">{{content}}</textarea>
|
<textarea id="entry-markdown"></textarea>
|
||||||
</section>
|
</section>
|
||||||
</section>{{!.entry-markdown}}
|
</section>{{!.entry-markdown}}
|
||||||
|
|
||||||
<section class="entry-preview">
|
<section class="entry-preview">
|
||||||
<header class="floatingheader">
|
<header class="floatingheader">
|
||||||
Preview <span class="entry-word-count">0 words</span>
|
Preview <span class="entry-word-count js-entry-word-count">0 words</span>
|
||||||
</header>
|
</header>
|
||||||
<section class="entry-preview-content">
|
<section class="entry-preview-content">
|
||||||
<div class="rendered-markdown">
|
<div class="rendered-markdown">
|
||||||
|
@ -53,13 +42,13 @@
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button id="entry-settings" href="#" class="button-link"><span class="hidden">Settings</span></button>
|
<button id="entry-settings" href="#" class="button-link"><span class="hidden">Settings</span></button>
|
||||||
<section id="entry-actions" class="splitbutton-save">
|
<section id="entry-actions" class="splitbutton-save">
|
||||||
<button type="button" class="button-save" data-state="save-draft">Save Draft</button>
|
<button type="button" class="button-save js-post-button"></button>
|
||||||
<a class="options up" href="#"><span class="hidden">Options</span></a>
|
<a class="options up" href="#"><span class="hidden">Options</span></a>
|
||||||
<ul class="editor-options overlay" style="display:none">
|
<ul class="editor-options overlay" style="display:none">
|
||||||
<li data-title="publish-now" data-url=""><a href="#">Publish Now</a></li>
|
<li data-set-status="published"><a href="#">Publish Now</a></li>
|
||||||
<li data-title="queue" data-url=""><a href="#">Add to Queue</a></li>
|
<li data-set-status="queue"><a href="#">Add to Queue</a></li>
|
||||||
<li data-title="publish-on" data-url=""><a href="#">Publish on...</a></li>
|
<li data-set-status="publish-on"><a href="#">Publish on...</a></li>
|
||||||
<li data-title="save-draft" data-url="" class="active"><a href="#">Save Draft</a></li>
|
<li data-set-status="draft"><a href="#">Save Draft</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response
|
// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response
|
||||||
requestHandler = function (apiMethod) {
|
requestHandler = function (apiMethod) {
|
||||||
return function (req, res) {
|
return function (req, res) {
|
||||||
var options = _.extend(req.body, req.params);
|
var options = _.extend(req.body, req.query, req.params);
|
||||||
return apiMethod(options).then(function (result) {
|
return apiMethod(options).then(function (result) {
|
||||||
res.json(result || {});
|
res.json(result || {});
|
||||||
}, function (error) {
|
}, function (error) {
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
/*global require, module */
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// We should just be able to require bookshelf and have it reference
|
|
||||||
// the `Knex` instance bootstraped at the app initialization.
|
|
||||||
var Bookshelf = require('bookshelf'),
|
|
||||||
Showdown = require('showdown'),
|
|
||||||
converter = new Showdown.converter(),
|
|
||||||
|
|
||||||
Post,
|
|
||||||
Posts,
|
|
||||||
User,
|
|
||||||
Users,
|
|
||||||
Setting,
|
|
||||||
Settings;
|
|
||||||
|
|
||||||
Post = Bookshelf.Model.extend({
|
|
||||||
|
|
||||||
tableName: 'posts',
|
|
||||||
|
|
||||||
hasTimestamps: true,
|
|
||||||
|
|
||||||
initialize: function () {
|
|
||||||
this.on('creating', this.creating, this);
|
|
||||||
this.on('saving', this.saving, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
saving: function () {
|
|
||||||
if (!this.get('title')) {
|
|
||||||
throw new Error('Post title cannot be blank');
|
|
||||||
}
|
|
||||||
this.set('content_html', converter.makeHtml(this.get('content')));
|
|
||||||
|
|
||||||
// refactoring of ghost required in order to make these details available here
|
|
||||||
// this.set('language', this.get('language') || ghost.config().defaultLang);
|
|
||||||
// this.set('status', this.get('status') || ghost.statuses().draft);
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
creating: function () {
|
|
||||||
if (!this.get('slug')) {
|
|
||||||
this.generateSlug();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
generateSlug: function () {
|
|
||||||
return this.set('slug', this.get('title').replace(/\:/g, '').replace(/\s/g, '-').toLowerCase());
|
|
||||||
},
|
|
||||||
|
|
||||||
user: function () {
|
|
||||||
return this.belongsTo(User, 'created_by');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Posts = Bookshelf.Collection.extend({
|
|
||||||
|
|
||||||
model: Post
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
User = Bookshelf.Model.extend({
|
|
||||||
tableName: 'users',
|
|
||||||
hasTimestamps: true,
|
|
||||||
posts: function () {
|
|
||||||
return this.hasMany(Posts, 'created_by');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Users = Bookshelf.Collection.extend({
|
|
||||||
|
|
||||||
model: User
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Setting = Bookshelf.Model.extend({
|
|
||||||
tableName: 'settings',
|
|
||||||
hasTimestamps: true
|
|
||||||
});
|
|
||||||
|
|
||||||
Settings = Bookshelf.Collection.extend({
|
|
||||||
model: Setting
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Post: Post,
|
|
||||||
Posts: Posts,
|
|
||||||
User: User,
|
|
||||||
Users: Users,
|
|
||||||
Setting: Setting,
|
|
||||||
Settings: Settings
|
|
||||||
};
|
|
||||||
}());
|
|
|
@ -29,6 +29,8 @@
|
||||||
"grunt-shell": "~0.2.2",
|
"grunt-shell": "~0.2.2",
|
||||||
"grunt-contrib-sass": "~0.3.0",
|
"grunt-contrib-sass": "~0.3.0",
|
||||||
"sinon": "~1.7.2",
|
"sinon": "~1.7.2",
|
||||||
"mocha": "~1.10.0"
|
"mocha": "~1.10.0",
|
||||||
|
"grunt-contrib-handlebars": "~0.5.9",
|
||||||
|
"grunt-contrib-watch": "~0.4.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue