From 31cd11ad60c2cbe0400346f6a40a0f56e314d4a3 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 11 Jan 2015 04:04:30 -0700 Subject: [PATCH] began setup wizard --- BUGS.md | 19 + README.md | 51 +- app.js | 6 +- bower.json | 3 +- components/desirae/desirae.js | 65 +- deardesi.js | 1063 +++++++++-------- index.html | 13 +- lib/deardesi-browser.js | 111 +- lib/fsapi.js | 3 +- server.js | 44 +- swatches.yml | 36 + views/about/about.html | 74 +- views/authors/authors.html | 233 ++++ views/authors/authors.js | 119 ++ views/build/build.html | 0 views/build/build.js | 16 - views/configure/configure.js | 29 - .../configure.html => site/site.html} | 94 +- views/site/site.js | 26 + views/{configure => site}/view1_test.js | 0 20 files changed, 1321 insertions(+), 684 deletions(-) create mode 100644 BUGS.md create mode 100644 swatches.yml create mode 100644 views/authors/authors.html create mode 100644 views/authors/authors.js create mode 100644 views/build/build.html delete mode 100644 views/configure/configure.js rename views/{configure/configure.html => site/site.html} (54%) create mode 100644 views/site/site.js rename views/{configure => site}/view1_test.js (100%) diff --git a/BUGS.md b/BUGS.md new file mode 100644 index 0000000..22cf1fb --- /dev/null +++ b/BUGS.md @@ -0,0 +1,19 @@ +BUGS +==== + +* index page /index/index.html +* rss feed missing + +Usability +========= + +* compile dev vs prod +* new posts + +Feautres +======== + +* permalink url maker +* tags +* categories +* output to os.tmpdir (i.e. /tmp) diff --git a/README.md b/README.md index d1251ab..286a50d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ Desirae ===== -In development. +A blog platform built for Developers, but with normal people in mind. -Blog Platform. A Ruhoh knock-off written in JavaScript for the Browser +Desirae runs entirely in the browser, but needs a little help from Node.js for saving and retrieving files. + +She can also be run from entirely headless from node.js. Key Features ------------ @@ -18,6 +20,51 @@ Node (optional) - if you'd prefer to go headless, you can. The server is *very* minimal and could easily be implemented in any language (such as ruby or python). +Install and Usage +================= + +If you're on OS X or Linux, it's as easy as pie to install and use Desirae. + +```bash +git clone git@github.com:DearDesi/desirae.git +pushd desirae + +# Downloads and installs node.js and a few other tools Desirae needs +bash setup.sh ./blog +``` + +After the initial installation you can launch Dear Desi, the Web-based configuration and build tool like so: + +``` +deardesi ./blog 65080 +``` + +Or, if you prefer, you can build with `desirae` from the command line: + +``` +desirae build ./blog + +desirae build-dev ./blog +``` + +Create a new Post +----------------- + +``` +desirae post "My First Post" +``` + +Configuration +============= + +There are a few configuration files: + +* `site.yml` is stuff that might be unique to your site, such as (title, url, adwords id, etc) +* `authors/<>` contains information about you (name, handle, facebook, etc) +* `desirae.yml` contains directives that describe *how* the blog should be compiled - more technical stuff. + +If any of these files change, the entire site needs to be retemplated. + Widgets ======= diff --git a/app.js b/app.js index a6d70d6..d2f86c3 100644 --- a/app.js +++ b/app.js @@ -4,8 +4,12 @@ angular.module('myApp', [ 'ngRoute', 'myApp.about', + 'myApp.authors', + 'myApp.site', 'myApp.build', - 'myApp.version' + 'myApp.create', + 'myApp.version', + 'myApp.services' ]). config(['$routeProvider', function ($routeProvider) { $routeProvider.otherwise({redirectTo: '/about'}); diff --git a/bower.json b/bower.json index adbeea0..d17de8f 100644 --- a/bower.json +++ b/bower.json @@ -41,7 +41,8 @@ "angular": "~1.3.8", "angular-route": "~1.3.8", "html5-boilerplate": "~4.3.0", - "bootstrap": "~3.3.1" + "bootstrap": "~3.3.1", + "md5": "~0.1.3" }, "resolutions": { "bluebird": "~2.6.2" diff --git a/components/desirae/desirae.js b/components/desirae/desirae.js index 377a0b7..f580ea2 100644 --- a/components/desirae/desirae.js +++ b/components/desirae/desirae.js @@ -1,9 +1,62 @@ angular.module('myApp.services', []). - factory('MyService', function($http) { - var MyService = {}; - $http.get('resources/data.json').success(function(response) { - MyService.data = response; + factory('Desirae', ['$q', function($q) { + var Desi = window.Desi || require('./deardesi').Desi + , desi = {} + , fsapi = window.fsapi + ; + + return { + reset: function () { + desi = {}; + } + , meta: function () { + var d = $q.defer() + ; + + if (desi.meta) { + d.resolve(desi); + return d.promise; + } + + Desi.init(desi).then(function () { + d.resolve(desi); }); - return MyService; - } + + return d.promise; + } + , build: function (env) { + var d = $q.defer() + ; + + if (desi.built) { + d.resolve(desi); + return d.promise; + } + + Desi.build(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)); + } + }; + }] ); diff --git a/deardesi.js b/deardesi.js index 816ef1d..f5f26d1 100644 --- a/deardesi.js +++ b/deardesi.js @@ -1,8 +1,6 @@ ;(function (exports) { 'use strict'; - //require('require-yaml'); - var PromiseA = exports.Promise || require('bluebird').Promise , path = exports.path || require('path') , Mustache = exports.Mustache || require('mustache') @@ -44,12 +42,14 @@ return str; } + /* function toLocaleDate(d) { return d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + ' ' + (d.getHours() % 12) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds()) ; } + */ function fromLocaleDate(str) { // handles ISO and ISO-ish dates @@ -204,16 +204,144 @@ } } - function runDesi(desi, env) { - var cache = desi.cache - //, config = desi.config - , cacheByPath = {} - , cacheBySha1 = {} - , dfiles - , dthemes - , droot - ; + console.log(''); + console.log(''); + console.info('getting config, data, caches...'); + function clone(obj) { + return JSON.parse(JSON.stringify(obj)); + } + + function Desi() { + } + + // read config and such + Desi.init = function (desi) { + // config.yml, data.yml, site.yml, authors + return PromiseA.all([fsapi.getBlogdir(), fsapi.getAllConfigFiles()]).then(function (plop) { + var blogdir = plop[0] + , arr = plop[1] + ; + + console.info('loaded config, data, caches, partials'); + console.log({ + config: arr.config + , data: arr.data + , site: arr.site + }); + + desi.blogdir = blogdir; + desi.originals = {}; + desi.copies = {}; + + Object.keys(arr).forEach(function (key) { + desi.originals[key] = arr[key]; + desi.copies[key] = clone(arr[key]); + desi[key] = clone(arr[key]); + }); + + desi.config.rootdir = desi.config.rootdir || '_root'; + + var collectionnames = Object.keys(desi.config.collections) + , themenames = Object.keys(desi.config.themes) + .filter(function (k) { return 'default' !== k; }) + //.map(function (n) { return path.join(n, 'layouts'); }) + , assetnames = Object.keys(desi.config.assets) + ; + + // TODO make document configurability + return PromiseA.all([ + fsapi.getMeta( + themenames + , { dotfiles: false + , extensions: ['md', 'markdown', 'htm', 'html', 'jade', 'css', 'js', 'yml'] + } + ) + , fsapi.getMeta( + [desi.config.rootdir] + , { dotfiles: false + , extensions: ['md', 'markdown', 'htm', 'html', 'jade'] + } + ) + , fsapi.getMeta( + collectionnames + , { dotfiles: false + , extensions: ['md', 'markdown', 'htm', 'html', 'jade'] + } + ) + , fsapi.getMeta( + assetnames + , { dotfiles: false + //, extensions: ['md', 'markdown', 'htm', 'html', 'jade', 'css', 'js', 'yml'] + } + ) + , fsapi.getCache() + ]); + }).then(function (things) { + console.info('loaded theme meta, root meta, collection meta'); + console.log({ + theme: things[0] + , root: things[1] + , collection: things[2] + , asset: things[3] + , cache: things[4] + }); + + function noErrors(map) { + Object.keys(map).forEach(function (path) { + map[path] = map[path].filter(function (m) { + if (!m.error && m.size) { + return true; + } + + if (!m.size) { + console.warn("Ignoring 0 byte file " + (m.path || m.name)); + return false; + } + + console.warn("Couldn't get stats for " + (m.path || m.name)); + console.warn(m.error); + }); + }); + + return map; + } + + var themes = noErrors(things[0]) + , root = noErrors(things[1])[desi.config.rootdir] + , collections = noErrors(things[2]) + , assets = noErrors(things[3]) + , cache = noErrors(things[4]) + ; + + if (!themes[Object.keys(themes)[0]].length) { + console.error('Missing THEMES!'); + throw new Error('It seems that your themes directory is missing'); + } + + if (!root.length) { + console.error('Missing ROOT!'); + } + + if (!collections[Object.keys(collections)[0]].length) { + console.error('Missing Collections!'); + } + + console.info('last update: ' + (cache && cache.lastUpdate && new Date(cache.lastUpdate) || 'never')); + desi.cache = cache; + desi.meta = { + themes: themes + , collections: collections + , root: root + , assets: assets + }; + desi.assets = []; + + return desi; + }); + }; + + Desi.setEnv = function (desi, env) { desi.urls = desi.config.urls = {}; desi.env = {}; if (-1 === ['development', 'staging'].indexOf(env) || !desi.config[env]) { @@ -225,6 +353,23 @@ desi.config.compiled_path = desi.config[env].compiled_path; desi.urls[env + '_url'] = desi.config[env].url; + return PromiseA.resolve(desi); + }; + + Desi.getDirtyFiles = function (desi, env) { + var cache = desi.cache + //, config = desi.config + , cacheByPath = {} + , cacheBySha1 = {} + , dfiles + , dthemes + , droot + ; + + if (!desi.env) { + Desi.setEnv(desi, env); + } + cache.sources = cache.sources || []; cache.sources.forEach(function (source) { cacheByPath[source.path] = source; @@ -265,6 +410,7 @@ console.warn(o.error); } + // TODO also retrieve from cache? desi.content = { root: arr[0].filter(noErrors) , collections: arr[1].filter(noErrors) @@ -273,130 +419,9 @@ return desi; }); - } + }; - console.log(''); - console.log(''); - console.info('getting config, data, caches...'); - - function Desi() { - } - Desi.init = function (desi) { - return PromiseA.all([fsapi.getConfig(), fsapi.getData(), fsapi.getCache(), fsapi.getPartials()]).then(function (arr) { - var config = arr[0] - , data = arr[1] - , cache = arr[2] - , partials = arr[3] - , collectionnames = Object.keys(config.collections) - , themenames = Object.keys(config.themes) - .filter(function (k) { return 'default' !== k; }) - //.map(function (n) { return path.join(n, 'layouts'); }) - , assetnames = Object.keys(config.assets) - ; - - console.info('loaded config, data, caches, partials'); - console.log({ - config: arr[0] - , data: arr[1] - , cache: arr[2] - , partials: arr[3] - }); - console.info('last update: ' + (cache.lastUpdate && new Date(cache.lastUpdate) || 'never')); - - // TODO make document configurability - config.rootdir = config.rootdir || '_root'; - return PromiseA.all([ - fsapi.getMeta( - themenames - , { dotfiles: false - , extensions: ['md', 'markdown', 'htm', 'html', 'jade', 'css', 'js', 'yml'] - } - ) - , fsapi.getMeta( - [config.rootdir] - , { dotfiles: false - , extensions: ['md', 'markdown', 'htm', 'html', 'jade'] - } - ) - , fsapi.getMeta( - collectionnames - , { dotfiles: false - , extensions: ['md', 'markdown', 'htm', 'html', 'jade'] - } - ) - , fsapi.getMeta( - assetnames - , { dotfiles: false - //, extensions: ['md', 'markdown', 'htm', 'html', 'jade', 'css', 'js', 'yml'] - } - ) - ]).then(function (things) { - console.info('loaded theme meta, root meta, collection meta'); - console.log({ - theme: things[0] - , root: things[1] - , collection: things[2] - , asset: things[3] - }); - - function noErrors(map) { - Object.keys(map).forEach(function (path) { - map[path] = map[path].filter(function (m) { - if (!m.error && m.size) { - return true; - } - - if (!m.size) { - console.warn("Ignoring 0 byte file " + (m.path || m.name)); - return false; - } - - console.warn("Couldn't get stats for " + (m.path || m.name)); - console.warn(m.error); - }); - }); - - return map; - } - - var themes = noErrors(things[0]) - , root = noErrors(things[1])[config.rootdir] - , collections = noErrors(things[2]) - , assets = noErrors(things[3]) - ; - - if (!themes[Object.keys(themes)[0]].length) { - console.error('Missing THEMES!'); - throw new Error('It seems that your themes directory is missing'); - } - - if (!root.length) { - console.error('Missing ROOT!'); - } - - if (!collections[Object.keys(collections)[0]].length) { - console.error('Missing Collections!'); - } - - return { - config: config - , data: data - , cache: cache - , meta: { - themes: themes - , collections: collections - , root: root - , assets: assets - } - , partials: partials - }; - }); - }); - }; - - Desi.runDesi = runDesi; - - Desi.otherStuff = function (desi) { + Desi.copyAssets = function(desi) { var files = {} ; @@ -427,377 +452,393 @@ } return desi; - }) || PromiseA.resolve(desi)).then(runDesi).then(function (desi) { - return readFrontmatter(desi.content.root.concat(desi.content.themes.concat(desi.content.collections))).then(function () { - return desi; - }); - }).then(function (desi) { - // TODO add missing metadata and resave file - desi.navigation = []; + }) || PromiseA.resolve(desi)); + }; - desi.content.root.forEach(function (page) { - var name = path.basename(page.path, path.extname(page.path)) - , nindex + Desi.parseFrontmatter = function (desi) { + return readFrontmatter(desi.content.root.concat(desi.content.themes.concat(desi.content.collections))).then(function () { + return desi; + }); + }; + + Desi.getNav = function (desi) { + // TODO add missing metadata and resave file + desi.navigation = []; + + desi.content.root.forEach(function (page) { + var name = path.basename(page.path, path.extname(page.path)) + , nindex + ; + + //if (-1 === desi.data.navigation.indexOf(name) && 'index' !== name) + nindex = desi.data.navigation.indexOf(name); + if (-1 === nindex) { + return; + } + + desi.navigation[nindex] = { + title: page.yml && page.yml.title || firstCap(name) + , href: desi.urls.base_path + '/' + name + , path: desi.urls.base_path + '/' + name + , name: name + , active: false // placeholder + }; + }); + + return PromiseA.resolve(desi); + }; + + Desi.normalizeYml = function (desi) { + desi.content.root.forEach(function (page) { + page.yml = page.yml || {}; + // TODO make default layout configurable + page.yml.layout = page.yml.layout || '_root'; + + if (!page.relativePath) { + page.relativePath = path.dirname(page.path); + page.name = page.name || path.basename(page.path); + } + + page.relativePath = page.relativePath.replace(desi.config.rootdir, '').replace(/^\//, ''); + page.path = path.join(page.relativePath, page.name); + + // TODO make bare root routes configurable + page.yml.permalink = page.yml.permalink || page.path.replace(/\.\w+$/, ''); + + page.yml.title = page.yml.title || firstCap(page.name.replace(/\.\w+$/, '')); + }); + + desi.content.collections = desi.content.collections.filter(function (article) { + if (!article.yml) { + console.warn("no frontmatter for " + article.name); + console.warn(article.name); + return; + } + + if (!article.body || !article.body.trim()) { + console.warn('Ignoring empty content file ' + (article.path || article.name)); + return; + } + + return true; + }); + + + function normalizeFrontmatter(page) { + var yml = page.yml + ; + + // TODO read the config for this collection for how to create premalink + if (!yml.permalink) { + if (page.name) { + page.htmlname = page.name.replace(/\.\w+$/, '.html'); + } + page.path = page.path || path.join(page.relativePath, page.name); + page.htmlpath = page.path.replace(/\.\w+$/, '.html'); + // TODO strip '_root' or whatever + // strip .html, .md, .jade, etc + if (!/^\/?(index)?\/?index(\.html)?/.test(yml.htmlpath)) { + console.info('found index again'); + yml.permalink = page.htmlpath; + } + console.info('1', yml.permalink); + } + + if (!/\.html?$/.test(yml.permalink)) { + console.info(page.yml.permalink); + yml.permalink = path.join(yml.permalink, 'index.html'); + } + + //yml.permalinkBase = path.join(path.dirname(yml.permalink), path.basename(yml.permalink, path.extname(yml.permalink))); + //yml.permalink = path.join(path.dirname(yml.permalink), path.basename(yml.permalink, path.extname(yml.permalink))); + + if (!page.yml.uuid) { + // TODO only do this if it's going to be saved + // page.yml.uuid = UUID.v4(); + } + + if (!page.yml.date) { + // TODO tell YAML parser to keep the date a string + page.yml.date = new Date(page.yml.created_at || page.yml.time || page.createdDate || page.lastModifiedDate).toISOString(); + } + if ('object' === typeof page.yml.date) { + page.yml.date = page.yml.date.toISOString(); + } + + if (!page.yml.updated_at) { + page.yml.updated_at = page.lastModifiedDate; + } + } + + function normalizeContentEntity(entity) { + entity.ext = path.extname(entity.path); + entity.url = desi.urls.url + path.join(desi.urls.base_path, entity.yml.permalink); + entity.canonical_url = desi.urls.url + path.join(desi.urls.base_path, entity.yml.permalink); + entity.relative_url = path.join(desi.urls.base_path, entity.yml.permalink); + entity.published_at = fromLocaleDate(entity.yml.date); + entity.year = entity.published_at.year; + entity.month = entity.published_at.month; + entity.day = entity.published_at.day; + entity.hour = entity.published_at.hour; + entity.twelve_hour = entity.published_at.twelve_hour; + entity.meridian = entity.published_at.meridian; + entity.minute = entity.published_at.minute; + entity.title = entity.yml.title; + // let's just agree that that's too far + //entity.second = entity.published_at.second; + + // The root index is the one exception + if (/^(index)?\/?index(\.html?)?$/.test(entity.yml.permalink)) { + entity.yml.permalink = ''; + console.info('found index', entity); + } + } + + desi.content.root.forEach(normalizeFrontmatter); + // TODO process tags and categories and such + desi.content.collections.forEach(normalizeFrontmatter); + + desi.content.root.forEach(normalizeContentEntity); + desi.content.collections.forEach(normalizeContentEntity); + + return PromiseA.resolve(desi); + }; + + Desi.collate = function (desi/*, collectionname*/) { + function byDate(a, b) { + if (a.year > b.year) { + return -1; + } else if (a.year < b.year) { + return 1; + } + + if (a.month > b.month) { + return -1; + } else if (a.month < b.month) { + return 1; + } + + if (a.day > b.day) { + return -1; + } else if (a.day < b.day) { + return 1; + } + + if (a.hour > b.hour) { + return -1; + } else if (a.hour < b.hour) { + return 1; + } + + if (a.minute > b.minute) { + return -1; + } else if (a.minute < b.minute) { + return 1; + } + + if (a.title.toLowerCase() <= b.title.toLowerCase()) { + return -1; + } + + return 1; + } + + function collate(entities) { + var yearsArr = [] + ; + + entities.forEach(function (f) { + var set + , yindex = 3000 - f.year + , mindex = 12 - f.month ; - //if (-1 === desi.data.navigation.indexOf(name) && 'index' !== name) - nindex = desi.data.navigation.indexOf(name); - if (-1 === nindex) { - return; + if (!yearsArr[yindex]) { + yearsArr[yindex] = { year: f.year, months: [] }; } + set = yearsArr[yindex]; - desi.navigation[nindex] = { - title: page.yml && page.yml.title || firstCap(name) - , href: desi.urls.base_path + '/' + name - , path: desi.urls.base_path + '/' + name - , name: name - , active: false // placeholder - }; + if (!set.months[mindex]) { + set.months[mindex] = { month: months[parseInt(f.month, 10)], pages: [] }; + } + set = set.months[mindex]; + + set.pages.push(f); }); - desi.content.root.forEach(function (page) { - page.yml = page.yml || {}; - // TODO make default layout configurable - page.yml.layout = page.yml.layout || '_root'; - - if (!page.relativePath) { - page.relativePath = path.dirname(page.path); - page.name = page.name || path.basename(page.path); + yearsArr = yearsArr.filter(function (y) { + if (!y) { + return false; } - page.relativePath = page.relativePath.replace(desi.config.rootdir, '').replace(/^\//, ''); - page.path = path.join(page.relativePath, page.name); + y.months = y.months.filter(function (m) { + return m && m.pages.length; + }); - // TODO make bare root routes configurable - page.yml.permalink = page.yml.permalink || page.path.replace(/\.\w+$/, ''); - - page.yml.title = page.yml.title || firstCap(page.name.replace(/\.\w+$/, '')); - }); - - desi.content.collections = desi.content.collections.filter(function (article) { - if (!article.yml) { - console.warn("no frontmatter for " + article.name); - console.warn(article.name); - return; - } - - if (!article.body || !article.body.trim()) { - console.warn('Ignoring empty content file ' + (article.path || article.name)); - return; + if (!y.months.length) { + return false; } return true; }); + return { years: yearsArr }; + } - function normalizeFrontmatter(page) { - var yml = page.yml - ; - // TODO read the config for this collection for how to create premalink - if (!yml.permalink) { - if (page.name) { - page.htmlname = page.name.replace(/\.\w+$/, '.html'); - } - page.path = page.path || path.join(page.relativePath, page.name); - page.htmlpath = page.path.replace(/\.\w+$/, '.html'); - // TODO strip '_root' or whatever - // strip .html, .md, .jade, etc - if (!/^\/?(index)?\/?index(\.html)?/.test(yml.htmlpath)) { - console.info('found index again'); - yml.permalink = page.htmlpath; - } - console.info('1', yml.permalink); - } + desi.content.collections.sort(byDate); + desi.collated = collate(desi.content.collections); + console.info('desi.collated'); + console.info(desi.collated); - if (!/\.html?$/.test(yml.permalink)) { - console.info(page.yml.permalink); - yml.permalink = path.join(yml.permalink, 'index.html'); - } + return PromiseA.resolve(desi); + }; - //yml.permalinkBase = path.join(path.dirname(yml.permalink), path.basename(yml.permalink, path.extname(yml.permalink))); - //yml.permalink = path.join(path.dirname(yml.permalink), path.basename(yml.permalink, path.extname(yml.permalink))); + Desi.build = function (desi) { + var compiled = [] + ; - if (!page.yml.uuid) { - // TODO only do this if it's going to be saved - // page.yml.uuid = UUID.v4(); - } - - if (!page.yml.date) { - // TODO tell YAML parser to keep the date a string - page.yml.date = new Date(page.yml.created_at || page.yml.time || page.createdDate || page.lastModifiedDate).toISOString(); - } - if ('object' === typeof page.yml.date) { - page.yml.date = page.yml.date.toISOString(); - } - - if (!page.yml.updated_at) { - page.yml.updated_at = page.lastModifiedDate; - } + /* + function compileScriptEntity(entity, i, arr) { + } + */ + function compileThemeEntity(entity, i, arr) { + console.log("compiling " + (i + 1) + "/" + arr.length + " " + (entity.path || entity.name)); + // TODO less / sass / etc + compiled.push({ contents: entity.body || entity.contents, path: path.join(desi.config.compiled_path, 'themes', entity.path) }); + if (/stylesheets.*\.css/.test(entity.path) && (!/google/.test(entity.path) || /obsid/.test(entity.path))) { + // TODO XXX move to a partial + desi.assets.push( + '' + ); } + } + desi.navigation.filter(function (n) { + return n; + }); + //console.log(desi.navigation); + function compileContentEntity(entity, i, arr) { + console.log("compiling " + (i + 1) + "/" + arr.length + " " + (entity.path || entity.name)); - function normalizeContentEntity(entity) { - entity.ext = path.extname(entity.path); - entity.url = desi.urls.url + path.join(desi.urls.base_path, entity.yml.permalink); - entity.canonical_url = desi.urls.url + path.join(desi.urls.base_path, entity.yml.permalink); - entity.relative_url = path.join(desi.urls.base_path, entity.yml.permalink); - entity.published_at = fromLocaleDate(entity.yml.date); - entity.year = entity.published_at.year; - entity.month = entity.published_at.month; - entity.day = entity.published_at.day; - entity.hour = entity.published_at.hour; - entity.twelve_hour = entity.published_at.twelve_hour; - entity.meridian = entity.published_at.meridian; - entity.minute = entity.published_at.minute; - entity.title = entity.yml.title; - // let's just agree that that's too far - //entity.second = entity.published_at.second; - - // The root index is the one exception - if (/^(index)?\/?index(\.html?)?$/.test(entity.yml.permalink)) { - entity.yml.permalink = ''; - console.info('found index', entity); - } - } - - function byDate(a, b) { - if (a.year > b.year) { - return -1; - } else if (a.year < b.year) { - return 1; - } - - if (a.month > b.month) { - return -1; - } else if (a.month < b.month) { - return 1; - } - - if (a.day > b.day) { - return -1; - } else if (a.day < b.day) { - return 1; - } - - if (a.hour > b.hour) { - return -1; - } else if (a.hour < b.hour) { - return 1; - } - - if (a.minute > b.minute) { - return -1; - } else if (a.minute < b.minute) { - return 1; - } - - if (a.title.toLowerCase() <= b.title.toLowerCase()) { - return -1; - } - - return 1; - } - - function collate(entities) { - var yearsArr = [] - ; - - entities.forEach(function (f) { - var set - , yindex = 3000 - f.year - , mindex = 12 - f.month - ; - - if (!yearsArr[yindex]) { - yearsArr[yindex] = { year: f.year, months: [] }; - } - set = yearsArr[yindex]; - - if (!set.months[mindex]) { - set.months[mindex] = { month: months[parseInt(f.month, 10)], pages: [] }; - } - set = set.months[mindex]; - - set.pages.push(f); - }); - - yearsArr = yearsArr.filter(function (y) { - if (!y) { - return false; - } - - y.months = y.months.filter(function (m) { - return m && m.pages.length; - }); - - if (!y.months.length) { - return false; - } - - return true; - }); - - return { years: yearsArr }; - } - - desi.content.root.forEach(normalizeFrontmatter); - // TODO process tags and categories and such - desi.content.collections.forEach(normalizeFrontmatter); - - desi.content.root.forEach(normalizeContentEntity); - desi.content.collections.forEach(normalizeContentEntity); - - desi.content.collections.sort(byDate); - desi.collated = collate(desi.content.collections); - console.info('desi.collated'); - console.info(desi.collated); - - return desi; - }).then(function (desi) { - var compiled = [] + var previous = '' + , layers + , view ; - /* - function compileScriptEntity(entity, i, arr) { - } - */ - desi.assets = []; - function compileThemeEntity(entity, i, arr) { - console.log("compiling " + (i + 1) + "/" + arr.length + " " + (entity.path || entity.name)); - // TODO less / sass / etc - compiled.push({ contents: entity.body || entity.contents, path: path.join(desi.config.compiled_path, 'themes', entity.path) }); - if (/stylesheets.*\.css/.test(entity.path) && (!/google/.test(entity.path) || /obsid/.test(entity.path))) { - // TODO XXX move to a partial - desi.assets.push( - '' - ); + layers = getLayout(desi, entity.yml.theme, entity.yml.layout, [entity]); + + view = { + page: entity.yml // data for just *this* page + //, data: desi.data // data.yml + // https://github.com/janl/mustache.js/issues/415 + , data: num2str(desi.data) + , collection: {} // data for just *this* collection + , categories: [] // *all* categories in all collections + , tags: [] // *all* tags in all collections + , site: num2str(desi.site || {}) + , url: entity.canonical_url + , canonical_url: entity.canonical_url + , relative_url: entity.relative_url + , urls: desi.urls + , previous: arr[i - 1] + , next: arr[i + 1] + , posts: { collated: desi.collated } + // TODO concat theme, widget, and site assets + , assets: desi.assets.join('\n') + }; + + //console.log('rel:', view.relative_url); + view.site.author = desi.data.author; + view.site.navigation = JSON.parse(JSON.stringify(desi.navigation)); + view.site.navigation.forEach(function (nav) { + + if (nav.href === view.relative_url) { + nav.active = true; } - } - desi.navigation.filter(function (n) { - return n; }); - //console.log(desi.navigation); - function compileContentEntity(entity, i, arr) { - console.log("compiling " + (i + 1) + "/" + arr.length + " " + (entity.path || entity.name)); + // backwards compat + view.site['navigation?to_pages'] = view.site.navigation; + view.site['navigation?to__root'] = view.site.navigation; + view.data.navigation = view.site.navigation; + view.data['navigation?to_pages'] = view.site.navigation; + view.data['navigation?to__root'] = view.site.navigation; - var previous = '' - , layers - , view + layers.forEach(function (current) { + // TODO meta.layout + var body = (current.body || current.contents || '').trim() + , html + , curview = {} ; - layers = getLayout(desi, entity.yml.theme, entity.yml.layout, [entity]); + // TODO move to normalization + current.path = current.path || (entity.relativePath + '/' + entity.name); - view = { - page: entity.yml // data for just *this* page - //, data: desi.data // data.yml - // https://github.com/janl/mustache.js/issues/415 - , data: num2str(desi.data) - , collection: {} // data for just *this* collection - , categories: [] // *all* categories in all collections - , tags: [] // *all* tags in all collections - , site: num2str(desi.site || {}) - , url: entity.canonical_url - , canonical_url: entity.canonical_url - , relative_url: entity.relative_url - , urls: desi.urls - , previous: arr[i - 1] - , next: arr[i + 1] - , posts: { collated: desi.collated } - // TODO concat theme, widget, and site assets - , assets: desi.assets.join('\n') - }; - - //console.log('rel:', view.relative_url); - view.site.author = desi.data.author; - view.site.navigation = JSON.parse(JSON.stringify(desi.navigation)); - view.site.navigation.forEach(function (nav) { - - if (nav.href === view.relative_url) { - nav.active = true; - } - }); - // backwards compat - view.site['navigation?to_pages'] = view.site.navigation; - view.site['navigation?to__root'] = view.site.navigation; - view.data.navigation = view.site.navigation; - view.data['navigation?to_pages'] = view.site.navigation; - view.data['navigation?to__root'] = view.site.navigation; - - layers.forEach(function (current) { - // TODO meta.layout - var body = (current.body || current.contents || '').trim() - , html - , curview = {} + if (/\.(html|htm)$/.test(current.path)) { + html = body; + } else if (/\.(md|markdown|mdown|mkdn|mkd|mdwn|mdtxt|mdtext)$/.test(current.ext)) { + html = marked.render(body) + //.replace('"', '"') + //.replace(''', "'") + //.replace('/', '/') ; - - // TODO move to normalization - current.path = current.path || (entity.relativePath + '/' + entity.name); - - if (/\.(html|htm)$/.test(current.path)) { - html = body; - } else if (/\.(md|markdown|mdown|mkdn|mkd|mdwn|mdtxt|mdtext)$/.test(current.ext)) { - html = marked.render(body) - //.replace('"', '"') - //.replace(''', "'") - //.replace('/', '/') - ; - } else { - console.error('unknown parser for ' + (entity.path)); - } - - view.content = previous; - view.page.content = previous; - - // to prevent perfect object equality (and potential template caching) - Object.keys(view).forEach(function (key) { - curview[key] = view[key]; - }); - previous = Mustache.render(html, curview, desi.partials); - }); - - // NOTE: by now, all permalinks should be in the format /path/to/page.html or /path/to/page/index.html - if (/^(index)?(\/?index.html)?$/.test(entity.yml.permalink)) { - console.info('found compiled index'); - compiled.push({ contents: previous, path: path.join(desi.config.compiled_path, 'index.html') }); } else { - compiled.push({ contents: previous, path: path.join(desi.config.compiled_path, entity.yml.permalink) }); + console.error('unknown parser for ' + (entity.path)); } - entity.yml.redirects = entity.yml.redirects || []; - if (/\/index.html$/.test(entity.yml.permalink)) { - entity.yml.redirects.push(entity.yml.permalink.replace(/\/index.html$/, '.html')); - } else if (/\.html$/.test(entity.yml.permalink)) { - entity.yml.redirects.push(entity.yml.permalink.replace(/\.html?$/, '/index.html')); - } else { - console.info('found index, ignoring redirect'); - } - entity.yml.redirects.forEach(function (redirect) { - var content - ; + view.content = previous; + view.page.content = previous; - // TODO move to partial - content = - '' - + '' - + 'Redirecting to ' + entity.yml.title + '' - + '' - + '' - + '' - + '

