Compare commits

..

72 Commits

Author SHA1 Message Date
b74b0cbe3b 0.9.1 2020-11-08 21:54:32 -07:00
0496aeeb60 update deps 2020-11-08 21:54:29 -07:00
715a09bfa7 0.9.0 2020-11-08 20:42:01 -07:00
b5c402f3b0 update deps 2020-11-08 20:40:18 -07:00
b1bb785739 update LICENSE 2020-11-08 20:30:23 -07:00
ff74030e52 make Prettier 2020-11-08 20:30:15 -07:00
416484db42 v0.8.6: update links, deps, and license 2018-11-07 10:42:59 -07:00
AJ ONeal
8f10d2ce7e fix git url 2017-05-18 14:51:21 -06:00
AJ ONeal
d1705aaabe add branch name 2017-05-18 14:50:04 -06:00
AJ ONeal
4a3cd589e5 v0.8.5 2017-04-07 01:54:27 -06:00
AJ ONeal
569c86a72b v0.8.4 2017-04-07 01:54:06 -06:00
AJ ONeal
10ba1c5e6b update repos and deps 2017-04-07 01:53:17 -06:00
AJ ONeal
5bf5214ade Merge branch 'master' of github.com:DearDesi/deardesi 2015-01-31 00:44:24 -07:00
AJ ONeal
90a9305295 v0.8.3 2015-01-31 00:44:08 -07:00
AJ ONeal
694697fe31 updated datamap ruhoh 2015-01-31 00:44:01 -07:00
AJ ONeal
a0dadc19da Update setup.sh 2015-01-24 15:26:48 -07:00
AJ ONeal
11153b8191 Update setup.sh 2015-01-24 15:24:13 -07:00
AJ ONeal
1b6e9d4be1 Update deardesi.js 2015-01-23 21:10:41 -07:00
AJ ONeal
43836863a3 v0.8.2 2015-01-23 20:09:54 -07:00
AJ ONeal
c736df077a fixed sourcepath bug 2015-01-23 20:09:50 -07:00
AJ ONeal
db954333dc fixed sourcepath bug 2015-01-23 20:09:37 -07:00
AJ ONeal
1cdd258100 Merge branch 'master' of github.com:DearDesi/deardesi 2015-01-23 19:19:34 -07:00
AJ ONeal
bfcb174b2e v0.8.1 2015-01-23 19:19:17 -07:00
AJ ONeal
04507b6aa5 updated readme 2015-01-23 19:19:10 -07:00
AJ ONeal
caf7f5a683 Create site.yml 2015-01-23 19:09:15 -07:00
AJ ONeal
c0c0245cf2 Create johndoe.yml 2015-01-23 19:05:13 -07:00
AJ ONeal
9ffa1c5855 Merge branch 'master' of github.com:DearDesi/deardesi 2015-01-23 18:52:46 -07:00
AJ ONeal
5c646376d2 v0.8.0 2015-01-23 18:25:51 -07:00
AJ ONeal
1281a9f86b updated to latest 2015-01-23 18:25:08 -07:00
AJ ONeal
8a5fd87747 updated themes, fixed some text output 2015-01-23 18:24:46 -07:00
AJ ONeal
84e27fa430 updated datamap ruhoh to latest 2015-01-23 18:24:13 -07:00
AJ ONeal
2b50a15582 updated desi to latest 2015-01-23 18:23:00 -07:00
AJ ONeal
cbd463cd3f updated desi to latest 2015-01-23 18:22:48 -07:00
AJ ONeal
5e935a3d8a transitioning to desirae v1 2015-01-23 02:56:27 -07:00
AJ ONeal
2b6d0d144f add jade 2015-01-23 02:54:31 -07:00
AJ ONeal
caa3598cae Update README.md 2015-01-21 19:57:23 -07:00
AJ ONeal
d3a564cfe8 Update README.md 2015-01-21 19:54:25 -07:00
AJ ONeal
058fbbcc8d Update README.md 2015-01-21 19:54:12 -07:00
AJ ONeal
4ad3666972 Update README.md 2015-01-21 19:53:57 -07:00
AJ ONeal
3e0bf5e793 let the user know install was successful 2015-01-21 18:44:17 -07:00
AJ ONeal
a1951fa1ad better spacing 2015-01-21 18:43:16 -07:00
AJ ONeal
a20663ec16 spacing update 2015-01-21 18:42:47 -07:00
AJ ONeal
ec9b6bac80 v0.7.2 2015-01-21 18:42:12 -07:00
AJ ONeal
c477752e2c update versions, better spacing 2015-01-21 18:42:04 -07:00
AJ ONeal
d6bdb8239c Merge branch 'master' of github.com:DearDesi/deardesi 2015-01-21 18:39:37 -07:00
AJ ONeal
7415df3119 better spacing 2015-01-21 18:39:22 -07:00
08ab2e7244 fix content typo 2015-01-21 20:38:14 -05:00
78721a8bee Merge branch 'master' of github.com:DearDesi/deardesi 2015-01-21 20:33:35 -05:00
c9ed1306f6 merged updates with live site 2015-01-21 20:33:11 -05:00
AJ ONeal
896d5c401b clarified instructions 2015-01-21 18:27:40 -07:00
AJ ONeal
a20a9a0d42 v0.7.0 2015-01-21 18:13:58 -07:00
AJ ONeal
8f8f7085e4 implemented init 2015-01-21 18:13:47 -07:00
AJ ONeal
a25281aa2a typo fix 2015-01-21 16:32:15 -07:00
AJ ONeal
bf0b8d9f91 add simpler setup 2015-01-21 16:30:56 -07:00
AJ ONeal
6baad90645 v0.6.1 2015-01-21 13:55:36 -07:00
AJ ONeal
0c2123542f add literal, html, and markdown link previews 2015-01-21 13:55:23 -07:00
AJ ONeal
ac54f466c5 v0.6.0 2015-01-21 13:38:20 -07:00
AJ ONeal
30ea318126 more consistent pathname output 2015-01-21 13:38:07 -07:00
AJ ONeal
78aa491fee support google drive and dropbox links 2015-01-21 13:31:15 -07:00
AJ ONeal
43e6333377 more friendly path display 2015-01-21 13:29:44 -07:00
AJ ONeal
5fee84c33d updated desi to latest 2015-01-21 13:27:37 -07:00
AJ ONeal
e3a24e9652 fix #3 by reloading metadata prior to each build 2015-01-21 02:41:12 -07:00
AJ ONeal
a73ea68402 v0.5.11 2015-01-17 18:41:05 -07:00
AJ ONeal
f045397486 update desirae 2015-01-17 18:41:03 -07:00
AJ ONeal
dc91b2c9c2 v0.5.10 2015-01-17 18:16:30 -07:00
AJ ONeal
da392e9ae6 update log paths, update desirae 2015-01-17 18:16:10 -07:00
AJ ONeal
ccdac994c0 version bump 2015-01-17 16:39:51 -07:00
AJ ONeal
e669beb1cb version bump 2015-01-17 16:39:44 -07:00
AJ ONeal
82a7901be1 Merge branch 'master' of github.com:DearDesi/deardesi 2015-01-17 16:31:32 -07:00
AJ ONeal
ad7f5977da version bump 2015-01-17 16:30:46 -07:00
AJ ONeal
97376fe27d Update README.md 2015-01-17 16:15:04 -07:00
AJ ONeal
7b6058357e version bump 2015-01-17 14:02:28 -07:00
36 changed files with 3610 additions and 1394 deletions

1
.prettierrc.json Normal file
View File

@ -0,0 +1 @@
{}

206
LICENSE
View File

@ -1,202 +1,4 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This Source Code Form is subject to the terms of the Mozilla
Public License, v. 2.0. If a copy of the MPL was not distributed
with this file, You can obtain one at
https://mozilla.org/MPL/2.0/.

163
README.md
View File

@ -1,76 +1,141 @@
Dear Desi
=========
<!--
Not a Web Developer?
====================
A nice, friendly tool to help you get set up and start blogging with Desirae
You're in the wrong place. **Go to <http://dear.desi>** and follow the instructions there.
-->
Front-end written in AngularJS, back-end in Node.js
# Did you mean to come here?
Install and Usage
=================
If you're a normal person interested in _Desi, the DIY blog platform for normal people_,
you might have meant to go to [DearDesi](http://dear.desi) instead.
If you're on OS X or Linux, it's as easy as pie to install and use Desirae.
Otherwise, if you're a cyborg, wizzard, or web developer: carry on.
First install io.js (or node.js), if you haven't already.
# Dear Desi
```bash
# io.js
These instructions cover the command line only.
echo "v1.0.1" > /tmp/NODE_VER
curl -fsSL bit.ly/easy-install-iojs | bash
If you want instructions for the web interface, head over to [DearDesi](http://dear.desi).
# node.js
## Command Line Install (for developers)
This assumes that you already have `git` and `node` installed,
otherwise see [node-installer.sh](https://git.coolaj86.com/coolaj86/node-installer.sh)
echo "v0.11.14" > /tmp/NODE_VER
curl -fsSL bit.ly/easy-install-node | bash
```
# Install with distributed tools on a decentralized system
npm install -g 'git+https://git.daplie.com/Daplie/deardesi.git#v1'
Then install desi
```bash
# Install with the centralized, concentrated hypocrinet
npm install -g desi
```
And create a copy of the desirae-seed
That was easy
## Quick Usage
- desi init -d ~/Desktop/new-blog
- pushd ~/Desktop/new-blog
- desi post "my first post"
- desi build
- desi serve
<http://local.dear.desi:65080>
**Note**: both through command line and web you need `site.yml` and `authors/xyz.yml` configured in order to create a post (as well as build).
The post commands output the location of post in various formats.
## Initialize your blog (step 1)
You can do this 3 ways:
1. Create a new blog with `desi init -d ~/Desktop/blog`
2. Clone the seed project and themes yourself
3. Clone the seed project and import your posts and themes
### Automated (desi init)
```bash
# initialize (and or create) a blog directory
desi init -d ~/Desktop/blog
# initialize the current directory
pushd ~/Desktop/blog
desi init
```
Note that you cannot initialize a directory that is already in use
(where 'in use' means has at least one non-dotfile).
### Manual (clone yourself)
There are a number of themes available at <https://github.com/DearDesi>,
just look for ones with 'theme' in the description.
```bash
git clone git@github.com:DearDesi/desirae-blog-template.git ~/my-desirae-blog
pushd ~/my-desirae-blog
git submodule init
git submodule update
git submodule add git@github.com:DearDesi/ruhoh-bootstrap-2.git themes/ruhoh-bootstrap-2
```
And now fire up Dear Desi to get started
You will need to make sure that you have some details about your theme in `config.yml`.
Basically that means that you specify a [`datamap`](https://github.com/DearDesi?query=datamap)
and which defaults for a `page` and `post` in the `layouts` folder.
```
desi serve -d ~/my-desirae-blog
Just open it up, it'll make sense.
### Migrate (import another blog)
Obviously this is a little different for everyone, so here's what I'd recommend:
1. start by following the Automated procedure above
2. copy over your posts/articles folder(s)
3. edit `config.yml` to add a config with a permalink with your collections (posts, articles, essays, whatever you call them)
4. skip ahead to the _Setup your blog_ section and make sure your `site.yml` and `authors/xxx.yml` are correct.
5. run `desi build -d /path/to/blog` to test if there are any issues with your existing yaml
- if there are, you can take a look at the [normalize](https://github.com/DearDesi/desirae/blob/master/lib/transform-core.js#L72) function and perhaps hand-edit a few things (and when you're ready, you can [register your transform](https://github.com/DearDesi/deardesi/blob/master/bin/deardesi.js#L28) for collections.
6. Now copy over your theme and set it to be the default in `site.yml`
7. build again. Your site probably won't look right:
- look for stuff like `urls.base_url`, `host`, `page.url` that might have an extra `/` at the beginning or end or be named slightly differently.
See <https://github.com/DearDesi/desirae/blob/master/GLOSSARY.md> for disambiguation about the meaning of terms in Desi.
## Setup your blog (step 2)
1. Create an authors file in `authors/YOUR_NAME.yml` and model it after [this example](https://github.com/DearDesi/deardesi/blob/master/example/authors/johndoe.yml)
- You don't need to use all of the fields (your template might not even support them all)
2. Create a site file as `site.yml`, similar to [this example](https://github.com/DearDesi/deardesi/blob/master/example/site.yml)
**Important Things**
- `site.yml.base_url` - the point of ownership (usually blog.example.com or example.com)
- `site.yml.base_path` - where the blog is "mounted", relative to the `base_url` (usually `/` or `/blog`)
- `authors/me.yml.name` - most templates use this
- `authors/me.yml.email` - and this
- `authors/me.yml.twitter` - and this
## Build your blog (step 2)
The build _will_ fail if you don't have `site.yml` and `authors/johndoe.yml` configured.
```bash
desi build -d /path/to/blog
desi serve -d /path/to/blog
```
Now open up your evergreen browser to <http://local.dear.desi:65080>
Commandline
===========
# License
Once you've done the initial setup in the browser, you can run `desi` from the commandline
**NOTE:** You can omit the `-d` if you are already in your blog directory.
Build Production Site
---------------------
You must set `base_path` and `base_url` in `site.yml` before attempting to build.
```
desi build -d ~/my-desirae-blog
```
Outputs to `~/my-desirae-blog/compiled`
Create a new Post
-----------------
```
desi post "My First Post" -d ~/my-desirae-blog
```
Outputs to `~/my-desirae-blog/posts/my-first-post.md`
This Source Code Form is subject to the terms of the Mozilla \
Public License, v. 2.0. If a copy of the MPL was not distributed \
with this file, You can obtain one at \
https://mozilla.org/MPL/2.0/.

34
app.js
View File

@ -1,17 +1,21 @@
'use strict';
"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'});
}]);
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" });
},
]);

View File

@ -1,56 +1,182 @@
#!/usr/bin/env node
'use strict';
"use strict";
var PromiseA = require('bluebird')
, fs = PromiseA.promisifyAll(require('fs'))
, path = require('path')
, cli = require('cli')
, UUID = require('node-uuid')
, Desi
;
var existsSync = require("fs").existsSync;
var fs = require("fs").promises;
var tar = require("tar");
var request = require("request");
var forEachAsync = require("foreachasync").forEachAsync;
//; spawn = require('child_process').spawn
var path = require("path");
var cli = require("cli");
var UUID = require("node-uuid");
var Desi;
var zlib = require("zlib");
cli.parse({
blogdir: ['d', 'Where your blog is, i.e. ~/path/to/blog', 'string', './']
//, output: ['o', 'name of output directory within ~/path/to/blog', 'string', './compiled']
blogdir: ["d", "Where your blog is, i.e. ~/path/to/blog", "string", "./"],
//, output: ['o', 'name of output directory within ~/path/to/blog', 'string', './compiled']
});
function init() {
Desi = require('desirae').Desirae;
Desi = require("desirae").Desirae;
Desi.registerDataMapper('ruhoh', require('desirae-datamap-ruhoh').DesiraeDatamapRuhoh);
Desi.registerDataMapper('ruhoh@2.6', require('desirae-datamap-ruhoh').DesiraeDatamapRuhoh);
//
// 1. Transform (yml, slug, etc)
//
Desi.registerTransform(
"lint",
require("desirae/lib/transform-core").DesiraeTransformCore.lint,
{ collections: true }
);
Desi.registerTransform(
"root",
require("desirae/lib/transform-core").DesiraeTransformCore.root,
{ root: true }
);
Desi.registerTransform(
"normalize",
require("desirae/lib/transform-core").DesiraeTransformCore.normalize,
{ root: true, collections: true }
);
Desi.registerTransform(
"disqus",
require("desirae/lib/transform-core").DesiraeTransformCore.disqus,
{ collections: true }
);
//
// 2. Aggregate (rss, categories, tags, etc)
//
Desi.registerAggregator(
require("desirae/lib/aggregate-core").DesiraeAggregateCore.collate
);
//
// 3. Datamap (ruhoh, desirae, jade, mustache, liquid)
//
Desi.registerDataMapper(
"desirae",
require("desirae/lib/datamap-core").DesiraeDatamapCore
);
Desi.registerDataMapper(
"desirae@1.0",
require("desirae/lib/datamap-core").DesiraeDatamapCore
);
// TODO ruhoh versions are ruhoh-twitter (1.0) and ruhoh-boostrap-2 (2.6)
Desi.registerDataMapper(
"ruhoh",
require("desirae-datamap-ruhoh").DesiraeDatamapRuhoh
);
Desi.registerDataMapper(
"ruhoh@1.0",
require("desirae-datamap-ruhoh").DesiraeDatamapRuhoh
);
Desi.registerDataMapper(
"ruhoh@2.6",
require("desirae-datamap-ruhoh").DesiraeDatamapRuhoh
);
//
// 4. Render (md -> html, less -> css, etc)
//
Desi.registerRenderer(
"js",
require("desirae/lib/render-core").DesiraeRenderJs,
{ themes: true, assets: true }
);
Desi.registerRenderer(
"css",
require("desirae/lib/render-core").DesiraeRenderCss,
{ themes: true, assets: true }
);
["html", "htm", "xhtm", "xhtml"].forEach(function (ext) {
Desi.registerRenderer(
ext,
require("desirae/lib/render-core").DesiraeRenderHtml,
{ root: true, collections: true, themes: true, assets: true }
);
});
["md", "markdown", "mdown", "mkdn", "mkd", "mdwn", "mdtxt", "mdtext"].forEach(
function (ext) {
Desi.registerRenderer(
ext,
require("desirae/lib/render-core").DesiraeRenderMarkdown,
{ root: true, collections: true }
);
}
);
Desi.registerRenderer(
"jade",
require("desirae/lib/render-core").DesiraeRenderJade,
// TODO how to support jade in place of Mustache for layouts?
{ root: true, collections: true, themes: true }
);
}
function serve(blogdir) {
var http = require('http')
function serve(displayDir, blogdir) {
var http = require("http"),
//, https = require('https')
, app = require('../server').create({ blogdir: blogdir })
, server
;
app = require("../server").create({ blogdir: blogdir }),
server;
server = http.createServer(app).listen(65080, function () {
console.info("Listening from " + blogdir);
console.info("Listening on http://local.dear.desi:" + server.address().port);
});
server = http
.createServer(app)
.listen(65080, function () {
console.info("Listening from " + displayDir);
console.info(
"Listening on http://local.dear.desi:" + server.address().port
);
})
.on("error", function (err) {
if (/EADDRINUSE/.test(err.message)) {
console.error("");
console.error("You're already running desi in another tab.");
console.error("");
console.error(
"Go to the other tab and press <control> + c to stop her. Then you can come back here to try again."
);
console.error("");
console.error("");
return;
}
throw err;
});
//secureServer = https.createServer(app).listen(65043);
}
function build(blogdir) {
var desi = {}
, env = {}
;
var desi = {},
env = {};
env.working_path = env.blogdir = blogdir;
Desi.init(desi, env).then(function () {
env.url = desi.site.base_url + desi.site.base_path.replace(/^\/$/, '');
env.url = desi.site.base_url + desi.site.base_path.replace(/^\/$/, "");
env.base_url = desi.site.base_url;
env.base_path = desi.site.base_path;
env.compiled_path = 'compiled';
env.compiled_path = "compiled";
//env.since = 0;
Desi.buildAll(desi, env).then(function () {
Desi.write(desi, env).then(function () {
console.info('Built and saved to ' + path.join(env.working_path, env.compiled_path));
Desi.write(desi, env).then(function (info) {
console.info(
"wrote",
info.numFiles,
"files",
"(" +
((info.size && (info.size / (1024 * 1024)).toFixed(2)) || "unkown"),
"MiB)",
"in",
((info.end - info.start) / 1000).toFixed(3) + "s"
);
console.info(
"Built and saved to " + path.join(env.working_path, env.compiled_path)
);
});
});
});
@ -58,27 +184,33 @@ function build(blogdir) {
function createPost(originalDir, blogdir, title, extra) {
if (!title) {
console.error("Usage desi post \"My First Post\"");
console.error('Usage desi post "My First Post"');
console.error("(you didn't specify a title)");
process.exit(1);
}
if (extra) {
console.error("Usage desi post \"My First Post\"");
console.error("(too many arguments - maybe you didn't put your title in quotes?)");
console.error('Usage desi post "My First Post"');
console.error(
"(too many arguments - maybe you didn't put your title in quotes?)"
);
process.exit(1);
}
var env = {}
, post = {}
, slug
, filepath
, displaypath
;
var desi = {},
env = {},
post = {},
slug,
filepath,
displaypath;
env.working_path = env.blogdir = blogdir;
Desi._initFileAdapter(env).then(function () {
/*
Desi.init(desi, env).then(function () {
// TODO move this 'create new post' logic to desirae proper
var collectionname = Object.keys(desi.config.collections)[0],
collection = desi.config.collections[collectionname],
entity = {};
/*
Desi.init(desi, env).then(function () {
env.url = desi.site.base_url + desi.site.base_path.replace(/^\/$/, '');
env.base_url = desi.site.base_url;
@ -87,114 +219,232 @@ function createPost(originalDir, blogdir, title, extra) {
//env.since = 0;
*/
// TODO move this logic to desirae
post.title = title;
post.description = "";
post.date = Desi.toLocaleDate(new Date());
// TODO use site.permalink or collection.permalink or something like that
slug = post.title.toLowerCase()
.replace(/["']/g, '')
.replace(/\W/g, '-')
.replace(/^-+/g, '')
.replace(/-+$/g, '')
.replace(/--/g, '-')
;
slug = Desi.slugify(post.title);
// TODO as per config
post.permalink = path.join('/', 'articles', slug + '.html');
post.uuid = UUID.v4();
// TODO as per config for default collection and default format (jade, md, etc)
filepath = path.join(blogdir, (/*config.collection ||*/ 'posts'), slug + '.md');
displaypath = path.join(originalDir, 'posts', slug + '.md').replace(/^\/(Users|home)\/[^\/]+\//, '~/').replace(/ /g, '\\ ');
filepath = path.join(blogdir, collectionname, slug + ".md");
displaypath = path
.join(originalDir, "posts", slug + ".md")
.replace(/^\/(Users|home)\/[^\/]+\//, "~/")
.replace(/ /g, "\\ ");
['updated', 'theme', 'layout', 'swatch'].forEach(function (key) {
["updated", "theme", "layout", "swatch"].forEach(function (key) {
if (!post[key]) {
delete post[key];
}
});
return Desi.fsapi.putFiles([{
path: filepath
, contents:
'---\n'
+ Desi.YAML.stringify(post).trim()
+ '\n'
+ '---\n'
+ '\n'
+ '\n'
}], { overwrite: false }).then(function (r) {
var err
;
Object.keys(post).forEach(function (key) {
entity[key] = post[key];
});
entity.slug = slug;
if (r.error || r.errors.length) {
err = r.error || r.errors[0];
if (/exists/i.test(err.message)) {
console.error('');
console.error("Looks like that post already exists. Try a different name?");
console.error('');
console.error('');
} else {
throw err;
post.permalink = Desi.permalinkify(
desi,
collection.fallback_permalink || collection.permalink,
entity
);
return Desi.fsapi
.putFiles(
[
{
path: filepath,
contents:
"---\n" +
Desi.YAML.stringify(post).trim() +
"\n" +
"---\n" +
"\n" +
"\n",
},
],
{ overwrite: false }
)
.then(function (r) {
var err;
if (r.error || r.errors.length) {
err = r.error || r.errors[0];
if (/exists/i.test(err.message)) {
console.error("");
console.error(
"Looks like that post already exists. Try a different name?"
);
console.error("");
console.error("");
} else {
throw err;
}
}
return;
}
console.log('');
console.log(displaypath);
console.log('');
console.log('vim ' + displaypath);
console.log('(or emacs ' + displaypath + ', if you swing that way)');
console.log('');
console.log('');
});
/*
console.log("");
console.log(displaypath);
console.log("");
console.log(
"Markdown: [" +
post.title +
"](" +
desi.site.base_url +
path.join(desi.site.base_path, post.permalink) +
")"
);
console.log(
'HTML: <a href="' +
desi.site.base_url +
path.join(desi.site.base_path, post.permalink) +
'">' +
post.title +
"</a>"
);
console.log("");
console.log("");
console.log("vim " + displaypath);
console.log("(or emacs " + displaypath + ", if you swing that way)");
console.log("");
console.log("");
});
/*
});
*/
});
}
function initialize(displayPath, blogdir) {
console.info("\nCreating new blog", displayPath);
return fs
.readdirAsync(blogdir)
.then(function (nodes) {
// ignore dotfiles (.DS_Store, etc)
nodes = nodes.filter(function (node) {
return !/^\./.test(node);
});
if (nodes.length) {
console.error(
"\n\tOops! It looks like that directory is already being used"
);
console.error(
"\nIf you want you can DELETE it and start from scratch:"
);
console.error("\n\trm -r '" + blogdir.replace("'", "'\"'\"'") + "'");
console.error("\nOr you can specify a different directory.");
console.error("\n");
process.exit(1);
}
})
.catch(function (/*err*/) {
// doesn't exist? No problamo (all the better, actually)
return;
})
.then(function () {
return fs.mkdir(blogdir, { recursive: true });
})
.then(function () {
return new Promise(function (resolve, reject) {
var t = tar.Extract({ path: blogdir, strip: 1 }),
gunzip = zlib.createGunzip();
console.info("Downloading blog template...", displayPath);
request
.get(
"https://github.com/DearDesi/desirae-blog-template/archive/v1.1.0.tar.gz"
)
.pipe(gunzip)
.pipe(t)
.on("end", resolve)
.on("error", reject);
});
})
.then(function () {
var themes;
themes = [
{
name: "ruhoh-twitter",
url:
"https://github.com/DearDesi/ruhoh-twitter/archive/v1.0.0.tar.gz",
},
{
name: "ruhoh-bootstrap-2",
url:
"https://github.com/DearDesi/ruhoh-bootstrap-2/archive/v1.0.1.tar.gz",
},
];
return forEachAsync(themes, function (theme) {
return new Promise(function (resolve, reject) {
var t = tar.Extract({
path: path.join(blogdir, "themes", theme.name),
strip: 1,
}),
gunzip = zlib.createGunzip();
console.info("Downloading theme '" + theme.name + "'");
request
.get(theme.url)
.pipe(gunzip)
.pipe(t)
.on("end", resolve)
.on("error", reject);
});
});
})
.then(function () {
console.info("Done.");
console.info("\nTo start the web editor run this:");
console.info(
"\n\tdesi serve -d '" + blogdir.replace("'", "'\"'\"'") + "'"
);
});
}
cli.main(function (args, options) {
init();
var command = args[0]
, blogdir = options.blogdir
, originalDir = blogdir
;
var command = args[0],
blogdir = options.blogdir,
originalDir = blogdir,
displayPath;
if (!blogdir) {
blogdir = path.resolve('./');
originalDir = './';
blogdir = path.resolve("./");
originalDir = "./";
}
if (!fs.existsSync(path.join(options.blogdir, 'site.yml'))) {
displayPath = path
.resolve(originalDir)
.replace(/^\/(Users|home)\/[^\/]+\//, "~/")
.replace(/ /g, "\\ ");
if ("init" === command) {
initialize(displayPath, blogdir);
return;
}
if (!existsSync(path.join(blogdir, "site.yml"))) {
console.error("Usage: desi [serve|init|post] -d ~/path/to/blog");
console.error("(if ~/path/to/blog doesn't yet exist or doesn't have config.yml, site.yml, etc, "
+ "try `deardesi init -d ~/path/to/blog'");
console.error(
"(if ~/path/to/blog doesn't yet exist or doesn't have config.yml, site.yml, etc, " +
"try `deardesi init -d ~/path/to/blog'"
);
process.exit(1);
return;
}
if ('init' === command) {
console.error("`init' not yet implemented");
process.exit(1);
return;
}
else if ('build' === command) {
} else if ("build" === command) {
build(blogdir);
return;
}
else if ('post' === command) {
} else if ("post" === command) {
createPost(originalDir, blogdir, args[1], args[2]);
return;
}
else if ('serve' === command) {
serve(blogdir);
} else if ("serve" === command) {
serve(displayPath, blogdir);
return;
}
else {
} else {
console.error("Usage: desi [serve|init|post] -d ~/path/to/blog");
return;
}

View File

@ -1,15 +1,10 @@
{
"name": "deardesi",
"version": "0.1.0",
"authors": [
"AJ ONeal <awesome@coolaj86.com>"
],
"version": "0.8.5",
"authors": ["AJ ONeal <awesome@coolaj86.com>"],
"description": "A blogging platform in the browser. Wow!",
"main": "deardesi.js",
"moduleType": [
"globals",
"node"
],
"moduleType": ["globals", "node"],
"keywords": [
"dear",
"desi",
@ -19,15 +14,9 @@
"platform",
"browser"
],
"license": "Apache2",
"license": "MPL-2.0",
"homepage": "http://github.com/DearDesi/deardesi",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"ignore": ["**/.*", "node_modules", "bower_components", "test", "tests"],
"dependencies": {
"escape-string-regexp": "~1.0.2",
"path": "~3.46.1",
@ -38,7 +27,8 @@
"html5-boilerplate": "~4.3.0",
"bootstrap": "~3.3.1",
"md5": "~0.1.3",
"desirae": "~0.9.6",
"desirae-datamap-ruhoh": "~1.0.0"
"desirae": "~0.11.1",
"desirae-datamap-ruhoh": "~1.0.3",
"jade": "~1.9.1"
}
}

View File

@ -1,15 +1,145 @@
angular.module('myApp.services', []).
factory('Desirae', ['$q', '$http', function ($q, $http) {
var Desi = window.Desirae || require('./deardesi').Desirae
, desi = {/*TODO api_base: '/api'*/}
;
angular.module("myApp.services", []).factory("Desirae", [
"$q",
"$http",
function ($q, $http) {
var Desi = window.Desirae || require("./deardesi").Desirae,
desi = {
/*TODO api_base: '/api'*/
};
//
// 1. Transform (yml, slug, etc)
//
Desi.registerTransform("lint", window.DesiraeTransformCore.lint, {
collections: true,
});
Desi.registerTransform("root", window.DesiraeTransformCore.root, {
root: true,
});
Desi.registerTransform("normalize", window.DesiraeTransformCore.normalize, {
root: true,
collections: true,
});
Desi.registerTransform("disqus", window.DesiraeTransformCore.disqus, {
collections: true,
});
// TODO what version of ruhoh are ruhoh-twitter and ruhoh-boostrap-2
Desi.registerDataMapper('ruhoh', window.DesiraeDatamapRuhoh || require('desirae-datamap-ruhoh').DesiraeDatamapRuhoh);
Desi.registerDataMapper('ruhoh@2.6', window.DesiraeDatamapRuhoh || require('desirae-datamap-ruhoh').DesiraeDatamapRuhoh);
//
// 2. Aggregate (rss, categories, tags, etc)
//
Desi.registerAggregator(window.DesiraeAggregateCore.collate);
function getBlogdir () {
return $http.get('/api/fs/rootdir').then(function (resp) {
//
// 3. Datamap (ruhoh, desirae, jade, mustache, liquid)
//
Desi.registerDataMapper("desirae", window.DesiraeDatamapCore);
Desi.registerDataMapper("desirae@1.0", window.DesiraeDatamapCore);
Desi.registerDataMapper("ruhoh", window.DesiraeDatamapRuhoh);
Desi.registerDataMapper("ruhoh@1.0", window.DesiraeDatamapRuhoh);
Desi.registerDataMapper("ruhoh@2.6", window.DesiraeDatamapRuhoh);
//
// 4. Render (md -> html, less -> css, etc)
//
Desi.registerRenderer("js", window.DesiraeRenderCss, {
themes: true,
assets: true,
});
Desi.registerRenderer("css", window.DesiraeRenderCss, {
themes: true,
assets: true,
});
["html", "htm", "xhtm", "xhtml"].forEach(function (ext) {
Desi.registerRenderer(ext, window.DesiraeRenderHtml, {
root: true,
collections: true,
themes: true,
assets: true,
});
});
[
"md",
"markdown",
"mdown",
"mkdn",
"mkd",
"mdwn",
"mdtxt",
"mdtext",
].forEach(function (ext) {
Desi.registerRenderer(ext, window.DesiraeRenderMarkdown, {
root: true,
collections: true,
});
});
Desi.registerRenderer("jade", window.DesiraeRenderJade, {
root: true,
collections: true,
themes: true,
});
function gdrive2host(str) {
// https://drive.google.com/folderview?id=0ByLnfhJOd1-baUh1Wms0US16QkE&usp=sharing
// https://googledrive.com/host/0ByLnfhJOd1-baUh1Wms0US16QkE
var m;
str = str || "";
m = str.match(/(?=drive.*google|google.*drive).*folderview.*id=([^&]+)/i);
console.log(m);
if (m && m[1]) {
return "https://googledrive.com/host/" + m[1];
}
}
function dropbox2host(str) {
if (!/dropbox/.test(str)) {
return;
}
// https://dl.dropboxusercontent.com/u/146173/index.html
// https://www.dropbox.com/s/3n20djtrs2p0j9k
// https://www.dropbox.com/s/3n20djtrs2p0j9k/index.html?dl=0
// https://dl.dropboxusercontent.com/s/3n20djtrs2p0j9k/index.html
str = str || "";
if (!str.match(/dropboxusercontent\.com\/u\/([^\/]+)\/index.html/)) {
window.alert(
"Sorry, Desi can't use that type of dropbox link." +
"\n\n1. Open the Dropbox folder on your computer" +
"\n (The DropBox app must be installed)" +
"\n\n2. Open the Public folder" +
"\n (if you don't have a Public folder, your account doesn't support hosting websites and you're simply out of luck)" +
"\n\n2. Create a new file called index.html" +
"\n\n3. Right-click on index.html" +
"\n\n4. Select 'Copy Public Link'" +
"\n\n5. Paste that link as the URL for Desi"
);
return;
}
return str.replace(/\/index\.html$/, "");
}
function splitUrl(str) {
var m;
str = str || "";
m = str.match(/(https?:\/\/)?([^\.\/?#]+\.[^\/?#]+)(\/[^#?]+)?/i);
console.log(m);
if (!m || !m[2]) {
return;
}
return {
baseUrl: (m[1] || "http://") + m[2],
basePath: (m[3] && m[3].replace(/\/$/, "")) || "/",
};
}
function getBlogdir() {
return $http.get("/api/fs/rootdir").then(function (resp) {
desi.blogdir = resp.data;
return resp.data;
});
@ -17,15 +147,16 @@ angular.module('myApp.services', []).
getBlogdir();
return {
splitUrl: splitUrl,
gdrive2host: gdrive2host,
dropbox2host: dropbox2host,
reset: function () {
desi = {};
return getBlogdir();
}
, toDesiDate: Desi.toLocaleDate
, meta: function () {
var d = $q.defer()
;
},
toDesiDate: Desi.toLocaleDate,
meta: function () {
var d = $q.defer();
if (desi.meta) {
d.resolve(desi);
return d.promise;
@ -36,11 +167,9 @@ angular.module('myApp.services', []).
});
return d.promise;
}
, build: function (env) {
var d = $q.defer()
;
},
build: function (env) {
var d = $q.defer();
if (desi.built) {
d.resolve(desi);
return d.promise;
@ -51,11 +180,9 @@ angular.module('myApp.services', []).
});
return d.promise;
}
, write: function (env) {
var d = $q.defer()
;
},
write: function (env) {
var d = $q.defer();
if (desi.written) {
d.resolve(desi);
return d.promise;
@ -66,10 +193,10 @@ angular.module('myApp.services', []).
});
return d.promise;
}
, putFiles: function (files) {
},
putFiles: function (files) {
return $q.when(Desi.fsapi.putFiles(files));
}
},
};
}]
);
},
]);

View File

@ -1,9 +1,13 @@
'use strict';
"use strict";
angular.module('myApp.version.interpolate-filter', [])
angular
.module("myApp.version.interpolate-filter", [])
.filter('interpolate', ['version', function(version) {
return function(text) {
return String(text).replace(/\%VERSION\%/mg, version);
};
}]);
.filter("interpolate", [
"version",
function (version) {
return function (text) {
return String(text).replace(/\%VERSION\%/gm, version);
};
},
]);

View File

@ -1,15 +1,19 @@
'use strict';
"use strict";
describe('myApp.version module', function() {
beforeEach(module('myApp.version'));
describe("myApp.version module", function () {
beforeEach(module("myApp.version"));
describe('interpolate filter', function() {
beforeEach(module(function($provide) {
$provide.value('version', 'TEST_VER');
}));
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');
it("should replace VERSION", inject(function (interpolateFilter) {
expect(interpolateFilter("before %VERSION% after")).toEqual(
"before TEST_VER after"
);
}));
});
});

View File

@ -1,9 +1,13 @@
'use strict';
"use strict";
angular.module('myApp.version.version-directive', [])
angular
.module("myApp.version.version-directive", [])
.directive('appVersion', ['version', function(version) {
return function(scope, elm, attrs) {
elm.text(version);
};
}]);
.directive("appVersion", [
"version",
function (version) {
return function (scope, elm, attrs) {
elm.text(version);
};
},
]);

View File

@ -1,16 +1,16 @@
'use strict';
"use strict";
describe('myApp.version module', function() {
beforeEach(module('myApp.version'));
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');
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');
inject(function ($compile, $rootScope) {
var element = $compile("<span app-version></span>")($rootScope);
expect(element.text()).toEqual("TEST_VER");
});
});
});

View File

@ -1,8 +1,9 @@
'use strict';
"use strict";
angular.module('myApp.version', [
'myApp.version.interpolate-filter',
'myApp.version.version-directive'
])
angular
.module("myApp.version", [
"myApp.version.interpolate-filter",
"myApp.version.version-directive",
])
.value('version', '0.8.0');
.value("version", "0.8.0");

View File

@ -1,11 +1,11 @@
'use strict';
"use strict";
describe('myApp.version module', function() {
beforeEach(module('myApp.version'));
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');
describe("version service", function () {
it("should return current version", inject(function (version) {
expect(version).toEqual("0.1");
}));
});
});

View File

@ -1,12 +1,8 @@
'use strict';
var fsapi = require('desirae/lib/node-adapters').fsapi
;
"use strict";
var fsapi = require("desirae/lib/node-adapters").fsapi;
module.exports.create = function (options) {
var restful = {}
;
var restful = {};
//
// Required for desirae
//
@ -16,18 +12,24 @@ module.exports.create = function (options) {
return;
}
var opts = {}
, dirnames = req.query.dir && [req.query.dir] || (req.query.dirs && req.query.dirs.split(/,/g)) || req.body.dirs
;
var opts = {},
dirnames =
(req.query.dir && [req.query.dir]) ||
(req.query.dirs && req.query.dirs.split(/,/g)) ||
req.body.dirs;
if (!dirnames || !dirnames.length) {
res.send({ error: "please specify GET w/ req.query.dir or POST w/ _method=GET&dirs=path/to/thing,..." });
res.send({
error:
"please specify GET w/ req.query.dir or POST w/ _method=GET&dirs=path/to/thing,...",
});
return;
}
if (!dirnames.every(function (dirname) {
return 'string' === typeof dirname;
})) {
if (
!dirnames.every(function (dirname) {
return "string" === typeof dirname;
})
) {
res.send({ error: "malformed request: " + JSON.stringify(dirnames) });
return;
}
@ -42,13 +44,13 @@ module.exports.create = function (options) {
opts.extensions = req.query.extensions.split(/,/g);
}
if ('true' === req.query.dotfiles) {
if ("true" === req.query.dotfiles) {
opts.dotfiles = true;
}
if ('false' === req.query.sha1sum) {
if ("false" === req.query.sha1sum) {
opts.sha1sum = false;
}
if ('true' === req.query.contents) {
if ("true" === req.query.contents) {
opts.contents = true;
}
@ -68,11 +70,15 @@ module.exports.create = function (options) {
return;
}
var filepaths = req.query.path && [req.query.path] || (req.query.paths && req.query.paths.split(/,/g)) || req.body.paths
;
var filepaths =
(req.query.path && [req.query.path]) ||
(req.query.paths && req.query.paths.split(/,/g)) ||
req.body.paths;
if (!filepaths || !filepaths.length) {
res.send({ error: "please specify GET w/ req.query.path or POST _method=GET&paths=path/to/thing,..." });
res.send({
error:
"please specify GET w/ req.query.path or POST _method=GET&paths=path/to/thing,...",
});
return;
}
@ -86,15 +92,15 @@ module.exports.create = function (options) {
};
restful.putFiles = function (req, res, next) {
if (!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))) {
if (
!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))
) {
next();
return;
}
var opts = {}
, files = req.body.files
;
var opts = {},
files = req.body.files;
if (!files || !files.length) {
res.send({ error: "please specify POST w/ req.body.files" });
return;
@ -107,16 +113,16 @@ module.exports.create = function (options) {
};
restful.copy = function (req, res, next) {
if (!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))) {
if (
!(/^POST|PUT$/i.test(req.method) || /^POST|PUT$/i.test(req.query._method))
) {
next();
return;
}
var opts = {}
, files = req.body.files
;
if ('object' !== typeof files || !Object.keys(files).length) {
var opts = {},
files = req.body.files;
if ("object" !== typeof files || !Object.keys(files).length) {
res.send({ error: "please specify POST w/ req.body.files" });
return;
}

View File

@ -0,0 +1,13 @@
name : JohnDoe
handle : dohnjoe
email : "dohnjoe@gmail.com"
github : dohnjoe
twitter : dohnjoe
facebook : dohnjoe.691
pintrest : dohnjoe.691
stackoverflow : 4242
googleplus : 42374237423742374237
twitter_id : 424242423737
feedburner : dohnjoe
website : "http://johndoe.com"
bio : "Live, Love, and Work Boston, MA. Yeah!"

8
example/site.yml Normal file
View File

@ -0,0 +1,8 @@
title : John Doe
tagline : "I'm an adult and I expect to be read like one!"
description : The one stop source for all things John Doe - my loves, hates, ramblingis, musings, and general misperadventurehaps.
base_url : "http://school.edu/~/johndoe"
base_path : /blog/
google_analytics_tracking_id : UA-XXXXXXXX-1
disqus_shortname : johndoe691-blog
theme : ruhoh-twitter

View File

@ -2,121 +2,190 @@
<!--[if lt IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html lang="en" ng-app="myApp" class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html lang="en" ng-app="myApp" class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!--[if gt IE 8]><!-->
<html lang="en" ng-app="myApp" class="no-js">
<!--<![endif]-->
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Dear Desi - Static Blog Generator</title>
<meta name="description" content="Desi is a static blog generator that is built for the browser and also works in node.js">
<title>Desi, the DIY Blog Platform</title>
<meta
name="description"
content="Desi is the DIY blog platform for normal people. You can build your blog with Desi in your browser and then host it on Google Drive, DropBox, Github, or a good ol' fashion web host via ftp."
/>
<!-- Style -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="bower_components/html5-boilerplate/css/normalize.css">
<link rel="stylesheet" href="bower_components/html5-boilerplate/css/main.css">
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css"><!-- just as a fallback -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.1/spacelab/bootstrap.min.css">
<!-- Style -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="bower_components/html5-boilerplate/css/normalize.css"
/>
<link
rel="stylesheet"
href="bower_components/html5-boilerplate/css/main.css"
/>
<link
rel="stylesheet"
href="bower_components/bootstrap/dist/css/bootstrap.min.css"
/>
<!-- just as a fallback -->
<link
rel="stylesheet"
href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.1/spacelab/bootstrap.min.css"
/>
<script src="bower_components/html5-boilerplate/js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<script src="bower_components/html5-boilerplate/js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="browsehappy">
You are using an <strong>outdated</strong> browser. Please
<a href="http://browsehappy.com/">upgrade your browser</a> to improve
your experience.
</p>
<![endif]-->
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<!--.navbar.navbar-default.navbar-fixed-top-->
<div
style="margin-bottom: 0; border-top-width: 0"
class="navbar navbar-default"
>
<div class="container">
<div class="navbar-header">
<div
style="padding-top: 9px; padding-left: 9px; padding-bottom: 9px"
class="pull-left"
>
<img
ng-src="http://dropsha.re/files/VY15+v8/desirae-parker-crop.jpg"
style="border: 1px solid grey; height: 54px; width: 54px"
class="navbar-logo"
/>
</div>
<a
href="#/"
style="padding-top: 23px; padding-left: 25px"
class="navbar-brand"
>Desi</a
>
<!--.navbar.navbar-default.navbar-fixed-top-->
<div style="margin-bottom: 0; border-top-width: 0;" class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<div style="padding-top: 9px; padding-left: 9px; padding-bottom: 9px;" class="pull-left"><img ng-src="http://dropsha.re/files/VY15+v8/desirae-parker-crop.jpg" style="border: 1px solid grey; height: 54px; width: 54px;" class="navbar-logo"/></div><a href="#/" style="padding-top: 23px; padding-left: 25px;" class="navbar-brand">Desi</a>
<div style="padding-top: 9px">
<button
type="button"
ng-init="navCollapsed = true"
ng-click="navCollapsed = !navCollapsed"
class="navbar-toggle"
>
<span class="icon-bar"></span><span class="icon-bar"></span
><span class="icon-bar"></span>
</button>
</div>
</div>
<div style="padding-top: 9px;">
<button type="button" ng-init="navCollapsed = true" ng-click="navCollapsed = !navCollapsed" class="navbar-toggle"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button>
<div
id="navbar-main"
ng-class="!navCollapsed &amp;&amp; 'in'"
class="navbar-collapse collapse"
>
<ul style="padding-top: 9px" class="nav navbar-nav">
<li><a href="#/authors">Authors</a></li>
<li><a href="#/site">Site</a></li>
<li><a href="#/post">Post</a></li>
<li><a href="#/build">Build</a></li>
</ul>
</div>
</div>
<div id="navbar-main" ng-class="!navCollapsed &amp;&amp; 'in'" class="navbar-collapse collapse">
<ul style="padding-top: 9px;" class="nav navbar-nav">
<li><a href="#/authors">Authors</a></li>
<li><a href="#/site">Site</a></li>
<li><a href="#/post">Post</a></li>
<li><a href="#/build">Build</a></li>
</ul>
</div>
</div>
</div>
<div ng-view></div>
<div ng-view></div>
<footer>
<center>
<p>
<!-- http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html -->
<!-- also needs updating at http://plus.google.com/me/about/edit/co -->
<a href="mailto:develop@dear.desi" rel="me">develop@dear.desi</a>
|
<a href="https://twitter.com/dearbesiblog?rel=author" rel="me">Twitter</a>
<!--a href="https://www.facebook.com/coolaj86?rel=author" rel="me">Facebook</a-->
|
<a href="https://github.com/DearDesi/desirae?rel=author" rel="me">Github</a> (v<span app-version></span>)
<!--a href="https://plus.google.com/111222501744950155474?rel=author" data-user="AJ ONeal" rel="me">Google+</a -->
|
<a href="#screencast" rel="me">YouTube</a>
|
<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2 License</a>
|
<a href="http://opensource.org/licenses/MIT">MIT License</a>
</p>
<p>© AJ ONeal Tech LLC 2015
with help from
<a href="http://angularjs.org" target="_blank" title="Superheroic JavaScript MVW Framework">AngularJS</a>,
<a href="http://nodejs.org" target="_blank"
title="Open source, cross-platform server-side JavaScript runtime environment">node.js</a>,
and <a href="http://twitter.github.com/bootstrap/" target="_blank">Twitter Bootstrap</a>
</p>
</center>
</footer>
<footer>
<center>
<p>
<!-- http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html -->
<!-- also needs updating at http://plus.google.com/me/about/edit/co -->
<a href="mailto:develop@dear.desi" rel="me">develop@dear.desi</a>
|
<a href="https://twitter.com/dearbesiblog?rel=author" rel="me"
>Twitter</a
>
<!--a href="https://www.facebook.com/coolaj86?rel=author" rel="me">Facebook</a-->
|
<a href="https://github.com/DearDesi/desirae?rel=author" rel="me"
>Github</a
>
(v<span app-version></span>)
<!--a href="https://plus.google.com/111222501744950155474?rel=author" data-user="AJ ONeal" rel="me">Google+</a -->
|
<a href="https://www.youtube.com/watch?v=pFAKAWP_sec" rel="me"
>YouTube</a
>
|
<a href="http://www.apache.org/licenses/LICENSE-2.0"
>Apache 2 License</a
>
|
<a href="http://opensource.org/licenses/MIT">MIT License</a>
</p>
<p>
© AJ ONeal Tech LLC 2015 with help from
<a
href="http://angularjs.org"
target="_blank"
title="Superheroic JavaScript MVW Framework"
>AngularJS</a
>,
<a
href="http://iojs.org"
target="_blank"
title="Open source, cross-platform server-side JavaScript runtime environment"
>io.js</a
>, and
<a href="http://twitter.github.com/bootstrap/" target="_blank"
>Twitter Bootstrap</a
>
</p>
</center>
</footer>
<!-- fork me on github sticker -->
<a href="https://github.com/DearDesi/desirae"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
<!-- Deps -->
<script src="./bower_components/mustache/mustache.js"></script>
<script src="./bower_components/markdown-it/dist/markdown-it.js"></script>
<script src="./bower_components/jade/jade.js"></script>
<script src="./bower_components/js-yaml/dist/js-yaml.js"></script>
<script src="./bower_components/path/path.js"></script>
<script src="./bower_components/node-uuid/uuid.js"></script>
<script src="./bower_components/forEachAsync/forEachAsync.js"></script>
<!-- Libs -->
<script src="./bower_components/desirae/lib/verify-config.js"></script>
<!-- Desi -->
<script src="./bower_components/desirae/desirae.js"></script>
<script src="./bower_components/desirae/lib/frontmatter.js"></script>
<script src="./bower_components/desirae/lib/browser-adapters.js"></script>
<script src="./bower_components/desirae/lib/utils.js"></script>
<script src="./bower_components/desirae/lib/transform-core.js"></script>
<script src="./bower_components/desirae/lib/aggregate-core.js"></script>
<script src="./bower_components/desirae/lib/datamap-core.js"></script>
<script src="./bower_components/desirae/lib/render-core.js"></script>
<script src="./bower_components/desirae-datamap-ruhoh/datamapper-ruhoh.js"></script>
<!-- Deps -->
<script src="./bower_components/bluebird/js/browser/bluebird.js"></script>
<script src="./bower_components/mustache/mustache.js"></script>
<script src="./bower_components/markdown-it/dist/markdown-it.js"></script>
<script src="./bower_components/js-yaml/dist/js-yaml.js"></script>
<script src="./bower_components/path/path.js"></script>
<script src="./bower_components/node-uuid/uuid.js"></script>
<script src="./bower_components/forEachAsync/forEachAsync.js"></script>
<!-- Libs -->
<script src="./bower_components/desirae/lib/verify-config.js"></script>
<!-- Desi -->
<script src="./bower_components/desirae/desirae.js"></script>
<script src="./bower_components/desirae/lib/frontmatter.js"></script>
<script src="./bower_components/desirae/lib/browser-adapters.js"></script>
<script src="./bower_components/desirae/lib/utils.js"></script>
<script src="./bower_components/desirae-datamap-ruhoh/datamapper-ruhoh.js"></script>
<!-- UX Using Angular, but not getting fancy -->
<script src="./bower_components/angular/angular.js"></script>
<script src="./bower_components/angular-route/angular-route.js"></script>
<script src="./bower_components/md5/build/md5.min.js"></script>
<script src="./app.js"></script>
<script src="./views/about/about.js"></script>
<script src="./views/authors/authors.js"></script>
<script src="./views/site/site.js"></script>
<script src="./views/configure/configure.js"></script>
<script src="./views/build/build.js"></script>
<script src="./views/post/post.js"></script>
<script src="components/desirae/desirae.js"></script>
<script src="components/version/version.js"></script>
<script src="components/version/version-directive.js"></script>
<script src="components/version/interpolate-filter.js"></script>
</body>
<!-- UX Using Angular, but not getting fancy -->
<script src="./bower_components/angular/angular.js"></script>
<script src="./bower_components/angular-route/angular-route.js"></script>
<script src="./bower_components/md5/build/md5.min.js"></script>
<script src="./app.js"></script>
<script src="./views/about/about.js"></script>
<script src="./views/authors/authors.js"></script>
<script src="./views/site/site.js"></script>
<script src="./views/configure/configure.js"></script>
<script src="./views/build/build.js"></script>
<script src="./views/post/post.js"></script>
<script src="components/desirae/desirae.js"></script>
<script src="components/version/version.js"></script>
<script src="components/version/version-directive.js"></script>
<script src="components/version/interpolate-filter.js"></script>
</body>
</html>

1282
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,10 @@
{
"name": "desi",
"version": "0.5.6",
"version": "0.9.1",
"description": "A nice, friendly tool to help you get set up and start blogging, built on the Desirae blogging platform",
"main": "server.js",
"scripts": {
"prettier": "prettier --write './**/*.{js,css,json,md,html}'",
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
@ -12,7 +13,7 @@
},
"repository": {
"type": "git",
"url": "https://github.com/DearDesi/deardesi.git"
"url": "https://git.coolaj86.com/coolaj86/deardesi.js.git"
},
"keywords": [
"dear",
@ -30,25 +31,27 @@
"nanoc",
"octopress"
],
"author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.com/)",
"license": "Apache2",
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0",
"bugs": {
"url": "https://github.com/DearDesi/deardesi/issues"
"url": "https://git.coolaj86.com/coolaj86/deardesi.js/issues"
},
"homepage": "https://github.com/DearDesi/deardesi",
"homepage": "https://git.coolaj86.com/coolaj86/deardesi.js",
"dependencies": {
"bluebird": "^2.6.4",
"body-parser": "^1.10.1",
"cli": "^0.6.5",
"compression": "^1.3.0",
"connect": "^3.3.4",
"connect-query": "^0.2.0",
"connect-send-json": "^1.0.0",
"desirae": "^0.9.6",
"desirae-datamap-ruhoh": "^1.0.0",
"fs.extra": "^1.3.0",
"desirae": "^0.12.4",
"desirae-datamap-ruhoh": "v1.0.6",
"foreachasync": "^5.0.5",
"jade": "^1.9.1",
"node-uuid": "^1.4.2",
"request": "^2.88.2",
"require-yaml": "0.0.1",
"serve-static": "^1.8.0"
"serve-static": "^1.8.0",
"tar": "^1.0.3"
}
}

View File

@ -1,36 +1,31 @@
'use strict';
"use strict";
var path = require('path');
var path = require("path");
function create(options) {
var connect = require('connect')
, query = require('connect-query')
, bodyParser = require('body-parser')
, serveStatic = require('serve-static')
, send = require('connect-send-json')
, app = connect()
, restful = require('./desirae-http-api').create(options)
;
var connect = require("connect"),
query = require("connect-query"),
bodyParser = require("body-parser"),
serveStatic = require("serve-static"),
send = require("connect-send-json"),
app = connect(),
restful = require("./desirae-http-api").create(options);
app
.use(send.json())
.use(query())
.use(bodyParser.json({ limit: 10 * 1024 * 1024 })) // 10mb
.use(require('compression')())
;
.use(require("compression")());
//
// Keeping the API *required* by desirae super minimal
// so that it can be implemented easily in any language
//
app
.use('/api/fs/static', serveStatic(options.blogdir))
.use('/api/fs/walk', restful.walk)
.use('/api/fs/files', restful.getFiles)
.use('/api/fs/files', restful.putFiles)
.use('/api/fs/copy', restful.copy)
;
.use("/api/fs/static", serveStatic(options.blogdir))
.use("/api/fs/walk", restful.walk)
.use("/api/fs/files", restful.getFiles)
.use("/api/fs/files", restful.putFiles)
.use("/api/fs/copy", restful.copy);
// end Desirae API
if (options.tmpdir) {
@ -38,32 +33,30 @@ function create(options) {
}
// this is used by DearDesi, but not required for desirae
app
.use('/api/fs/rootdir', function (req, res) {
var pathname = path.resolve(options.blogdir)
;
res.send({
path: pathname
, name: path.basename(pathname)
, relativePath: path.dirname(pathname)
});
return;
})
;
app.use("/api/fs/rootdir", function (req, res) {
var pathname = path.resolve(options.blogdir);
res.send({
path: pathname,
name: path.basename(pathname),
relativePath: path.dirname(pathname),
});
return;
});
app
// the AngularJS App
.use(serveStatic(__dirname))
// TODO change file requests to '/blog'
//.use(serveStatic(options.blogdir))
.use('/blog', serveStatic(options.blogdir))
.use('/compiled_dev', serveStatic(path.join(options.blogdir, '/compiled_dev')))
.use('/compiled', serveStatic(path.join(options.blogdir, '/compiled')))
;
.use("/blog", serveStatic(options.blogdir))
.use(
"/compiled_dev",
serveStatic(path.join(options.blogdir, "/compiled_dev"))
)
.use("/compiled", serveStatic(path.join(options.blogdir, "/compiled")));
return app;
}
module.exports = create({ blogdir: path.join(__dirname, 'blog') });
module.exports = create({ blogdir: path.join(__dirname, "blog") });
module.exports.create = create;

45
setup.sh Normal file
View File

@ -0,0 +1,45 @@
#!/bin/bash
if [ -n "$(which curl)" ]; then
CMD_CURL="curl -fsSL"
elif [ -n "$(which wget)" ]; then
CMD_CURL="wget -nv --quiet --no-verbose -O -"
else
echo "Couldn't find 'curl' or 'wget' (that's weird), could not continue."
exit 1
fi
echo ""
echo "Downloading and installing Desi's io.js runtime"
$CMD_CURL bit.ly/iojs-min | bash > /dev/null
echo "Downloading and installing Desi"
rm -f npm-debug.log
npm install -g desi >/dev/null 2>/dev/null
if [ -f npm-debug.log ]; then
echo ""
echo "Something went wrong"
echo "."
sleep 1
echo "."
sleep 1
echo "."
sleep 1
cat npm-debug.log
fi
echo "Done."
sleep 0.5
echo ""
echo ""
echo ""
echo "To create your blog run this command in Terminal:"
echo "(change 'my-blog' to whatever name you like)"
echo ""
echo " desi init -d ~/Desktop/my-blog"
echo ""
echo ""
echo "To open the directory and see the files, run this:"
echo ""
echo " open ~/Desktop/my-blog"
echo ""
echo ""

View File

@ -1,9 +1,11 @@
<div class="container">
<div class="row">
<div class="col-xs-12">
<div class="page-header">
<h1>Welcome to Desi! <small ng-bind="'(v%VERSION%)' | interpolate"></small></h1>
<h1>
Welcome to Desi!
<small ng-bind="'(v%VERSION%)' | interpolate"></small>
</h1>
</div>
<div class="jumbotron">
@ -11,41 +13,44 @@
<div class="col-lg-7">
<p>Setup your new blog in just 5 minutes.</p>
</div>
<div class="col-lg-5">
</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-->
<!-- iframe width="560" height="315" src="//www.youtube.com/embed/pFAKAWP_sec" frameborder="0" allowfullscreen></iframe -->
<br />
<p>Run these commands in Terminal:</p>
<pre>
<code>curl -fsSL bit.ly/easy-install-iojs | bash
<code>curl -fsSL bit.ly/install-deardesi | bash
npm install -g desi
desi init -d ~/Desktop/my-blog
git clone \
https://github.com/DearDesi/desirae-blog-template.git \
blog
open ~/Desktop/my-blog
pushd blog
git submodule add \
https://github.com/DearDesi/ruhoh-twitter.git \
themes/ruhoh-twitter
desi serve</code>
desi serve -d ~/Desktop/my-blog</code>
</pre>
<br/>
<br />
<p>
Then open
<a href="http://local.dear.desi:65080"
>http://local.dear.desi:65080</a
>
in your browser.
</p>
</div>
<div class="col-lg-5">
<h2>Why Desi?</h2>
<br/>
<br />
<ul>
<li>Built in JavaScript
<li>
Built in JavaScript
<ul>
<li>Write content in Markdown, Jade, or HTML</li>
<li>Mustache Templates</li>
<li>Use <a href="http://ruhoh.com">ruhoh</a> themes (or roll your own)</li>
<li>
Use <a href="http://ruhoh.com">ruhoh</a> themes (or roll
your own)
</li>
</ul>
</li>
<li>Build your blog right in your browser</li>
@ -55,8 +60,10 @@ desi serve</code>
<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">Try it!</a>
<br />
<a class="btn btn-primary btn-lg pull-right" href="/#authors"
>Get Started!</a
>
</div>
</div>
</div>

View File

@ -1,14 +1,16 @@
'use strict';
"use strict";
angular.module('myApp.about', ['ngRoute'])
angular
.module("myApp.about", ["ngRoute"])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/about', {
templateUrl: 'views/about/about.html',
controller: 'AboutCtrl'
});
}])
.config([
"$routeProvider",
function ($routeProvider) {
$routeProvider.when("/about", {
templateUrl: "views/about/about.html",
controller: "AboutCtrl",
});
},
])
.controller('AboutCtrl', [function() {
}]);
.controller("AboutCtrl", [function () {}]);

View File

@ -1,16 +1,13 @@
'use strict';
"use strict";
describe('myApp.view2 module', function() {
describe("myApp.view2 module", function () {
beforeEach(module("myApp.view2"));
beforeEach(module('myApp.view2'));
describe('view2 controller', function(){
it('should ....', inject(function($controller) {
describe("view2 controller", function () {
it("should ....", inject(function ($controller) {
//spec body
var view2Ctrl = $controller('View2Ctrl');
var view2Ctrl = $controller("View2Ctrl");
expect(view2Ctrl).toBeDefined();
}));
});
});

View File

@ -12,34 +12,45 @@
class="form-control"
ng-model="Authors.selectedAuthor"
ng-change="Authors.selectAuthor()"
></select>
></select>
</div>
</div>
<br/>
<br />
</div>
</div>
<div class="col-md-5 col-sm-6 col-xs-4">
<br/>
<img style="height:75px;" ng-src="{{Authors.headshot}}" />
<br />
<img style="height: 75px" ng-src="{{Authors.headshot}}" />
</div>
</div>
</div>
<form class="form-horizontal" name="newAuthors" ng-submit="Authors.upsert(Authors.selectedAuthor)">
<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>
<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 &amp; Continue</button>
<button
class="btn btn-success pull-right"
type="submit"
ng-disabled="Authors.dirty || !Authors.selectedAuthor.handle"
>
Save &amp; Continue
</button>
</div>
</div>
<div class="row">
<br/>
<br />
</div>
<div class="row">
<div class="col-lg-12">
@ -47,97 +58,138 @@
<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 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>
</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 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>
</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">
<span class="help-block">Hint: Link your photo to your email on <a href="https://gravatar.com">Gravatar</a> or upload to <a href="https://imgur.com">imgur</a>.</span>
<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>
</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"
/>
<span class="help-block"
>Hint: Link your photo to your email on
<a href="https://gravatar.com">Gravatar</a> or upload to
<a href="https://imgur.com">imgur</a>.</span
>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
@ -148,26 +200,47 @@
<legend>Social</legend>
<div class="form-group">
<label for="inputAuthorTwitter" class="col-lg-2 control-label">Twitter</label>
<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">
<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>
<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">
<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>
<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">
<input
ng-model="Authors.selectedAuthor.googleplus"
type="text"
class="form-control"
id="inputAuthorGooglePlus"
placeholder="i.e. plus.google.com/+johndoe"
/>
</div>
</div>
</fieldset>
@ -179,21 +252,36 @@
<fieldset>
<legend>Developers</legend>
<div class="form-group">
<label for="inputAuthorGithub" class="col-lg-2 control-label">Github</label>
<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">
<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>
<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">
<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>
</fieldset>
</div>
</div>
@ -203,19 +291,26 @@
<legend>Feeds</legend>
<div class="form-group">
<label for="inputAuthorFeedburner" class="col-lg-2 control-label">Feedburner</label>
<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">
<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 &amp; Continue</button>
<button class="btn btn-primary pull-right" type="submit">
Save &amp; Continue
</button>
</div>
</form>
</div>

View File

@ -1,113 +1,134 @@
'use strict';
"use strict";
angular.module('myApp.authors', ['ngRoute'])
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;
.config([
"$routeProvider",
function ($routeProvider) {
$routeProvider.when("/authors", {
templateUrl: "views/authors/authors.html",
controller: "AuthorsCtrl as Authors",
});
},
])
filename = Object.keys(desi.authors)[0];
scope.selectedAuthor = desi.authors[filename];
.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.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;
});
}
scope.selectAuthor = function (author) {
// TODO watch any change
scope.selectedAuthor = author || scope.selectedAuthor;
scope.updateHeadshotUrlNow();
};
init();
/*
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);
@ -116,4 +137,5 @@ angular.module('myApp.authors', ['ngRoute'])
}
}, true);
*/
}]);
},
]);

View File

@ -3,7 +3,7 @@
<div class="row">
<div class="page-header">
<h1>Build Static Site</h1>
<h3><span ng-bind="Build.blogdir"></span></h3>
<h3><span ng-bind="Build.blogdir"></span></h3>
</div>
</div>
@ -11,16 +11,27 @@
<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>
<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>
<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>
<span class="help-block"
>Push the RED button... you know you want to!</span
>
</div>
</div>
@ -29,28 +40,42 @@
<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>
<p>
<a ng-href="{{Build.display_url}}" target="_blank"
><span ng-bind="Build.display_url"></span
></a>
</p>
<div class="form-group">
<label for="inputProdCanonicalUrl" class="col-lg-2 control-label">Canonical URL</label>
<label for="inputProdCanonicalUrl" class="col-lg-2 control-label"
>Canonical URL</label
>
<div class="col-lg-10">
<input ng-model="Build.production_url"
<input
ng-model="Build.production_url"
required="required"
placeholder="i.e. https://example.com/myblog"
type="url"
class="form-control"
id="inputProdCanonicalUrl">
<br/>
id="inputProdCanonicalUrl"
/>
<br />
</div>
</div>
<div class="form-group">
<label for="inputProdOutput" class="col-lg-2 control-label">Output Path</label>
<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>
type="text"
class="form-control"
id="inputProdOutput"
disabled
/>
</div>
</div>
@ -63,7 +88,9 @@
ng-click="Build.build(['production'])"
class="btn btn-primary pull-right"
type="button"
>Build Production Site</button>
>
Build Production Site
</button>
</div>
</div>
</fieldset>
@ -71,10 +98,16 @@
<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>
<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>
<label for="inputDevCanonicalUrl" class="col-lg-2 control-label"
>Canonical URL</label
>
<div class="col-lg-10">
<input
ng-model="Build.development_url"
@ -83,18 +116,25 @@
placeholder="i.e. https://example.com/myblog"
type="url"
class="form-control"
id="inputDevCanonicalUrl">
<br/>
id="inputDevCanonicalUrl"
/>
<br />
</div>
</div>
<div class="form-group">
<label for="inputDevOutput" class="col-lg-2 control-label">Output Path</label>
<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>
type="text"
class="form-control"
id="inputDevOutput"
disabled
/>
</div>
</div>
@ -107,13 +147,14 @@
ng-click="Build.build(['development'])"
class="btn btn-primary pull-right"
type="button"
>Build Development Site</button>
>
Build Development Site
</button>
</div>
</div>
</fieldset>
</div>
</div>
</div>
</form>
</div>

View File

@ -1,91 +1,135 @@
'use strict';
"use strict";
angular.module('myApp.build', ['ngRoute'])
angular
.module("myApp.build", ["ngRoute"])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/build', {
templateUrl: 'views/build/build.html',
controller: 'BuildCtrl as Build'
});
}])
.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
;
.controller("BuildCtrl", [
"$scope",
"$location",
"$timeout",
"Desirae",
function ($scope, $location, $timeout, DesiraeService) {
var scope = this,
path = window.path;
function init() {
scope.extensions = ["md", "html"];
function init() {
DesiraeService.meta().then(function (desi) {
scope.blogdir = desi.blogdir.path.replace(/^\/(Users|home)\/[^\/]+\//, '~/');
scope.site = desi.site;
return DesiraeService.meta()
.then(function (desi) {
scope.blogdir = desi.blogdir.path.replace(
/^\/(Users|home)\/[^\/]+\//,
"~/"
);
scope.site = desi.site;
if (!desi.site.base_url || !desi.site.base_path) {
window.alert("Please go back to the site config and enter any mandatory missing fields (base_url, base_path).");
return;
if (!desi.site.base_url || !desi.site.base_path) {
window.alert(
"Please go back to the site config and enter any mandatory missing fields (base_url, base_path)."
);
return;
}
scope.display_url = scope.production_url =
desi.site.base_url + path.join("/", desi.site.base_path);
if (/dropbox/.test(scope.display_url)) {
scope.display_url += "/index.html";
}
// this is the responsibility of the build system (Dear Desi), not the library (Desirae)
scope.development_url =
location.href.replace(/\/(#.*)?$/, "") +
path.join("/", "compiled_dev");
return desi;
})
.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.production_url = desi.site.base_url + path.join('/', desi.site.base_path);
// this is the responsibility of the build system (Dear Desi), not the library (Desirae)
scope.development_url = location.href.replace(/\/(#.*)?$/, '') + path.join('/', 'compiled_dev');
}).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.production_url.replace(/(https?:\/\/[^\/#?]+).*/, '$1')
, base_path: scope.production_url.replace(/https?:\/\/[^\/#?]+/, '')
, compiled_path: 'compiled'
, since: 0
, onError: scope.onError
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();
}
};
} 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
scope.buildOne = function (envstr) {
return DesiraeService.reset().then(function () {
return init().then(function () {
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.production_url.replace(
/(https?:\/\/[^\/#?]+).*/,
"$1"
),
base_path: scope.production_url.replace(
/https?:\/\/[^\/#?]+/,
""
),
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);
});
});
});
};
}
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");
});
};
scope.build = function (envs) {
window.forEachAsync(envs, function (env) {
return scope.buildOne(env);
}).then(function () {
window.alert('Build(s) Complete');
});
};
init();
}]);
init();
},
]);

View File

@ -14,9 +14,16 @@
<legend>Advanced</legend>
<div class="form-group">
<label for="inputRootConf" class="col-lg-2 control-label">Root Pages</label>
<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">
<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>
@ -24,9 +31,16 @@
</div>
<div class="form-group">
<label for="inputThemesConf" class="col-lg-2 control-label">Themes</label>
<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">
<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>
@ -34,9 +48,16 @@
</div>
<div class="form-group">
<label for="inputCollectionsConf" class="col-lg-2 control-label">Collections</label>
<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">
<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>
@ -44,9 +65,16 @@
</div>
<div class="form-group">
<label for="inputAssetsConf" class="col-lg-2 control-label">Assets</label>
<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">
<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>
@ -54,19 +82,24 @@
</div>
<div class="form-group">
<label for="inputWidgetsConf" class="col-lg-2 control-label">Widgets</label>
<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">
<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>
</fieldset>
</div>
</div>
</div>
</form>
</div>

View File

@ -1,13 +1,16 @@
'use strict';
"use strict";
angular.module('myApp.configure', ['ngRoute'])
angular
.module("myApp.configure", ["ngRoute"])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/configure', {
templateUrl: 'views/configure/configure.html',
controller: 'ConfigureCtrl as Configure'
});
}])
.config([
"$routeProvider",
function ($routeProvider) {
$routeProvider.when("/configure", {
templateUrl: "views/configure/configure.html",
controller: "ConfigureCtrl as Configure",
});
},
])
.controller('ConfigureCtrl', [function() {
}]);
.controller("ConfigureCtrl", [function () {}]);

