separating deardesi/desirae
This commit is contained in:
parent
332b9e2125
commit
ef8d7b5796
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
// Declare app level module which depends on views, and components
|
||||
angular.module('myApp', [
|
||||
'ngRoute',
|
||||
'myApp.about',
|
||||
'myApp.authors',
|
||||
'myApp.site',
|
||||
'myApp.build',
|
||||
'myApp.configure',
|
||||
'myApp.post',
|
||||
'myApp.version',
|
||||
'myApp.services'
|
||||
]).
|
||||
config(['$routeProvider', function ($routeProvider) {
|
||||
$routeProvider.otherwise({redirectTo: '/about'});
|
||||
}]);
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "deardesi",
|
||||
"version": "1.0.0",
|
||||
"authors": [
|
||||
"AJ ONeal <awesome@coolaj86.com>"
|
||||
],
|
||||
"description": "A blogging platform in the browser. Wow!",
|
||||
"main": "deardesi.js",
|
||||
"moduleType": [
|
||||
"globals",
|
||||
"node"
|
||||
],
|
||||
"keywords": [
|
||||
"dear",
|
||||
"desi",
|
||||
"deardesi",
|
||||
"blog",
|
||||
"blogging",
|
||||
"platform",
|
||||
"browser"
|
||||
],
|
||||
"license": "Apache2",
|
||||
"homepage": "http://github.com/coolaj86/deardesi",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"mustache": "~0.8.2",
|
||||
"bluebird": "~2.6.2",
|
||||
"rsvp": "~3.0.16",
|
||||
"escape-string-regexp": "~1.0.2",
|
||||
"js-yaml": "~3.2.5",
|
||||
"path": "~3.46.1",
|
||||
"forEachAsync": "~5.0.5",
|
||||
"node-uuid": "~1.4.2",
|
||||
"markdown-it": "~3.0.2",
|
||||
"angular": "~1.3.8",
|
||||
"angular-route": "~1.3.8",
|
||||
"html5-boilerplate": "~4.3.0",
|
||||
"bootstrap": "~3.3.1",
|
||||
"md5": "~0.1.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"bluebird": "~2.6.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
angular.module('myApp.services', []).
|
||||
factory('Desirae', ['$q', function($q) {
|
||||
var Desi = window.Desi || require('./deardesi').Desi
|
||||
, desi = {}
|
||||
, fsapi = window.fsapi
|
||||
;
|
||||
|
||||
return {
|
||||
reset: function () {
|
||||
desi = {};
|
||||
}
|
||||
, toDesiDate: Desi.toLocaleDate
|
||||
, meta: function () {
|
||||
var d = $q.defer()
|
||||
;
|
||||
|
||||
if (desi.meta) {
|
||||
d.resolve(desi);
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
Desi.init(desi).then(function () {
|
||||
d.resolve(desi);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
, build: function (env) {
|
||||
var d = $q.defer()
|
||||
;
|
||||
|
||||
if (desi.built) {
|
||||
d.resolve(desi);
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
Desi.buildAll(desi, env).then(function () {
|
||||
d.resolve(desi);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
, write: function (env) {
|
||||
var d = $q.defer()
|
||||
;
|
||||
|
||||
if (desi.written) {
|
||||
d.resolve(desi);
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
Desi.write(desi, env).then(function () {
|
||||
d.resolve(desi);
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
}
|
||||
, putFiles: function (files) {
|
||||
return $q.when(fsapi.putFiles(files));
|
||||
}
|
||||
};
|
||||
}]
|
||||
);
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.version.interpolate-filter', [])
|
||||
|
||||
.filter('interpolate', ['version', function(version) {
|
||||
return function(text) {
|
||||
return String(text).replace(/\%VERSION\%/mg, version);
|
||||
};
|
||||
}]);
|
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
describe('myApp.version module', function() {
|
||||
beforeEach(module('myApp.version'));
|
||||
|
||||
describe('interpolate filter', function() {
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('version', 'TEST_VER');
|
||||
}));
|
||||
|
||||
it('should replace VERSION', inject(function(interpolateFilter) {
|
||||
expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after');
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.version.version-directive', [])
|
||||
|
||||
.directive('appVersion', ['version', function(version) {
|
||||
return function(scope, elm, attrs) {
|
||||
elm.text(version);
|
||||
};
|
||||
}]);
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
describe('myApp.version module', function() {
|
||||
beforeEach(module('myApp.version'));
|
||||
|
||||
describe('app-version directive', function() {
|
||||
it('should print current version', function() {
|
||||
module(function($provide) {
|
||||
$provide.value('version', 'TEST_VER');
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
var element = $compile('<span app-version></span>')($rootScope);
|
||||
expect(element.text()).toEqual('TEST_VER');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.version', [
|
||||
'myApp.version.interpolate-filter',
|
||||
'myApp.version.version-directive'
|
||||
])
|
||||
|
||||
.value('version', '0.8.0');
|
|
@ -0,0 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
describe('myApp.version module', function() {
|
||||
beforeEach(module('myApp.version'));
|
||||
|
||||
describe('version service', function() {
|
||||
it('should return current version', inject(function(version) {
|
||||
expect(version).toEqual('0.1');
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Welcome to Dear Desi! <small ng-bind="'(v%VERSION%)' | interpolate"></small></h1>
|
||||
</div>
|
||||
|
||||
<div class="jumbotron">
|
||||
<div class="row">
|
||||
<div class="col-lg-7">
|
||||
<p>Setup your new blog in just 5 minutes.</p>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-7">
|
||||
<br/>
|
||||
<iframe width="560" height="315" src="//www.youtube.com/embed/YZzhIIJmlE0" frameborder="0" allowfullscreen></iframe>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<h2>Dear Desi is...</h2>
|
||||
<br/>
|
||||
<ul>
|
||||
<li>Builds in the Browser
|
||||
<ul>
|
||||
<li>The in-browser static blog generator</li>
|
||||
<li>Write content in Markdown, Jade, or HTML</li>
|
||||
<li>Mustache Templates</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Git, Dropbox, or regular Folders for management</li>
|
||||
<li>Go headless with Node.js support</li>
|
||||
<li>Dual Licensed Apache2 and MIT</li>
|
||||
<li>No Ruby version Hell - it'll still work in 6 months! :-D</li>
|
||||
</ul>
|
||||
<h3>What are you waiting for?</h3>
|
||||
<br/>
|
||||
<a class="btn btn-primary btn-lg pull-right" href="/#authors">Get Started</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.about', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/about', {
|
||||
templateUrl: 'views/about/about.html',
|
||||
controller: 'AboutCtrl'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('AboutCtrl', [function() {
|
||||
|
||||
}]);
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
describe('myApp.view2 module', function() {
|
||||
|
||||
beforeEach(module('myApp.view2'));
|
||||
|
||||
describe('view2 controller', function(){
|
||||
|
||||
it('should ....', inject(function($controller) {
|
||||
//spec body
|
||||
var view2Ctrl = $controller('View2Ctrl');
|
||||
expect(view2Ctrl).toBeDefined();
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
|
@ -0,0 +1,233 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="page-header">
|
||||
<div class="col-md-5 col-sm-6 col-xs-8">
|
||||
<h1>Primary Author</h1>
|
||||
|
||||
<div class="row" ng-if="Authors.authors">
|
||||
<div class="col-md-offset-1 col-md-8">
|
||||
<div class="form-group">
|
||||
<select
|
||||
ng-options="author as author.filename for (handle, author) in Authors.authors"
|
||||
class="form-control"
|
||||
ng-model="Authors.selectedAuthor"
|
||||
ng-change="Authors.selectAuthor()"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-5 col-sm-6 col-xs-4">
|
||||
<br/>
|
||||
<img style="height:75px;" ng-src="{{Authors.headshot}}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form-horizontal" name="newAuthors" ng-submit="Authors.upsert(Authors.selectedAuthor)">
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<small><span ng-bind="Authors.blogdir"
|
||||
></span>/authors/<span ng-bind="Authors.selectedAuthor.handle"
|
||||
></span><span ng-if="Authors.selectedAuthor.handle"
|
||||
>.yml</span></small>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button class="btn btn-success pull-right" type="submit" ng-disabled="Authors.dirty || !Authors.selectedAuthor.handle">Save & Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<br/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Profile Basics</legend>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorName" class="col-lg-4 control-label">Name*</label>
|
||||
<div class="col-lg-8">
|
||||
<input ng-model="Authors.selectedAuthor.name"
|
||||
required="required"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputAuthorName"
|
||||
name="inputAuthorName"
|
||||
placeholder="i.e. John Doe">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorNickname" class="col-lg-4 control-label">Handle*</label>
|
||||
<div class="col-lg-8">
|
||||
<input ng-model="Authors.selectedAuthor.handle"
|
||||
required="required"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputAuthorNickname"
|
||||
name="inputAuthorNickname"
|
||||
placeholder="i.e. johndoe">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorEmail" class="col-lg-4 control-label">Email*</label>
|
||||
<div class="col-lg-8">
|
||||
<input ng-model="Authors.selectedAuthor.email" ng-change="Authors.updateHeadshotUrl()"
|
||||
required="required"
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="inputAuthorEmail"
|
||||
name="inputAuthorEmail"
|
||||
placeholder="i.e. john.doe@gmail.com">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorWebsite" class="col-lg-4 control-label">Website</label>
|
||||
<div class="col-lg-8">
|
||||
<input ng-model="Authors.selectedAuthor.website"
|
||||
type="url"
|
||||
class="form-control"
|
||||
id="inputAuthorWebsite"
|
||||
name="inputAuthorWebsite"
|
||||
placeholder="i.e. http://johndoe.name"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorBio" class="col-lg-2 control-label">Bio
|
||||
<small>(<span ng-bind="Authors.selectedAuthor.bio.length || 0"></span>/140)</small></label>
|
||||
<div class="col-lg-10">
|
||||
<textarea ng-model="Authors.selectedAuthor.bio"
|
||||
class="form-control" id="inputAuthorBio" placeholder="i.e. Brogrammatic Ninja-throwing Rockstar Badassian Wizard JavaScript Superstar. 3+ years experience as a jalapeno poppers brony. YOLO."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorHeadshot" class="col-lg-2 control-label">Headshot</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.headshot" ng-change="Authors.updateHeadshotUrl()"
|
||||
type="text" class="form-control" id="inputAuthorHeadshot" placeholder="i.e. https://i.imgur.com/qqpxDmJ.jpg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Social</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorTwitter" class="col-lg-2 control-label">Twitter</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.twitter"
|
||||
type="text" class="form-control" id="inputAuthorTwitter" placeholder="i.e. @johndoe">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorFacebook" class="col-lg-2 control-label">Facebook URL</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.facebook"
|
||||
type="text" class="form-control" id="inputAuthorFacebook" placeholder="i.e. facebook.com/johndoe">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorGooglePlus" class="col-lg-2 control-label">Google+ URL</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.googleplus"
|
||||
type="text" class="form-control" id="inputAuthorGooglePlus" placeholder="i.e. plus.google.com/+johndoe">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Developers</legend>
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorGithub" class="col-lg-2 control-label">Github</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.github"
|
||||
type="text" class="form-control" id="inputAuthorGithub" placeholder="i.e. johndoe">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorStackOverflow" class="col-lg-2 control-label">StackOverflow</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.stackoverflow"
|
||||
type="text" class="form-control" id="inputAuthorStackOverflow" placeholder="i.e. http://stackoverflow.com/users/151312/johndoe">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Feeds</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorFeedburner" class="col-lg-2 control-label">Feedburner</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.feedburner"
|
||||
type="text" class="form-control" id="inputAuthorFeedburner" placeholder="i.e. johndoe">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary pull-right" type="submit">Save & Continue</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Instagram
|
||||
Etsy
|
||||
<div class="form-group">
|
||||
<label for="inputAuthorPinterest" class="col-lg-2 control-label">Pinterest</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Authors.selectedAuthor.pinterest"
|
||||
type="text" class="form-control" id="inputAuthorPinterest" placeholder="i.e. @johndoe">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
-->
|
|
@ -0,0 +1,119 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.authors', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/authors', {
|
||||
templateUrl: 'views/authors/authors.html',
|
||||
controller: 'AuthorsCtrl as Authors'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('AuthorsCtrl'
|
||||
, ['$scope', '$timeout', '$location', 'Desirae'
|
||||
, function($scope, $timeout, $location, Desirae) {
|
||||
var scope = this
|
||||
;
|
||||
|
||||
scope.newAuthor = function () {
|
||||
console.log('new author');
|
||||
scope.new = { filename: 'new' };
|
||||
scope.selectAuthor(scope.new);
|
||||
};
|
||||
|
||||
scope.selectAuthor = function (author) {
|
||||
// TODO watch any change
|
||||
scope.selectedAuthor = author || scope.selectedAuthor;
|
||||
scope.updateHeadshotUrlNow();
|
||||
};
|
||||
|
||||
scope.upsert = function () {
|
||||
var author = scope.selectedAuthor
|
||||
, files = []
|
||||
, filename = author.filename
|
||||
;
|
||||
|
||||
delete author.filename;
|
||||
if ('new' !== filename && filename !== author.handle) {
|
||||
files.push({ path: 'authors/' + filename + '.yml', contents: '', delete: true });
|
||||
}
|
||||
files.push({ path: 'authors/' + author.handle + '.yml', contents: window.jsyaml.dump(author) });
|
||||
|
||||
console.log(files);
|
||||
|
||||
Desirae.putFiles(files).then(function (results) {
|
||||
console.log('updated author', results);
|
||||
$location.path('/site');
|
||||
}).catch(function (e) {
|
||||
author.filename = filename;
|
||||
console.error(e);
|
||||
window.alert("Error Nation! :/");
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
scope.updateHeadshotUrlNow = function () {
|
||||
var gravatar = 'http://www.gravatar.com/avatar/' + window.md5((scope.selectedAuthor.email||'foo').toLowerCase()) + '?d=identicon'
|
||||
;
|
||||
|
||||
if (scope.selectedAuthor.headshot) {
|
||||
scope.headshot = scope.selectedAuthor.headshot;
|
||||
}
|
||||
else if (scope.selectedAuthor.email) {
|
||||
scope.headshot = gravatar;
|
||||
}
|
||||
else {
|
||||
scope.headshot = 'http://www.gravatar.com/avatar/' + window.md5((scope.selectedAuthor.email||'foo').toLowerCase()) + '?d=mm';
|
||||
}
|
||||
};
|
||||
|
||||
scope.updateHeadshotUrl = function () {
|
||||
$timeout.cancel(scope.hslock);
|
||||
scope.hslock = $timeout(function () {
|
||||
scope.updateHeadshotUrlNow();
|
||||
}, 300);
|
||||
};
|
||||
|
||||
function init() {
|
||||
scope.newAuthor();
|
||||
|
||||
console.log('desi loading');
|
||||
Desirae.meta().then(function (desi) {
|
||||
var filename
|
||||
;
|
||||
|
||||
scope.blogdir = desi.blogdir.path.replace(/^\/(Users|home)\/[^\/]+\//, '~/');
|
||||
desi.authors = desi.authors || {};
|
||||
desi.authors.new = scope.new;
|
||||
scope.authors = desi.authors;
|
||||
|
||||
Object.keys(desi.authors).forEach(function (filename) {
|
||||
if ('new' === filename) {
|
||||
return;
|
||||
}
|
||||
desi.authors[filename].filename = filename;
|
||||
desi.authors[filename].handle = desi.authors[filename].handle || filename;
|
||||
});
|
||||
|
||||
filename = Object.keys(desi.authors)[0];
|
||||
scope.selectedAuthor = desi.authors[filename];
|
||||
|
||||
scope.updateHeadshotUrlNow();
|
||||
}).catch(function (e) {
|
||||
window.alert("An Error Occured. Most errors that occur in the init phase are parse errors in the config files or permissions errors on files or directories, but check the error console for details.");
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
/*
|
||||
$scope.$watch(angular.bind(this, function () { return this.selectedAuthor; }), function (newValue, oldValue) {
|
||||
//$scope.$watch('Authors.selecteAuthor', function (newValue, oldValue)
|
||||
console.log(newValue, oldValue);
|
||||
if(newValue !== oldValue) {
|
||||
scope.dirty = true;
|
||||
}
|
||||
}, true);
|
||||
*/
|
||||
}]);
|
|
@ -0,0 +1,119 @@
|
|||
<div class="container">
|
||||
<form ng-submit="Post.upsert()" class="form-horizontal">
|
||||
<div class="row">
|
||||
<div class="page-header">
|
||||
<h1>Build Static Site</h1>
|
||||
<h3><span ng-bind="Build.blogdir"></span></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h2>Are you ready?</h2>
|
||||
<p>
|
||||
<small><a href="https://www.youtube.com/watch?v=-E0oiKjLzTc" target="_blank">Bonesaw</a> is READY!</small>
|
||||
</p>
|
||||
<p>
|
||||
<button
|
||||
ng-click="Build.build(['production', 'development'])"
|
||||
class="btn btn-danger"
|
||||
type="button"
|
||||
>Build Sites</button>
|
||||
</p>
|
||||
<span class="help-block">Push the RED button... you know you want to!</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Production</legend>
|
||||
<p><a ng-href="{{Build.production_url}}" target="_blank"><span ng-bind="Build.production_url"></span></a></p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputProdCanonicalUrl" class="col-lg-2 control-label">Canonical URL</label>
|
||||
<div class="col-lg-10">
|
||||
<input ng-model="Build.production_url"
|
||||
required="required"
|
||||
placeholder="i.e. https://example.com/myblog"
|
||||
type="url"
|
||||
class="form-control"
|
||||
id="inputProdCanonicalUrl">
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputProdOutput" class="col-lg-2 control-label">Output Path</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
disabled
|
||||
ng-value="Build.blogdir + '/compiled'"
|
||||
type="text" class="form-control" id="inputProdOutput" disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-lg-10">
|
||||
<!-- TODO progress bar -->
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<button
|
||||
ng-click="Build.build(['production'])"
|
||||
class="btn btn-primary pull-right"
|
||||
type="button"
|
||||
>Build Production Site</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Development</legend>
|
||||
<p><a ng-href="{{Build.development_url}}" target="_blank"><span ng-bind="Build.development_url"></span></a></p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDevCanonicalUrl" class="col-lg-2 control-label">Canonical URL</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
ng-model="Build.development_url"
|
||||
required="required"
|
||||
disabled
|
||||
placeholder="i.e. https://example.com/myblog"
|
||||
type="url"
|
||||
class="form-control"
|
||||
id="inputDevCanonicalUrl">
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDevOutput" class="col-lg-2 control-label">Output Path</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
disabled
|
||||
ng-value="Build.blogdir + '/compiled_dev'"
|
||||
type="text" class="form-control" id="inputDevOutput" disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-lg-10">
|
||||
<!-- TODO progress bar -->
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<button
|
||||
ng-click="Build.build(['development'])"
|
||||
class="btn btn-primary pull-right"
|
||||
type="button"
|
||||
>Build Development Site</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,91 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.build', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/build', {
|
||||
templateUrl: 'views/build/build.html',
|
||||
controller: 'BuildCtrl as Build'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('BuildCtrl'
|
||||
, ['$scope', '$location', '$timeout', 'Desirae'
|
||||
, function ($scope, $location, $timeout, DesiraeService) {
|
||||
var scope = this
|
||||
, path = window.path
|
||||
;
|
||||
|
||||
function init() {
|
||||
console.log('desi loading');
|
||||
DesiraeService.meta().then(function (desi) {
|
||||
scope.blogdir = desi.blogdir.path.replace(/^\/(Users|home)\/[^\/]+\//, '~/');
|
||||
scope.site = desi.site;
|
||||
|
||||
console.log(desi.site.base_url);
|
||||
console.log(desi.site.base_path);
|
||||
scope.production_url = desi.site.base_url + path.join('/', desi.site.base_path);
|
||||
console.log(scope.production_url);
|
||||
|
||||
// this is the responsibility of the build system (Dear Desi), not the library (Desirae)
|
||||
scope.development_url = location.href.replace(/\/(#.*)?$/, '') + path.join('/', 'compiled_dev');
|
||||
console.log(scope.development_url);
|
||||
|
||||
}).catch(function (e) {
|
||||
window.alert("An Error Occured. Most errors that occur in the init phase are parse errors in the config files or permissions errors on files or directories, but check the error console for details.");
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
||||
|
||||
scope.extensions = ['md', 'html'];
|
||||
}
|
||||
|
||||
scope.onError = function (e) {
|
||||
console.error(e);
|
||||
if (window.confirm("Encountered an error. Please inspect the console.\n\nWould you like to ignore the error and continue?")) {
|
||||
return window.Promise.resolve();
|
||||
} else {
|
||||
return window.Promise.reject();
|
||||
}
|
||||
};
|
||||
|
||||
scope.buildOne = function (envstr) {
|
||||
var env
|
||||
;
|
||||
|
||||
// TODO is there a legitimate case where in addition to base_path (root of the blog)
|
||||
// a user would need owner_base? i.e. school.edu/~/rogers/blog school.edu/~/rogers/assets
|
||||
if ('production' === envstr) {
|
||||
env = {
|
||||
url: scope.production_url
|
||||
, base_url: scope.development_url.replace(/(https?:\/\/[^\/#?]+)/, '$1')
|
||||
, compiled_path: 'compiled'
|
||||
, since: 0
|
||||
, onError: scope.onError
|
||||
};
|
||||
} else {
|
||||
env = {
|
||||
url: scope.development_url
|
||||
, base_url: scope.development_url.replace(/(https?:\/\/[^\/#?]+)/, '$1')
|
||||
, base_path: scope.development_url.replace(/https?:\/\/[^\/#?]+/, '')
|
||||
, compiled_path: 'compiled_dev'
|
||||
, since: 0
|
||||
, onError: scope.onError
|
||||
};
|
||||
}
|
||||
|
||||
return DesiraeService.build(env).then(function () {
|
||||
DesiraeService.write(env);
|
||||
});
|
||||
};
|
||||
|
||||
scope.build = function (envs) {
|
||||
window.forEachAsync(envs, function (env) {
|
||||
return scope.buildOne(env);
|
||||
}).then(function () {
|
||||
window.alert('Build(s) Complete');
|
||||
});
|
||||
};
|
||||
|
||||
init();
|
||||
}]);
|
|
@ -0,0 +1,72 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="page-header">
|
||||
<h1>Site Configuration</h1>
|
||||
<h3><span ng-bind="Site.blogdir"></span></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form-horizontal">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Advanced</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputRootConf" class="col-lg-2 control-label">Root Pages</label>
|
||||
<div class="col-lg-8">
|
||||
<input type="text" class="form-control" id="inputRootConf" placeholder="i.e. images">
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<button class="btn" type="button">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputThemesConf" class="col-lg-2 control-label">Themes</label>
|
||||
<div class="col-lg-8">
|
||||
<input type="text" class="form-control" id="inputThemesConf" placeholder="i.e. images">
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<button class="btn" type="button">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputCollectionsConf" class="col-lg-2 control-label">Collections</label>
|
||||
<div class="col-lg-8">
|
||||
<input type="text" class="form-control" id="inputCollectionsConf" placeholder="i.e. images">
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<button class="btn" type="button">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputAssetsConf" class="col-lg-2 control-label">Assets</label>
|
||||
<div class="col-lg-8">
|
||||
<input type="text" class="form-control" id="inputAssetsConf" placeholder="i.e. images">
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<button class="btn" type="button">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputWidgetsConf" class="col-lg-2 control-label">Widgets</label>
|
||||
<div class="col-lg-8">
|
||||
<input type="text" class="form-control" id="inputWidgetsConf" placeholder="i.e. images">
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<button class="btn" type="button">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.configure', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/configure', {
|
||||
templateUrl: 'views/configure/configure.html',
|
||||
controller: 'ConfigureCtrl as Configure'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('ConfigureCtrl', [function() {
|
||||
}]);
|
|
@ -0,0 +1,83 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="page-header">
|
||||
<h1>Blog Configuration</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form-horizontal">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail" class="col-lg-2 control-label">Email</label>
|
||||
<div class="col-lg-10">
|
||||
<input type="text" class="form-control" id="inputEmail" placeholder="Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputPassword" class="col-lg-2 control-label">Password</label>
|
||||
<div class="col-lg-10">
|
||||
<input type="password" class="form-control" id="inputPassword" placeholder="Password">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"> Checkbox
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="textArea" class="col-lg-2 control-label">Textarea</label>
|
||||
<div class="col-lg-10">
|
||||
<textarea class="form-control" rows="3" id="textArea"></textarea>
|
||||
<span class="help-block">A longer block of help text that breaks onto a new line and may extend beyond one line.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-2 control-label">Radios</label>
|
||||
<div class="col-lg-10">
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked="">
|
||||
Option one is this
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="optionsRadios" id="optionsRadios2" value="option2">
|
||||
Option two can be something else
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="select" class="col-lg-2 control-label">Selects</label>
|
||||
<div class="col-lg-10">
|
||||
<select class="form-control" id="select">
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</select>
|
||||
<br>
|
||||
<select multiple="" class="form-control">
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-10 col-lg-offset-2">
|
||||
<button class="btn btn-default">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div id="source-button" class="btn btn-primary btn-xs" style="display: none;">< ></div></div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
<div class="container">
|
||||
<form ng-submit="Post.upsert()" class="form-horizontal">
|
||||
<div class="row">
|
||||
<div class="page-header">
|
||||
<h1>Write a Post</h1>
|
||||
<h3><span ng-bind="Post.selected.abspath" ></span></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label for="inputPostTitle" class="col-lg-2 control-label">Title*</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
required="required"
|
||||
ng-model="Post.selected.post.yml.title"
|
||||
ng-change="Post.onChange()"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputPostTitle"
|
||||
placeholder="i.e. My First Post"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<div class="form-group">
|
||||
<label for="inputPostUuid" class="col-lg-2 control-label">UUID</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
disabled
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputPostUuid"
|
||||
placeholder="ERROR"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="form-group">
|
||||
<label for="textAreaPost" class="col-lg-2 control-label">Post*</label>
|
||||
<div class="col-lg-10">
|
||||
<textarea
|
||||
required="required"
|
||||
ng-model="Post.selected.post.body"
|
||||
ng-change="Post.onChange()"
|
||||
class="form-control"
|
||||
rows="20"
|
||||
id="textAreaPost"
|
||||
placeholder="i.e. I Don't Know Anything About the Gold Standard... But I Do Love Little Kittens!"
|
||||
></textarea>
|
||||
<span class="help-block">Put your lovely post here, in github-flavored markdown!</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="select" class="col-lg-2 control-label">Format*</label>
|
||||
<div class="col-lg-10">
|
||||
<select
|
||||
required="required"
|
||||
ng-model="Post.selected.format"
|
||||
ng-change="Post.onChange()"
|
||||
class="form-control"
|
||||
id="select">
|
||||
<option value="html">HTML</option>
|
||||
<option selected="selected" value="md">Markdown</option>
|
||||
<option disabled="disabled" value="jade">Jade (Not Implemented)</option>
|
||||
</select>
|
||||
<!--div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox"> Draft
|
||||
</label>
|
||||
</div-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="textAreaDesc" class="col-lg-2 control-label">Description*
|
||||
<small>(<span ng-bind="Post.selected.post.yml.description.length || 0"></span>/140)</small></label>
|
||||
<div class="col-lg-10">
|
||||
<textarea
|
||||
required="required"
|
||||
ng-change="Post.onChange()"
|
||||
ng-model="Post.selected.post.yml.description"
|
||||
placeholder="i.e. An alternate recipe for Peeta Mellarks's famous apple goat cheese tarts using only ingredients available in district 10"
|
||||
class="form-control"
|
||||
rows="2"
|
||||
id="textAreaDesc"></textarea>
|
||||
<span class="help-block">The description is often used by search engines as the snippit shown in search results.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="textAreaYaml" class="col-lg-2 control-label">Frontmatter</label>
|
||||
<div class="col-lg-10">
|
||||
<textarea
|
||||
required="required"
|
||||
ng-change="Post.onFrontmatterChange()"
|
||||
ng-model="Post.selected.post.frontmatter"
|
||||
class="form-control"
|
||||
rows="5"
|
||||
id="textAreaYaml"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputPostAbsPath" class="col-lg-2 control-label">Filepath</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
required="required"
|
||||
disabled
|
||||
ng-model="Post.selected.abspath"
|
||||
ng-change="Post.onChange()"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputPostAbsPath"
|
||||
placeholder="i.e. ~/blog.me.co/posts/my-first-post.md"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="form-group">
|
||||
<label for="select" class="col-lg-2 control-label">Selects</label>
|
||||
<div class="col-lg-10">
|
||||
<select class="form-control" id="select">
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</select>
|
||||
</div>
|
||||
TODO theme -->
|
||||
<!-- TODO layout -->
|
||||
<!-- TODO swatch -->
|
||||
|
||||
<!--
|
||||
<div class="form-group">
|
||||
<label for="inputPostDate" class="col-lg-2 control-label">Date</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
disabled
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputPostDate"
|
||||
ng-placeholder="i.e. {{Post.year}}-{{Post.month}}-{{Post.day}} {{Post.hour}}:{{Post.minute}}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!--
|
||||
<div class="form-group">
|
||||
<label for="inputPostPermalink" class="col-lg-2 control-label">Permalink</label>
|
||||
<div class="col-lg-10">
|
||||
<input
|
||||
required="required"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputPostPermalink"
|
||||
placeholder="i.e. /articles/my-first-post.html"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-lg-10 col-lg-offset-2">
|
||||
<!--button class="btn btn-default">Save Draft</button-->
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary pull-right"
|
||||
>Publish</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div id="source-button" class="btn btn-primary btn-xs" style="display: none;">< ></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,165 @@
|
|||
'use strict';
|
||||
angular.module('myApp.post', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/post', {
|
||||
templateUrl: 'views/post/post.html',
|
||||
controller: 'PostCtrl as Post'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('PostCtrl'
|
||||
, ['$scope', '$location', '$timeout', 'Desirae'
|
||||
, function ($scope, $location, $timeout, Desirae) {
|
||||
var scope = this
|
||||
;
|
||||
|
||||
function init() {
|
||||
console.log('desi loading');
|
||||
Desirae.meta().then(function (desi) {
|
||||
scope.blogdir = desi.blogdir.path.replace(/^\/(Users|home)\/[^\/]+\//, '~/');
|
||||
scope.site = desi.site;
|
||||
newPost();
|
||||
|
||||
updateDate();
|
||||
}).catch(function (e) {
|
||||
window.alert("An Error Occured. Most errors that occur in the init phase are parse errors in the config files or permissions errors on files or directories, but check the error console for details.");
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
||||
|
||||
scope.extensions = ['md', 'html'];
|
||||
}
|
||||
|
||||
function newPost() {
|
||||
scope.selected = {
|
||||
format: 'md'
|
||||
, permalink: "/article/new.md"
|
||||
, uuid: window.uuid.v4()
|
||||
, abspath: scope.blogdir
|
||||
, post: {
|
||||
yml: {
|
||||
title: ""
|
||||
, permalink: "/article/new.md"
|
||||
, date: Desirae.toDesiDate(new Date())// "YYYY-MM-DD HH:MM pm" // TODO desirae
|
||||
, updated: null
|
||||
, description: ""
|
||||
, categories: []
|
||||
, tags: []
|
||||
, theme: null
|
||||
, layout: null
|
||||
, swatch: null
|
||||
}
|
||||
}
|
||||
};
|
||||
scope.selected.date = scope.selected.post.yml.date;
|
||||
scope.selected.post.frontmatter = window.jsyaml.dump(scope.selected.post.yml).trim();
|
||||
}
|
||||
|
||||
scope.onChange = function () {
|
||||
var post = scope.selected.post
|
||||
, selected = scope.selected
|
||||
;
|
||||
|
||||
post.yml.title = post.yml.title || '';
|
||||
post.yml.description = post.yml.description || '';
|
||||
|
||||
if (selected.permalink === post.yml.permalink) {
|
||||
selected.permalink = '/articles/' + post.yml.title.toLowerCase()
|
||||
.replace(/["']/g, '')
|
||||
.replace(/\W/g, '-')
|
||||
.replace(/^-+/g, '')
|
||||
.replace(/-+$/g, '')
|
||||
.replace(/--/g, '-')
|
||||
+ '.' + selected.format
|
||||
;
|
||||
|
||||
post.yml.permalink = selected.permalink;
|
||||
}
|
||||
if (window.path.extname(post.yml.permalink) !== '.' + selected.format) {
|
||||
post.yml.permalink = post.yml.permalink.replace(/\.\w+$/, '.' + selected.format);
|
||||
}
|
||||
|
||||
post.frontmatter = window.jsyaml.dump(post.yml).trim();
|
||||
|
||||
// TODO use some sort of filepath pattern in config.yml
|
||||
selected.path = window.path.join((selected.collection || 'posts'), window.path.basename(post.yml.permalink));
|
||||
selected.abspath = window.path.join(scope.blogdir, selected.path);
|
||||
};
|
||||
scope.onFrontmatterChange = function () {
|
||||
var data
|
||||
;
|
||||
|
||||
try {
|
||||
if (!scope.selected.post.frontmatter || !scope.selected.post.frontmatter.trim()) {
|
||||
throw new Error('deleted frontmatter');
|
||||
}
|
||||
data = window.jsyaml.load(scope.selected.post.frontmatter);
|
||||
scope.selected.format = data.permalink.replace(/.*\.(\w+$)/, '$1');
|
||||
if (!data.permalink) {
|
||||
data = scope.selected.permalink;
|
||||
}
|
||||
scope.selected.post.yml = data;
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
console.error('ignoring update that created parse error');
|
||||
scope.selected.post.frontmatter = window.jsyaml.dump(scope.selected.post.yml).trim();
|
||||
}
|
||||
};
|
||||
|
||||
function updateDate() {
|
||||
$timeout.cancel(scope.dtlock);
|
||||
scope.dtlock = $timeout(function () {
|
||||
if (scope.selected && scope.selected.date === scope.selected.post.yml.date) {
|
||||
scope.selected.date = scope.selected.post.yml.date = Desirae.toDesiDate(new Date());
|
||||
}
|
||||
scope.onChange();
|
||||
updateDate();
|
||||
}, 60 * 1000);
|
||||
}
|
||||
|
||||
scope.upsert = function () {
|
||||
console.log('upserted');
|
||||
if (-1 === scope.extensions.indexOf(scope.selected.format)) {
|
||||
window.alert('.' + scope.selected.format + ' is not a supported extension.\n\nPlease choose from: .' + scope.extensions.join(' .'));
|
||||
return;
|
||||
}
|
||||
|
||||
scope.selected.post.yml.uuid = scope.selected.uuid;
|
||||
['updated', 'theme', 'layout', 'swatch'].forEach(function (key) {
|
||||
if (!scope.selected.post.yml[key]) {
|
||||
delete scope.selected.post.yml[key];
|
||||
}
|
||||
});
|
||||
scope.onChange();
|
||||
|
||||
var files = []
|
||||
;
|
||||
|
||||
files.push({
|
||||
path: scope.selected.path
|
||||
, contents:
|
||||
'---\n'
|
||||
+ scope.selected.post.frontmatter.trim()
|
||||
+ '\n'
|
||||
+ '---\n'
|
||||
+ '\n'
|
||||
+ scope.selected.post.body.trim()
|
||||
});
|
||||
|
||||
console.log(files);
|
||||
Desirae.putFiles(files).then(function (results) {
|
||||
console.log('TODO check for error');
|
||||
console.log(results);
|
||||
$location.path('/build');
|
||||
}).catch(function (e) {
|
||||
$timeout.cancel(scope.dtlock);
|
||||
console.error(scope.site);
|
||||
console.error(e);
|
||||
window.alert("Error Nation! :/");
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
init();
|
||||
}]);
|
|
@ -0,0 +1,200 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="page-header">
|
||||
<h1>Site Configuration</h1>
|
||||
<h3><span ng-bind="Site.blogdir"></span><button class="btn btn-primary pull-right" type="submit" form="formSiteConf">Save & Continue</button></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form id="formSiteConf" class="form-horizontal" ng-submit="Site.upsert()">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>General</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputBlogTitle" class="col-lg-3 control-label">Title</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
ng-model="Site.site.title"
|
||||
required="required"
|
||||
type="text" class="form-control" id="inputBlogTitle" placeholder="My Awesome Blog">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputBlogTagline" class="col-lg-3 control-label">Tagline</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
ng-model="Site.site.tagline"
|
||||
type="text" class="form-control" id="inputBlogTagline"
|
||||
placeholder="i.e. I have not failed, I've just found 10,000 ways that do not work.">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="form-group">
|
||||
<label for="inputSiteDesc" class="col-lg-3 control-label">Description
|
||||
<small>(<span ng-bind="Site.site.description.length || 0"></span>/140)</small></label>
|
||||
<div class="col-lg-9">
|
||||
<textarea
|
||||
ng-model="Site.site.description"
|
||||
required="required"
|
||||
class="form-control" id="inputSiteDesc" placeholder="i.e. For tips and tricks for try-hard ethical master cleanses, 3 wolf moons on Tumblr, disruptive lo-fi, preserving narwhals and eating kale chips, etc. YOLO."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- TODO -->
|
||||
<!--
|
||||
<div class="form-group">
|
||||
<label for="inputProdUrl" class="col-lg-3 control-label">URL</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
ng-model="Site.site.url"
|
||||
ng-change="Site.updateUrls()"
|
||||
placeholder="i.e. https://example.com/myblog"
|
||||
type="url"
|
||||
class="form-control"
|
||||
id="inputProdUrl">
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputProdHost" class="col-lg-3 control-label">Base URL</label>
|
||||
<div class="col-lg-9">
|
||||
<input ng-model="Site.site.base_url"
|
||||
required="required"
|
||||
placeholder="i.e. https://example.com in https://example.com/myblog"
|
||||
type="url"
|
||||
class="form-control"
|
||||
id="inputProdHost">
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputProdBase" class="col-lg-3 control-label">Base Path</label>
|
||||
<div class="col-lg-9">
|
||||
<input ng-model="Site.site.base_path"
|
||||
required="required"
|
||||
placeholder="i.e. / for blog.test.com or /blog for test.com/blog"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputProdBase">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputProdOutput" class="col-lg-3 control-label">Output Path</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
ng-value="Site.blogdir + '/compiled'"
|
||||
type="text" class="form-control" id="inputProdOutput" disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Development</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDevHost" class="col-lg-3 control-label">Base URL</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
value="https://local.dear.desi:65080"
|
||||
type="url"
|
||||
class="form-control"
|
||||
id="inputDevHost"
|
||||
disabled>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDevBase" class="col-lg-3 control-label">Base Path</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
value="/compiled_dev"
|
||||
placeholder="i.e. /blog in https://example.com/blog"
|
||||
type="text" class="form-control" id="inputDevBase"
|
||||
disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDevOutput" class="col-lg-3 control-label">Output Path</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
ng-value="Site.blogdir + '/compiled_dev'"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputDevOutput"
|
||||
disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="well bs-component">
|
||||
<fieldset>
|
||||
<legend>Plugins</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputGoogleAnalyticsId" class="col-lg-3 control-label">Google Analytics ID</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
ng-model="Site.site.google_analytics_tracking_id"
|
||||
placeholder="i.e. UA-XXXYYYZZ-1"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="inputGoogleAnalyticsId"
|
||||
>
|
||||
<br/>
|
||||
<small>Found in the Admin section of Google Analytics</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDisqusShortname" class="col-lg-3 control-label">Disqus Shortname</label>
|
||||
<div class="col-lg-9">
|
||||
<input
|
||||
ng-model="Site.site.disqus_shortname"
|
||||
placeholder="i.e. johndoe-blog"
|
||||
type="text" class="form-control" id="inputDisqusShortname"
|
||||
>
|
||||
<br/>
|
||||
<small>Found under Admin > Settings in Disqus</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary pull-right" type="submit">Save & Continue</button>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,49 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('myApp.site', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/site', {
|
||||
templateUrl: 'views/site/site.html',
|
||||
controller: 'SiteCtrl as Site'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('SiteCtrl', ['$scope', '$location', 'Desirae', function ($scope, $location, Desirae) {
|
||||
var scope = this
|
||||
;
|
||||
|
||||
|
||||
function init() {
|
||||
console.log('desi loading');
|
||||
Desirae.meta().then(function (desi) {
|
||||
scope.blogdir = desi.blogdir.path.replace(/^\/(Users|home)\/[^\/]+\//, '~/');
|
||||
scope.site = desi.site;
|
||||
}).catch(function (e) {
|
||||
window.alert("An Error Occured. Most errors that occur in the init phase are parse errors in the config files or permissions errors on files or directories, but check the error console for details.");
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
scope.upsert = function () {
|
||||
var files = []
|
||||
;
|
||||
|
||||
files.push({ path: 'site.yml', contents: scope.site });
|
||||
|
||||
console.log(files);
|
||||
Desirae.putFiles(files).then(function (results) {
|
||||
console.log('TODO check for error');
|
||||
console.log(results);
|
||||
$location.path('/post');
|
||||
}).catch(function (e) {
|
||||
console.error(scope.site);
|
||||
console.error(e);
|
||||
window.alert("Error Nation! :/");
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
init();
|
||||
}]);
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
describe('myApp.view1 module', function() {
|
||||
|
||||
beforeEach(module('myApp.view1'));
|
||||
|
||||
describe('view1 controller', function(){
|
||||
|
||||
it('should ....', inject(function($controller) {
|
||||
//spec body
|
||||
var view1Ctrl = $controller('View1Ctrl');
|
||||
expect(view1Ctrl).toBeDefined();
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue