Skip to content

Commit

Permalink
Merge pull request #25 from mixonic/docs
Browse files Browse the repository at this point in the history
Docs
  • Loading branch information
mixonic committed Jul 27, 2015
2 parents ee2e1a4 + e9a5d42 commit 652d1bc
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 153 deletions.
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### The MIT License (MIT)

Copyright (c) 2014 Garth Poitras
Copyright (c) 2014, 2015 Garth Poitras and Bustle Labs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
165 changes: 92 additions & 73 deletions MOBILEDOC.md
Original file line number Diff line number Diff line change
@@ -1,93 +1,112 @@
# mobiledoc format
## Mobiledoc

mobiledoc is the output format that content-kit-editor produces.
It aims to be a terse format that is efficiently transferred over the wire
for a client (be it web or native) to render.
Mobiledoc is a simple post or article format that aims to be:

## goals for mobiledoc
* **Platform agnostic**. Should be possible to render without an HTML parser.
* **Efficient to transfer**. Compresses well, and limits the duplication of
content.
* **Extensible at runtime**. Stores content, not layout or final display.

### efficient to transfer
Mobiledoc is primarily intended to be used for news-related content such as
articles and blog posts. It is deliberately simple, and organizes its content
in an array of "sections" that are considered as individual blocks of content.

The mobiledoc format is intended to be simple and fairly terse so that it compresses as well or better than HTML.
There is no concept of layout or design built into Mobiledoc. It is up to the
renderer to generate a display appropriate for its context. On mobile this may
mean each section is full-width and they are displayed sequentially. On larger
displays the sections may be rendered side-by-side. Mobiledoc makes no
prescription for output display.

### platform-agnostic
## Usage

mobiledoc is intended to be largely platform-agnostic. Sample use cases:
Often Mobiledoc will be used with the
[Content-Kit Editor](https://github.com/bustlelabs/content-kit-editor),
[Mobiledoc DOM Renderer](https://github.com/bustlelabs/mobiledoc-dom-renderer),
and/or [Mobiledoc HTML Renderer](https://github.com/bustlelabs/mobiledoc-html-renderer).

* A web app downloads an article in mobiledoc format via ajax and uses the mobiledoc-dom-renderer to render and display it
* An Ember app is running server-side via fastboot and needs to generate HTML server-side for SEO purposes. It uses the mobiledoc-html-renderer to turn a mobiledoc article that it retrieves from the API into an HTML string
* A native iOS app downloads an article in mobiledoc format and uses a custom renderer to generate a native view to display the article
## Specification

In all the above cases, the input is the same: An article in mobiledoc format, provided via an api.
Mobiledoc consists of a wrapping object and nested array of section data. Using
arrays makes Mobiledocs each to render quickly.

### content-focused
The wrapper signature:

mobiledoc is primarily intended to be used for news-related content such as articles and blog posts. It is deliberately simple, and organizes its content in an array of "sections" that are considered as individual blocks of content.

There is not currently any concept of layout or design built into the format. It is up to the renderer to generate a display appropriate for its context. On mobile this may mean each section is full-width and they are displayed sequentially. On larger displays the sections may be rendered side-by-side. mobiledoc makes no prescription for output display.

### extensible

mobiledoc should be open for extension with custom types (called "cards") that bring their own configuration, and possibly their own renderers. Primary renderers (the dom, html, native, etc renderers) should be able to gracefully handle cards that they cannot natively render.

## format specification

mobiledoc format is an array with two elements: `markerTypes` and `sections`. The marker types describe all of the possible `markups` that may be applied to `markers` within the sectionssection.

#### markupTypes

A `markupType` is an array of 1 or 2 items: `[markerTypes, sections]`. The first item in the array is the type of markup, typically a tagName (such as `"B"`). The markupType can optionally have a second item in its array, which is an array of `attributes` to be applied to it. The attributes are listed as `propName`, `propValue` in the array. Each `nth` item in the array is a propName and each `(n+1)th` item in the array is that propName's propValue.

Example markupTypes:

* Bold tag: `["B"]`
* Anchor tag linking to this repository: `["A", ["href", "http://github.com/bustlelabs/content-kit-editor"]]`
* Anchor tag linking to this repository and with `target="_blank"`: `["A", ["href", "http://github.com/bustlelabs/content-kit-editor", "target", "_blank"]]`

#### sections
```
{
version: "0.1", ──── Versioning information
sections: [
[ ──── List of markup types.
markup,
markup
],
[ ──── List of sections.
section,
section,
section
]
]
}
```

A section is an array of three items: `[sectionType, tagName, markers]`. `sectionType` defines the type of block section that this is. The only supported type now is `1`. `tagName` is the name of the block tag to use. H1-H6 and P are possible values. `markers` is an array of the content within that section, separated by the markup that is to be applied to it.
**Markup signature**

#### markers in sections
```
{
version: "0.1",
sections: [
[tagName, optionalAttributes], ──── Markup
['em'], ──── Example simple markup
['a', ['href', 'http://google.com']], ──── Example markup with attributes
],[
// ...
]]
}
```

A marker is an array of three items: `[openingMarkupTypes, closingMarkupCount, value]`. `openingMarkupTypes` is an array of the indexes of the `markupTypes` to apply to this marker, `closingMarkupCount` is how many markup types are closing at the end of this marker, and `value` is the text value inside the marker.
**Text Section**

Assuming these `markerTypes`:
```
markerTypes = [
["B"], // index 0
["I"], // index 1
["A", ["href", "google.com"]], // index 2
]
{
version: "0.1",
sections: [[
["b"], ──── Markup at index 0
["i"] ──── Markup at index 1
],[
[typeIdentifier, tagName, markers], ──── typeIdentifier for text sections
[1, "h2", [ is always 1.
[[], 0, "Simple h2 example"],
]],
[1, "p", [
[openMarkupsIndexes, numberOfClosedMarkups, value],
[[], 0, "Example with no markup"],
[[0], 1, "Example wrapped in b tag"],
[[1], 0, "Example opening i tag"],
[[], 1, "Example closing i tag"],
[[1, 0], 1, "Example opening i tag and b tag, closing b tag"],
[[], 1, "Example closing b tag"]
]]
]]
}
```

And this section:
The first item in the `sections` array is a list of markups. Markups have
a tagName, and optionally an array of `attributeName, attributeValue]` pairs.
The index in `openMarkupsIndex` specifies which markups should be opened at
the start of the `value` text. As these tags are opened, then create a stack
of opened markups. The `numberOfClosedMarkups` says how many markups should
be closed at the end of a `value`.

**Card Section**

```
section = [
1, // type of section
"P", // tagName of section
[ // markers
[
[1], // open marker type of index 1 (italics)
0, // close 0 open markers
'italicized' // the text value
],
[
[0], // open marker type of index 0 (bold)
1, // close 1 markerType (the most-recently opened marker type (bold) will be closed)
'bold + italicized'
],
[
[], // open no marker types
1, // close 1 marker type (in this case, italics)
'only italicized'
],
[
[2], // start the A tag
1, // and close it after this text
'I am a link'
]
]
]
{
version: "0.1",
sections: [[],[
[typeIdentifier, tagName, markers], ──── typeIdentifier for card sections
[10, "card-name", cardPayload] is always 10.
]]
}
```

`cardPayload` is arbitrary and should be passed through to the card
implementation.
135 changes: 56 additions & 79 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,38 @@
## ContentKit-Editor [![Build Status](https://travis-ci.org/bustlelabs/content-kit-editor.svg?branch=master)](https://travis-ci.org/bustlelabs/content-kit-editor)
## Content-Kit Editor [![Build Status](https://travis-ci.org/bustlelabs/content-kit-editor.svg?branch=master)](https://travis-ci.org/bustlelabs/content-kit-editor)

ContentKit is a modern WYSIWYG editor supporting interactive cards. Try a
demo at [http://content-kit.herokuapp.com/](http://content-kit.herokuapp.com/).
Content-Kit is a WYSIWYG editor supporting rich content via cards. Try a
demo at [content-kit.herokuapp.com](http://content-kit.herokuapp.com/).

ContentKit articles (we will use the term article, but any kind of content could
be authored) are built from models. Models represent rows of content, and can
be static or dynamic.
* It makes limited use of Content Editable, the siren-song of doomed web editor
technologies.
* Content-Kit is designed for *rich* content. We call these sections of an
article "cards", and implementing a new one doesn't require an understanding
of Content-Kit internals. Adding a new card take an afternoon, not several
days.
* Posts are serialized to a JSON payload called **Mobiledoc** instead of to
HTML. Mobiledoc can be rendered for the web, mobile web, or in theory on any
platform. Mobiledoc is portable and fast.

For example, the teardown of an article might look like:
**FIXME** this needs to be updated:
To learn more about Content-Kit in the abstract,
[read this announcement blog post](http://madhatted.com/2015/7/27/announcing-content-kit-and-mobiledoc).

```
┌──────────────────────────────────────────────────────────────────────────────┐
│ ┌──────────┐ │
│ │ Article │ │
│ └──────────┘ │
│┌────────────────────────────────────────────────────────────────────────────┐│
││ ┌──────────────┐ ││
││ │ MarkupModel │ ││
││ └──────────────┘ ││
││ <h2>One Weird Trick</h2> ││
│└────────────────────────────────────────────────────────────────────────────┘│
│┌────────────────────────────────────────────────────────────────────────────┐│
││ ┌──────────────┐ ││
││ │ BlockModel │ ││
││ └──────────────┘ ││
││ <p> ││
││ You will never guess what <i>happens</i> ││
││ when these cats take a <b>bath</b> ││
││ </p> ││
│└────────────────────────────────────────────────────────────────────────────┘│
│┌────────────────────────────────────────────────────────────────────────────┐│
││ ┌──────────────┐ ││
││ │ EmbedModel │ ││
││ └──────────────┘ ││
││ <img src="http://some-cdn.com/uploaded.jpg" /> ││
││ <!-- this HTML is generated by the embed API endpoint at edit-time --> ││
│└────────────────────────────────────────────────────────────────────────────┘│
│┌────────────────────────────────────────────────────────────────────────────┐│
││ ┌────────────────────────┐ ││
││ │ BlockModel (type CARD) │ ││
││ └────────────────────────┘ ││
││ node.innerHTML = 'Cards can change content at render time. For example, ││
││ use an Ember component'; ││
│└────────────────────────────────────────────────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────────────┘
```
Content-Kit saves posts to
**[Mobiledoc](https://github.com/bustlelabs/content-kit-editor/blob/master/MOBILEDOC.md)**.

### Usage

ContentKit is attached to a DOM node when the `Editor` factory is instantiated.
The `ContentKit.Editor` class is invoked with an element to render into and
optionally a Mobiledoc to load. For example:

```js
var editor = new ContentKit.Editor(this.element, options);
var simpleMobiledoc = [[], [
[1, "p", [
[[], 0, "Welcome to Content-Kit"]
]]
];
var element = $('#editor')[0];
var options = { mobiledoc: simpleMobiledoc };
var editor = new ContentKit.Editor(element, options);
```

`options` is an object which may include the following properties:
Expand All @@ -63,67 +43,53 @@ var editor = new ContentKit.Editor(this.element, options);
* `spellcheck` - a boolean option enabling spellcheck. Default is true.
* `autofocus` - a boolean option for grabbing input focus when an editor is
rendered.
* `serverHost` - a URL prefix for embed and image API request. **[FIXME Remove]**
* `cards` - an object describing available cards.

### Public Editor API

* `editor.loadPost(post)` - render the editor with a post. **[FIXME Implement]**
* `editor.serializePost()` - serialize the current post for persistence. **[FIXME Implement]**

### Deploy the demo website
### Editor API

The demo website is hosted at github pages.
To publish a new version do:

* `npm run build-website`. This builds the website into `website/` and commits it
* `npm run deploy-website`. Pushes the `website/` subtree to the `gh-pages` branch at github

Visit [http://bustlelabs.github.io/content-kit-editor/demo](http://bustlelabs.github.io/content-kit-editor/demo).
* `editor.serialize()` - serialize the current post for persistence. Returns
Mobiledoc.
* `editor.destroy()` - teardown the editor event listeners, free memory etc.

### Contributing

Running ContentKit tests and demo server locally requires the following dependencies:
Fork the repo, write a test, make a change, open a PR.

* [node.js](http://nodejs.org/) ([install package](http://nodejs.org/download/)) or `brew install node`
* `broccoli`, via `npm install -g broccoli-cli`

### Tests
#### Tests

Install npm and bower:

* `bower i i`
* `npm i`
* [Node.js](http://nodejs.org/) is required
* `npm install -g npm && npm install -g bower`
* `broccoli`, via `npm install -g broccoli-cli`
* `bower install`
* `npm install`

Run tests via the built-in broccoli server:

* `npm run serve`
* `broccoli serve`
* `open http://localhost:4200/tests`

Or run tests via testem:
Or run headless tests via testem:

* `npm test`

To view the demo:
To quickly view the demo:

* `broccoli serve`
* open http://localhost:4200/demo
* (Note that image uploads and embeds are not currently working)
* Image uploads and embeds are not supported running in this manner.

#### To Do

Bring back image uploads and embed. Notes below:

For uploads and embeds to work, you will have to configure AWS and
Embedly keys as environment variables:
To view the full demo with uploads and embed, you will have to configure AWS
and Embedly keys as environment variables:

```bash
export AWS_ACCESS_KEY_ID=XXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXX
export EMBEDLY_KEY=XXXXXX
```

Also, set the `bucketName` in `server/config.json` with the name of your AWS
Also set the `bucketName` in `server/config.json` with the name of your AWS
S3 bucket for uploading files.

Then to boot the server:
Expand All @@ -132,4 +98,15 @@ Then to boot the server:
node server/index.js
```

And visit http://localhost:5000/dist/demo/index.html
And visit [localhost:5000/dist/demo/index.html](http://localhost:5000/dist/demo/index.html)

#### Re-deploy the demo

The demo website is hosted at github pages. To publish a new version:

* `npm run build-website` - This builds the website into `website/` and commits it
* `npm run deploy-website` - Pushes the `website/` subtree to the `gh-pages` branch at github

Visit [bustlelabs.github.io/content-kit-editor/demo](http://bustlelabs.github.io/content-kit-editor/demo).

*Initial development of Content-Kit was generously funded by [Bustle Labs](http://www.bustle.com/labs). Bustle Labs is the tech team behind the editorial staff at [Bustle](http://www.bustle.com), a fantastic and successful feminist and women’s interest site based in NYC.*

0 comments on commit 652d1bc

Please sign in to comment.