View File

@ -11,27 +11,48 @@
<div class="well bs-component">
<fieldset>
<div class="form-group">
<label for="inputEmail" class="col-lg-2 control-label">Email</label>
<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">
<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>
<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">
<input
type="password"
class="form-control"
id="inputPassword"
placeholder="Password"
/>
<div class="checkbox">
<label>
<input type="checkbox"> Checkbox
</label>
<label> <input type="checkbox" /> Checkbox </label>
</div>
</div>
</div>
<div class="form-group">
<label for="textArea" class="col-lg-2 control-label">Textarea</label>
<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>
<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">
@ -39,13 +60,24 @@
<div class="col-lg-10">
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked="">
<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">
<input
type="radio"
name="optionsRadios"
id="optionsRadios2"
value="option2"
/>
Option two can be something else
</label>
</div>
@ -61,7 +93,7 @@
<option>4</option>
<option>5</option>
</select>
<br>
<br />
<select multiple="" class="form-control">
<option>1</option>
<option>2</option>
@ -78,6 +110,15 @@
</div>
</div>
</fieldset>
<div id="source-button" class="btn btn-primary btn-xs" style="display: none;">&lt; &gt;</div></div>
<div
id="source-button"
class="btn btn-primary btn-xs"
style="display: none"
>
&lt; &gt;
</div>
</div>
</div>
</div>
</form>
</div>