This page has moved to a ' - + entity.yml.title - + '.

' - + '' - + '' - ; - - compiled.push({ contents: content, path: path.join(desi.config.compiled_path, redirect) }); + // to prevent perfect object equality (and potential template caching) + Object.keys(view).forEach(function (key) { + curview[key] = view[key]; }); + previous = Mustache.render(html, curview, desi.partials); + }); + + // NOTE: by now, all permalinks should be in the format /path/to/page.html or /path/to/page/index.html + if (/^(index)?(\/?index.html)?$/.test(entity.yml.permalink)) { + console.info('found compiled index'); + compiled.push({ contents: previous, path: path.join(desi.config.compiled_path, 'index.html') }); + } else { + compiled.push({ contents: previous, path: path.join(desi.config.compiled_path, entity.yml.permalink) }); } + entity.yml.redirects = entity.yml.redirects || []; + if (/\/index.html$/.test(entity.yml.permalink)) { + entity.yml.redirects.push(entity.yml.permalink.replace(/\/index.html$/, '.html')); + } else if (/\.html$/.test(entity.yml.permalink)) { + entity.yml.redirects.push(entity.yml.permalink.replace(/\.html?$/, '/index.html')); + } else { + console.info('found index, ignoring redirect'); + } + entity.yml.redirects.forEach(function (redirect) { + var content + ; + + // TODO move to partial + content = + '' + + '' + + 'Redirecting to ' + entity.yml.title + '' + + '' + + '' + + '' + + '