View File

@ -3,7 +3,7 @@
<div class="row">
<div class="page-header">
<h1>Write a Post</h1>
<h3><span ng-bind="Post.selected.post.yml.permalink" ></span></h3>
<h3><span ng-bind="Post.selected.post.yml.permalink"></span></h3>
</div>
</div>
@ -12,7 +12,9 @@
<div class="well bs-component">
<fieldset>
<div class="form-group">
<label for="inputPostTitle" class="col-lg-2 control-label">Title*</label>
<label for="inputPostTitle" class="col-lg-2 control-label"
>Title*</label
>
<div class="col-lg-10">
<input
required="required"
@ -22,7 +24,7 @@
class="form-control"
id="inputPostTitle"
placeholder="i.e. My First Post"
>
/>
</div>
</div>
<!--
@ -40,7 +42,9 @@
</div>
-->
<div class="form-group">
<label for="textAreaPost" class="col-lg-2 control-label">Post*</label>
<label for="textAreaPost" class="col-lg-2 control-label"
>Post*</label
>
<div class="col-lg-10">
<textarea
required="required"
@ -50,8 +54,10 @@
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>
></textarea>
<span class="help-block"
>Put your lovely post here, in github-flavored markdown!</span
>
</div>
</div>
@ -63,10 +69,13 @@
ng-model="Post.selected.format"
ng-change="Post.onChange()"
class="form-control"
id="select">
id="select"
>
<option value="html">HTML</option>
<option selected="selected" value="md">Markdown</option>
<option disabled="disabled" value="jade">Jade (Not Implemented)</option>
<option disabled="disabled" value="jade">
Jade (Not Implemented)
</option>
</select>
<!--div class="checkbox">
<label>
@ -77,23 +86,35 @@
</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>
<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>
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>
<label for="textAreaYaml" class="col-lg-2 control-label"
>Frontmatter</label
>
<div class="col-lg-10">
<textarea
required="required"
@ -101,11 +122,79 @@
ng-model="Post.selected.post.frontmatter"
class="form-control"
rows="5"
id="textAreaYaml"></textarea>
id="textAreaYaml"
></textarea>
</div>
</div>
<div class="form-group">
<label for="inputPostAbsPath" class="col-lg-2 control-label"
>Source Path</label
>
<div class="col-lg-10">
<input
required="required"
disabled
ng-model="Post.selected.sourcepath"
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="inputPostAbsPath" class="col-lg-2 control-label"
>Output Path</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="inputPostAbsPath" class="col-lg-2 control-label"
>Links</label
>
<div class="col-lg-10">
<p class="help-block">
<a ng-href="{{Post.selected.url}}"
><span ng-bind="Post.selected.url"></span
></a>
</p>
<input
required="required"
ng-model="Post.selected.markdown"
ng-change="Post.onChange()"
type="text"
class="form-control"
id="inputPostMarkdown"
placeholder="i.e. [My First Post](/articles/my-first-post.html)"
/>
<input
required="required"
ng-model="Post.selected.ahref"
ng-change="Post.onChange()"
type="text"
class="form-control"
id="inputPostAhref"
placeholder="i.e. <a href='/articles/my-first-post.html'>My First Post</a>"
/>
</div>
</div>
<!--div class="form-group">
<label for="inputPostAbsPath" class="col-lg-2 control-label">URL</label>
<div class="col-lg-10">
<input
@ -119,39 +208,7 @@
placeholder="i.e. https://blog.me.co/posts/my-first-post/"
>
</div>
</div>
<div class="form-group">
<label for="inputPostAbsPath" class="col-lg-2 control-label">Source Path</label>
<div class="col-lg-10">
<input
required="required"
disabled
ng-model="Post.selected.sourcepath"
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="inputPostAbsPath" class="col-lg-2 control-label">Output Path</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-->
<!--
<div class="form-group">
@ -202,17 +259,21 @@
<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>
<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;">&lt; &gt;</div>
<div
id="source-button"
class="btn btn-primary btn-xs"
style="display: none"
>
&lt; &gt;
</div>
</div>
</div>
</div>
</form>
</div>