This page has moved to a ' + + entity.yml.title + + '.

' + + '' + + '' + ; + + compiled.push({ contents: content, path: path.join(desi.config.compiled_path, redirect) }); + }); + } + + function doStuff() { console.info('[first] compiling theme assets'); desi.content.themes.filter(function (f) { return !/\blayouts\b/.test(f.path); }).forEach(compileThemeEntity); @@ -807,54 +848,62 @@ desi.content.collections.forEach(compileContentEntity); desi.compiled = compiled; - return desi; - }).then(function (desi) { - var compiled = desi.compiled.slice(0) - , batches = [] - , now - , size = 0 - ; + return PromiseA.resolve(desi); + } - if (!compiled.length) { - console.info("No files were deemed worthy to compile. Done"); - return; - } + if (!desi.partials) { + return fsapi.getAllPartials().then(doStuff); + } else { + return doStuff(); + } + }; - // because some servers / proxies are terrible at handling large uploads (>= 100k) - // (vagrant? or express? one of the two is CRAZY slow) - console.info('saving compiled files', desi.compiled); - while (compiled.length) { - batches.push(compiled.splice(0, 500)); - } + Desi.save = function (desi) { + var compiled = desi.compiled.slice(0) + , batches = [] + , now + , size = 0 + ; - now = Date.now(); - console.info('compiled files'); - return forEachAsync(batches, function (files) { - return fsapi.putFiles(files).then(function (saved) { - size += saved.size; + if (!compiled.length) { + console.info("No files were deemed worthy to compile. Done"); + return; + } - if (saved.error) { - console.error(saved.error); - } + // because some servers / proxies are terrible at handling large uploads (>= 100k) + // (vagrant? or express? one of the two is CRAZY slow) + console.info('saving compiled files', desi.compiled); + while (compiled.length) { + batches.push(compiled.splice(0, 500)); + } - if (!saved.errors || !saved.errors.length) { - return; - } + now = Date.now(); + console.info('compiled files'); + return forEachAsync(batches, function (files) { + return fsapi.putFiles(files).then(function (saved) { + size += saved.size; - saved.errors.forEach(function (e) { - console.error(e); - }); - //console.info('saved ' + files.length + ' files'); - //console.log(saved); + if (saved.error) { + console.error(saved.error); + } + + if (!saved.errors || !saved.errors.length) { + return; + } + + saved.errors.forEach(function (e) { + console.error(e); }); - }).then(function () { - // TODO update cache - console.info('wrote ' + desi.compiled.length - + ' files (' + (size / (1024 * 1024)).toFixed(2) - + ' MiB) in ' - + ((Date.now() - now) / 1000).toFixed(3) + 's' - ); + //console.info('saved ' + files.length + ' files'); + //console.log(saved); }); + }).then(function () { + // TODO update cache + console.info('wrote ' + desi.compiled.length + + ' files (' + (size / (1024 * 1024)).toFixed(2) + + ' MiB) in ' + + ((Date.now() - now) / 1000).toFixed(3) + 's' + ); }); }; diff --git a/index.html b/index.html index 7b8c4d5..2c8c535 100644 --- a/index.html +++ b/index.html @@ -38,8 +38,10 @@ @@ -102,9 +104,14 @@ + - + + + + + diff --git a/lib/deardesi-browser.js b/lib/deardesi-browser.js index 96c411a..42935d0 100644 --- a/lib/deardesi-browser.js +++ b/lib/deardesi-browser.js @@ -193,6 +193,8 @@ var extensions = '' , dotfiles = '' + , contents = '' + , sha1sum = '' ; if (Array.isArray(opts.extensions)) { @@ -201,10 +203,17 @@ if (opts.dotfiles) { dotfiles = '&dotfiles=true'; } + if (opts.contents) { + contents = '&contents=true'; + } + if (false === opts.sha1sum) { + sha1sum = '&sha1sum=false'; + } - return request.post('/api/fs/walk?_method=GET' + dotfiles + extensions, { + return request.post('/api/fs/walk?_method=GET' + dotfiles + extensions + contents + sha1sum, { dirs: collections }).then(function (resp) { + console.log(collections); return JSON.parse(resp); }); }; @@ -223,6 +232,102 @@ }); }; + fsapi.getConfigs = function (confs) { + var opts = { extensions: ['yml', 'yaml', 'json'], dotfiles: false, contents: true, sha1sum: true } + ; + + return fsapi.getMeta(confs, opts).then(function (collections) { + var obj = {} + ; + + Object.keys(collections).forEach(function (key) { + var files = collections[key] + , keyname = key.replace(/\.(json|ya?ml|\/)$/i, '') + ; + + obj[keyname] = obj[keyname] || {}; + + files.forEach(function (file) { + var filename = file.name.replace(/\.(json|ya?ml)$/i, '') + , data = {} + ; + + if (/\.(ya?ml)$/i.test(file.name)) { + console.log(); + try { + data = exports.YAML.parse(file.contents) || {}; + if ("undefined" === obj[keyname][filename]) { + data = {}; + } + } catch(e) { + console.error("Could not parse yaml for " + filename); + console.error(file); + console.error(e); + } + } + else if (/\.(json)$/i.test(file.name)) { + try { + data = JSON.parse(file.contents) || {}; + } catch(e) { + console.error("Could not parse json for " + filename); + console.error(file); + console.error(e); + } + } else { + console.error("Not sure what to do with this one..."); + console.error(file); + } + + obj[keyname][filename] = data; + /* + if (!obj[keyname][filename]) { + obj[keyname][filename] = {}; + } + + Object.keys(data).forEach(function (key) { + obj[keyname][filename][key] = data[key]; + }); + */ + }); + }); + + return obj; + }); + }; + fsapi.getAllPartials = function () { + return fsapi.getConfigs(['partials', 'partials.yml']).then(function (results) { + var partials = {} + ; + + Object.keys(results.partials).forEach(function (key) { + var partial = partials[key]; + Object.keys(partial).forEach(function (prop) { + if (partials[prop]) { + console.warn('partial \'' + prop + '\' overwritten by ' + key); + } + partials[prop] = partial[prop]; + }); + }); + + return partials; + }); + }; + fsapi.getBlogdir = function () { + return request.get('/api/fs').then(function (resp) { + return JSON.parse(resp); + }); + }; + fsapi.getAllConfigFiles = function () { + return fsapi.getConfigs(['config.yml', 'site.yml', 'authors']).then(function (results) { + var authors = results.authors + , config = results.config.config + , site = results.site.site + ; + + return { config: config, authors: authors, site: site }; + }); + }; + fsapi.getData = function () { return request.get('/data.yml').then(function (resp) { return exports.YAML.parse(resp); @@ -232,8 +337,10 @@ fsapi.getCache = function () { return request.get('/cache.json').then(function (resp) { return JSON.parse(resp); - }).catch(function () { + }).catch(function (/*e*/) { return {}; + }).then(function (obj) { + return obj; }); }; diff --git a/lib/fsapi.js b/lib/fsapi.js index 18ffb1a..a3bd996 100644 --- a/lib/fsapi.js +++ b/lib/fsapi.js @@ -10,6 +10,7 @@ var PromiseA = require('bluebird').Promise , sha1sum = function (str) { return require('secret-utils').hashsum('sha1', str); } , mkdirp = PromiseA.promisify(require('mkdirp')) , fsExtra = PromiseA.promisifyAll(require('fs.extra')) + //, tmpdir = require('os').tmpdir() ; function strip(prefix, pathname) { @@ -32,7 +33,7 @@ function walkDir(parent, sub, opts) { return false; } - if ('.' === name[0] && !opts.dotfiles) { + if (!opts.dotfiles && ('.' === name[0])) { return false; } diff --git a/server.js b/server.js index da3e311..451a93f 100644 --- a/server.js +++ b/server.js @@ -15,9 +15,11 @@ var connect = require('connect') , getfs = require('./lib/fsapi').getfs , putfs = require('./lib/fsapi').putfs - , config = require('./config.yml') + , blogdir = process.argv[2] || 'blog' + , port = process.argv[3] || '65080' + , path = require('path') - , blogdir = path.resolve(config.blogdir || __dirname) + //, config = require(path.join('./', blogdir, 'config.yml')) ; @@ -41,6 +43,13 @@ app return; } + if (!dirnames.every(function (dirname) { + return 'string' === typeof dirname; + })) { + res.json({ error: "malformed request: " + JSON.stringify(dirnames) }); + return; + } + /* if (req.query.excludes) { opts.excludes = req.query.excludes.split(','); @@ -54,6 +63,12 @@ app if ('true' === req.query.dotfiles) { opts.dotfiles = true; } + if ('false' === req.query.sha1sum) { + opts.sha1sum = false; + } + if ('true' === req.query.contents) { + opts.contents = true; + } // TODO opts.contents? walk.walkDirs(blogdir, dirnames, opts).then(function (stats) { @@ -125,18 +140,29 @@ app }); }) - .use('/api/fs', function (req, res, next) { - next(); + .use('/api/fs', function (req, res) { + var pathname = path.resolve(blogdir) + ; + + res.json({ + path: pathname + , name: path.basename(pathname) + , relativePath: path.dirname(pathname) + //, cwd: path.resolve() + //, patharg: blogdir + }); return; }) - .use('/api/fs/static', serveStatic('.')) + .use('/api/fs/static', serveStatic(blogdir)) - .use(serveStatic('.')) - .use(serveStatic(blogdir)) + .use(serveStatic('./')) + .use('/compiled_dev', serveStatic(path.join(blogdir, '/compiled_dev'))) + // TODO + //.use(serveStatic(tmpdir)) ; module.exports = app; -require('http').createServer().on('request', app).listen(process.argv[2] || 65080, function () { - console.log('listening ' + (process.argv[2] || 65080)); +require('http').createServer().on('request', app).listen(port, function () { + console.log('listening ' + port); }); diff --git a/swatches.yml b/swatches.yml new file mode 100644 index 0000000..277a2dd --- /dev/null +++ b/swatches.yml @@ -0,0 +1,36 @@ +bootstrap_cdn: //maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css +bootswatches: + - Cerulean + - Cosmo + - Cyborg + - Darkly + - Flatly + - Journal + - Lumen + - Paper + - Readable + - Sandstone + - Simplex + - Slate + - Spacelab + - Superhero + - United + - Yeti +bootswatches_2: + - Amelia + - Cerulean + - Cosmo + - Cyborg + - Flatly + - Journal + - Readable + - Simplex + - Slate + - Spacelab + - Superhero + - United +bootswatch_cdn: //maxcdn.bootstrapcdn.com/bootswatch/3.3.1/{{name}}/bootstrap.min.css +fontawesome_cdn: //maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css + +bootswatch_2_download: http://bootswatch.com/2/{{bootswatch}}/bootstrap.min.css +bootstrap_2_cdn://maxcdn.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css diff --git a/views/about/about.html b/views/about/about.html index cab8ef4..caf5ac4 100644 --- a/views/about/about.html +++ b/views/about/about.html @@ -1,45 +1,47 @@ -
-
-
-
+
+
+
+ + + +
-
-

Dear Desi, ...


+
+

Setup your new blog in just 5 minutes.

+
+
- -
-

Desirae

-

The in-browser static blog generator - -

-
-
-
- -

Features

-
- -
-
-
    -
  • Builds in the Browser -
      -
    • Write content in Markdown, Jade, or HTML
    • -
    • Mustache Templates
    • -
    -
  • -
  • Git, Dropbox, or regular Folders for management
  • -
  • Go headless with Node.js support
  • -
  • Dual Licensed Apache2 and MIT
  • -
  • No Ruby version Hell - it'll still work in 6 months! :-D
  • -
-
-
+
+
+
+ +
+
+
+

Dear Desi is...

+
+
    +
  • Builds in the Browser +
      +
    • The in-browser static blog generator
    • +
    • Write content in Markdown, Jade, or HTML
    • +
    • Mustache Templates
    • +
    +
  • +
  • Git, Dropbox, or regular Folders for management
  • +
  • Go headless with Node.js support
  • +
  • Dual Licensed Apache2 and MIT
  • +
  • No Ruby version Hell - it'll still work in 6 months! :-D
  • +
+

What are you waiting for?

+
+ Get Started
-
diff --git a/views/authors/authors.html b/views/authors/authors.html new file mode 100644 index 0000000..99ea45d --- /dev/null +++ b/views/authors/authors.html @@ -0,0 +1,233 @@ +
+
+ +
+ +
+
+
+ /authors/.yml +
+
+ +
+
+
+
+
+
+
+
+
+ Profile Basics + +
+
+
+ +
+ +
+
+
+ +
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+ +
+
+
+ Social + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+ Developers +
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+
+
+ Feeds + +
+ +
+ +
+
+ +
+
+
+ + + +
+
+
+ + diff --git a/views/authors/authors.js b/views/authors/authors.js new file mode 100644 index 0000000..1390a38 --- /dev/null +++ b/views/authors/authors.js @@ -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); + */ +}]); diff --git a/views/build/build.html b/views/build/build.html new file mode 100644 index 0000000..e69de29 diff --git a/views/build/build.js b/views/build/build.js index 4e388c9..c1cdf92 100644 --- a/views/build/build.js +++ b/views/build/build.js @@ -10,20 +10,4 @@ angular.module('myApp.build', ['ngRoute']) }]) .controller('BuildCtrl', [function() { - var Desi = window.Desi || require('./deardesi').Desi - , scope = this - , desi = {} - ; - - Desi.init(desi).then(function () { - scope.run = function () { - return Desi.runDesi(desi).then(function () { Desi.otherStuff(); }) - .catch(function (e) { - console.error('A great and uncatchable error has befallen the land. Read ye here for das detalles..'); - console.error(e.message); - console.error(e); - throw e; - }); - }; - }); }]); diff --git a/views/configure/configure.js b/views/configure/configure.js deleted file mode 100644 index 356edc9..0000000 --- a/views/configure/configure.js +++ /dev/null @@ -1,29 +0,0 @@ -'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() { - var Desi = window.Desi || require('./deardesi').Desi - , scope = this - , desi = {} - ; - - Desi.init(desi).then(function () { - scope.run = function () { - return Desi.runDesi(desi).then(function () { Desi.otherStuff(); }) - .catch(function (e) { - console.error('A great and uncatchable error has befallen the land. Read ye here for das detalles..'); - console.error(e.message); - console.error(e); - throw e; - }); - }; - }); -}]); diff --git a/views/configure/configure.html b/views/site/site.html similarity index 54% rename from views/configure/configure.html rename to views/site/site.html index 395ca06..3590ba0 100644 --- a/views/configure/configure.html +++ b/views/site/site.html @@ -2,6 +2,7 @@
@@ -9,71 +10,47 @@
-
- General +
+ General -
- -
- -
+
+ +
+
+
-
- -
- -
+
+ +
+
+
-
- -
- -
-
- -
- -
- -
-
- -
- -
-
- -
- -
-
- -
-
+ +
-
+
Production
- +
- +
- +
@@ -86,31 +63,6 @@
-
-
-
- Development -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
diff --git a/views/site/site.js b/views/site/site.js new file mode 100644 index 0000000..b3bc260 --- /dev/null +++ b/views/site/site.js @@ -0,0 +1,26 @@ +'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', 'Desirae', function($scope, Desirae) { + var scope = this + ; + + console.log('desi loading'); + Desirae.meta().then(function (desi) { + console.log('desi loaded'); + console.log(desi); + scope.blogdir = desi.blogdir.path.replace(/^\/(Users|home)\/[^\/]+\//, '~/'); + }).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; + }); +}]); diff --git a/views/configure/view1_test.js b/views/site/view1_test.js similarity index 100% rename from views/configure/view1_test.js rename to views/site/view1_test.js