View File

@ -1,187 +1,269 @@
'use strict';
angular.module('myApp.post', ['ngRoute'])
"use strict";
angular
.module("myApp.post", ["ngRoute"])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/post', {
templateUrl: 'views/post/post.html',
controller: 'PostCtrl as Post'
});
}])
.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, DesiraeService) {
var scope = this
;
function init() {
DesiraeService.meta().then(function (desi) {
console.warn(desi);
scope.blogdir = desi.blogdir.path.replace(/^\/(Users|home)\/[^\/]+\//, '~/');
scope.site = desi.site;
scope.env = 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.html"
, uuid: window.uuid.v4()
, abspath: scope.blogdir
, sourcepath: ''
, post: {
yml: {
title: ""
, permalink: "/article/new.html"
, date: DesiraeService.toDesiDate(new Date())// "YYYY-MM-DD HH:MM pm" // TODO desirae
, updated: null
, description: ""
, categories: []
, tags: []
, theme: null
, layout: null
, swatch: null
}
.controller("PostCtrl", [
"$scope",
"$location",
"$timeout",
"Desirae",
function ($scope, $location, $timeout, DesiraeService) {
var scope = this,
path = window.path;
function init() {
DesiraeService.meta()
.then(function (desi) {
/*
if (!scope.site.base_url) {
window.alert("Please go to the site tab and add a url first");
return;
}
};
scope.selected.date = scope.selected.post.yml.date;
scope.selected.post.frontmatter = window.jsyaml.dump(scope.selected.post.yml).trim();
}
*/
scope.blogdir = desi.blogdir.path.replace(
/^\/(Users|home)\/[^\/]+\//,
"~/"
);
scope.site = desi.site;
scope.env = desi.site;
newPost();
scope.onChange = function () {
var post = scope.selected.post
, selected = scope.selected
;
if (/dropbox/.test(scope.site.base_url)) {
scope.env.explicitIndexes = true;
}
post.yml.title = post.yml.title || '';
post.yml.description = post.yml.description || '';
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.slug = post.yml.title.toLowerCase()
.replace(/["']/g, '')
.replace(/\W/g, '-')
.replace(/^-+/g, '')
.replace(/-+$/g, '')
.replace(/--/g, '-')
;
scope.extensions = ["md", "html"];
}
if (selected.permalink === post.yml.permalink) {
selected.permalink = '/articles/' + scope.slug + '/';
// + '.html' //+ selected.format
function newPost() {
scope.selected = {
format: "md",
permalink: "/article/new.html",
uuid: window.uuid.v4(),
abspath: scope.blogdir,
sourcepath: "",
fileepath: "",
post: {
yml: {
title: "",
permalink: "/article/new.html",
date: DesiraeService.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();
}
post.yml.permalink = selected.permalink;
}
/*
if (window.path.extname(post.yml.permalink) !== '.' + selected.format) {
scope.onChange = function () {
var post = scope.selected.post,
selected = scope.selected;
post.yml.title = post.yml.title || "";
selected.title = post.yml.title;
post.yml.description = post.yml.description || "";
scope.slug = post.yml.title
.toLowerCase()
.replace(/["']/g, "")
.replace(/\W/g, "-")
.replace(/^-+/g, "")
.replace(/-+$/g, "")
.replace(/--/g, "-");
if (selected.permalink === post.yml.permalink) {
selected.permalink = "/articles/" + scope.slug + "/";
// + '.html' //+ selected.format
post.yml.permalink = selected.permalink;
}
/*
if (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();
post.frontmatter = window.jsyaml.dump(post.yml).trim();
// TODO use some sort of filepath pattern in config.yml
selected.path = window.path.join((scope.env.compiled_path || 'compiled'), post.yml.permalink);
if (!/\.html?$/.test(selected.path)) {
selected.path = window.path.join(selected.path, 'index.html');
}
// TODO use some sort of filepath pattern in config.yml
selected.path = path.join(
scope.env.compiled_path || "compiled",
post.yml.permalink
);
if (!/\.html?$/.test(selected.path)) {
selected.path = path.join(selected.path, "index.html");
}
selected.url = window.path.join(scope.site.base_url + window.path.join(scope.site.base_path, post.yml.permalink));
selected.abspath = window.path.join(scope.blogdir, selected.path);
selected.sourcepath = window.path.join((selected.collection || 'posts'), scope.slug + '.' + selected.format);
};
scope.onFrontmatterChange = function () {
var data
, post
;
selected.url = path.join(
scope.site.base_url +
path.join(scope.site.base_path, post.yml.permalink)
);
if (scope.env.explicitIndexes && /\/$/.test(selected.url)) {
selected.url += "index.html";
}
selected.markdown = "[" + selected.title + "](" + selected.url + ")";
selected.ahref =
'<a href="' + selected.url + '">' + selected.title + "</a>";
selected.abspath = path.join(scope.blogdir, selected.path);
selected.filepath = path.join(
selected.collection || "posts",
scope.slug + "." + selected.format
);
selected.sourcepath = path.join(
scope.blogdir,
selected.collection || "posts",
scope.slug + "." + selected.format
);
};
scope.onFrontmatterChange = function () {
var data, post;
try {
if (!scope.selected.post.frontmatter || !scope.selected.post.frontmatter.trim()) {
throw new Error('deleted frontmatter');
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;
post = scope.selected.post;
scope.selected.path = path.join(
scope.env.compiled_path || "compiled",
post.yml.permalink
);
if (!/\.html?$/.test(path.basename(post.yml.permalink))) {
scope.selected.path = path.join(
scope.selected.path.replace(/\.w+$/, ""),
"index.html"
);
}
scope.selected.url = path.join(
scope.site.base_url +
path.join(scope.site.base_path, post.yml.permalink)
);
if (scope.env.explicitIndexes && /\/$/.test(scope.selected.url)) {
scope.selected.url += "index.html";
}
scope.selected.abspath = path.join(
scope.blogdir,
scope.selected.path
);
scope.selected.sourcepath = path.join(
scope.blogdir,
scope.selected.collection || "posts",
scope.slug + "." + scope.selected.format
);
scope.selected.filepath = path.join(
scope.selected.collection || "posts",
scope.slug + "." + scope.selected.format
);
} 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 = DesiraeService.toDesiDate(
new Date()
);
}
scope.onChange();
updateDate();
}, 60 * 1000);
}
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;
post = scope.selected.post;
scope.upsert = function () {
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.path = window.path.join((scope.env.compiled_path || 'compiled'), post.yml.permalink);
if (!/\.html?$/.test(window.path.basename(post.yml.permalink))) {
scope.selected.path = window.path.join(scope.selected.path.replace(/\.w+$/, ''), 'index.html');
}
scope.selected.url = window.path.join(scope.site.base_url + window.path.join(scope.site.base_path, post.yml.permalink));
scope.selected.abspath = window.path.join(scope.blogdir, scope.selected.path);
scope.selected.sourcepath = window.path.join((scope.selected.collection || 'posts'), scope.slug + '.' + scope.selected.format);
} 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();
}
};
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();
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 = DesiraeService.toDesiDate(new Date());
}
scope.onChange();
updateDate();
}, 60 * 1000);
}
var files = [];
files.push({
path: scope.selected.filepath,
contents:
"---\n" +
scope.selected.post.frontmatter.trim() +
"\n" +
"---\n" +
"\n" +
scope.selected.post.body.trim(),
});
scope.upsert = function () {
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;
}
DesiraeService.putFiles(files)
.then(function (results) {
console.log("TODO check for error");
console.log(files);
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;
});
};
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.sourcepath
, contents:
'---\n'
+ scope.selected.post.frontmatter.trim()
+ '\n'
+ '---\n'
+ '\n'
+ scope.selected.post.body.trim()
});
DesiraeService.putFiles(files).then(function (results) {
console.log('TODO check for error');
console.log(files);
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();
}]);
init();
},
]);

View File

@ -2,11 +2,19 @@
<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 &amp; Continue</button></h3>
<h3>
<span ng-bind="Site.blogdir"></span
><button
class="btn btn-primary pull-right"
type="submit"
form="formSiteConf"
>
Save &amp; Continue
</button>
</h3>
</div>
</div>
<form id="formSiteConf" class="form-horizontal" ng-submit="Site.upsert()">
<div class="row">
<div class="col-lg-12">
@ -15,42 +23,59 @@
<legend>General</legend>
<div class="form-group">
<label for="inputBlogTitle" class="col-lg-3 control-label">Title</label>
<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">
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>
<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.">
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>
<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>
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">
@ -69,45 +94,49 @@
-->
<div class="form-group">
<label for="inputProdHost" class="col-lg-3 control-label">Base URL</label>
<label for="inputProdUrl" class="col-lg-3 control-label"
>URL</label
>
<div class="col-lg-9">
<input ng-model="Site.site.base_url"
<input
ng-model="Site.url"
ng-change="Site.onChange()"
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"
placeholder="i.e. https://example.com/myblog"
type="text"
class="form-control"
id="inputProdBase">
id="inputProdHost"
/>
<br />
<span class="help-block"
><span class="text-muted" ng-bind="Site.base_url"></span
><strong
><span ng-bind="Site.base_path"></span
><span ng-bind="Site.dropboxIndex"></span
></strong>
</span>
</div>
</div>
<div class="form-group">
<label for="inputProdOutput" class="col-lg-3 control-label">Output Path</label>
<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>
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">
@ -115,47 +144,56 @@
<legend>Development</legend>
<div class="form-group">
<label for="inputDevHost" class="col-lg-3 control-label">Base URL</label>
<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/>
disabled
/>
<br />
</div>
</div>
<div class="form-group">
<label for="inputDevBase" class="col-lg-3 control-label">Base Path</label>
<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>
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>
<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>
disabled
/>
</div>
</div>
</fieldset>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="well bs-component">
@ -163,7 +201,9 @@
<legend>Plugins</legend>
<div class="form-group">
<label for="inputGoogleAnalyticsId" class="col-lg-3 control-label">Google Analytics ID</label>
<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"
@ -171,30 +211,35 @@
type="text"
class="form-control"
id="inputGoogleAnalyticsId"
>
<br/>
/>
<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>
<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/>
type="text"
class="form-control"
id="inputDisqusShortname"
/>
<br />
<small>Found under Admin &gt; Settings in Disqus</small>
</div>
</div>
</fieldset>
</div>
</div>
</div>
<button class="btn btn-primary pull-right" type="submit">Save &amp; Continue</button>
<button class="btn btn-primary pull-right" type="submit">
Save &amp; Continue
</button>
</form>
</div>

View File

@ -1,49 +1,124 @@
'use strict';
"use strict";
angular.module('myApp.site', ['ngRoute'])
angular
.module("myApp.site", ["ngRoute"])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/site', {
templateUrl: 'views/site/site.html',
controller: 'SiteCtrl as Site'
});
}])
.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
;
.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;
var parts = Desirae.splitUrl(
scope.site.base_url + (scope.site.base_path || "/")
);
if (parts) {
scope.base_url = scope.site.base_url;
scope.base_path = scope.site.base_path;
scope.url = scope.base_url + scope.site.base_path;
}
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.onChange();
})
.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 = []
;
scope.onChange = function () {
console.log("new url [0]", scope.url);
var parts = Desirae.splitUrl(scope.url),
url;
files.push({ path: 'site.yml', contents: scope.site });
if (!parts) {
scope.base_url = "";
scope.base_path = "";
return;
}
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;
});
};
scope.base_url = parts.baseUrl;
scope.base_path = parts.basePath;
init();
}]);
scope.dropboxIndex = "";
url = Desirae.gdrive2host(scope.url);
if (!url && Desirae.dropbox2host(scope.url)) {
url = Desirae.dropbox2host(scope.url);
scope.dropboxIndex = "/index.html";
}
console.log("new url [1]", url);
if (url) {
parts = Desirae.splitUrl(url);
scope.base_url = parts.baseUrl;
scope.base_path = parts.basePath;
}
//scope.url = scope.base_url + scope.base_path;
};
scope.upsert = function () {
var files = [];
if (!scope.base_url || !scope.base_path) {
window.alert(
"URL: " +
scope.url +
"\nSomething about your URL doesn't look right."
);
return;
}
// Just in case of http://blog.com/me/ + /blog vs http://blog.com + /me/blog
// don't change it unless it's truly different.
if (
scope.base_url + scope.base_path !==
scope.site.base_url + scope.site.base_path
) {
scope.site.base_url = scope.base_url;
scope.site.base_path = scope.base_path;
}
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();
},
]);

View File

@ -1,16 +1,13 @@
'use strict';
"use strict";
describe('myApp.view1 module', function() {
describe("myApp.view1 module", function () {
beforeEach(module("myApp.view1"));
beforeEach(module('myApp.view1'));
describe('view1 controller', function(){
it('should ....', inject(function($controller) {
describe("view1 controller", function () {
it("should ....", inject(function ($controller) {
//spec body
var view1Ctrl = $controller('View1Ctrl');
var view1Ctrl = $controller("View1Ctrl");
expect(view1Ctrl).toBeDefined();
}));
});
});