alongslide 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +118 -0
- data/Rakefile +5 -0
- data/lib/alongslide/engine.rb +10 -0
- data/lib/alongslide/redcarpet/render.rb +28 -0
- data/lib/alongslide/syntax_error.rb +11 -0
- data/lib/alongslide/templates.rb +123 -0
- data/lib/alongslide/treetop/parser.rb +152 -0
- data/lib/alongslide.rb +81 -0
- data/lib/generators/alongslide_generator.rb +13 -0
- data/lib/generators/templates/alongslide.rb +9 -0
- data/spec/alongslide_spec.rb +26 -0
- data/spec/fixtures/gallery.haml +6 -0
- data/spec/fixtures/image.haml +9 -0
- data/spec/redcarpet/render_spec.rb +13 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/treetop/parser_spec.rb +146 -0
- metadata +125 -0
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# Alongslide
|
2
|
+
|
3
|
+
Alongslide is a layout engine extending Markdown syntax.
|
4
|
+
|
5
|
+
It was developed by [Triple Canopy](http://canopycanopycanopy.com/) as a
|
6
|
+
text-editable yet sophisticated platform for producing long-form reading
|
7
|
+
content on the web.
|
8
|
+
|
9
|
+
Try the demo at [alongslide.com](http://alongslide.com) or [read some theory](http://canopycanopycanopy.com/contents/announcing_alongslide) underlying the project.
|
10
|
+
|
11
|
+
## Dependencies
|
12
|
+
|
13
|
+
The Ruby backend uses Redcarpet to extract layout directives from the
|
14
|
+
Markdown, Treetop to parse their grammar, and HAML to render them into
|
15
|
+
templates.
|
16
|
+
|
17
|
+
The CoffeeScript/SASS frontend then scan the resulting HTML for layout
|
18
|
+
cues, using Adobe's CSS Regions polyfill to flow the body text, and
|
19
|
+
skrollr to build transition animations and respond to user scrolling.
|
20
|
+
|
21
|
+
*Copyright 2013 Canopy Canopy Canopy, Inc.*
|
22
|
+
|
23
|
+
## Syntax
|
24
|
+
|
25
|
+
Alongslide blocks override the normal Markdown behavior for code blocks.
|
26
|
+
Any text indented by four spaces will be handled by Alongslide.
|
27
|
+
|
28
|
+
Within these blocks, special directives indicate which templates to render.
|
29
|
+
Directives begin with a plus sign.
|
30
|
+
|
31
|
+
For example:
|
32
|
+
|
33
|
+
```
|
34
|
+
Flowing paragraph text...
|
35
|
+
|
36
|
+
+ panel BigPanel fullscreen
|
37
|
+
|
38
|
+
# Huge text in a huge panel.
|
39
|
+
|
40
|
+
Flowing paragraph text continues...
|
41
|
+
|
42
|
+
```
|
43
|
+
|
44
|
+
## Adding templates
|
45
|
+
|
46
|
+
Templates are in `views/`. While basic `panel` and `section` templates are closely wedded to the frontend parser, panel templates may contain other templates, which are preloaded from `panel/content/`.
|
47
|
+
|
48
|
+
### Nesting templates
|
49
|
+
|
50
|
+
Each template begins with Middleman-style YAML frontmatter. Currently only one value is read: `greedy`, which refers to whether the template may contain other templates or not. If `true`, the template represents a
|
51
|
+
greedy node, and can contain other templates; if `false`, it is
|
52
|
+
non-greedy, and therefore cannot contain other templates.
|
53
|
+
|
54
|
+
For example:
|
55
|
+
|
56
|
+
```
|
57
|
+
Flowing paragraph text...
|
58
|
+
|
59
|
+
+ panel GalleryPanel fullscreen
|
60
|
+
|
61
|
+
+ gallery
|
62
|
+
|
63
|
+
+ image url "/one.jpg"
|
64
|
+
|
65
|
+
+ image url "/two.jpg"
|
66
|
+
|
67
|
+
Flowing paragraph text continues...
|
68
|
+
|
69
|
+
```
|
70
|
+
|
71
|
+
In the above example, the two (nongreedy) `image` templates will both be
|
72
|
+
contained by the `gallery` template (greedy), which in turn will be contained by the `panel` template (greedy).
|
73
|
+
|
74
|
+
### Template parameters
|
75
|
+
|
76
|
+
The syntax parser accepts arbitrary key/value pairs for templates, which are handed as they are to the HAML engine.
|
77
|
+
|
78
|
+
Nongreedy templates additionally consume any arbitrary Markdown which appears
|
79
|
+
below them.
|
80
|
+
|
81
|
+
For example:
|
82
|
+
|
83
|
+
```
|
84
|
+
+ image url "/one.jpg" position top opacity 0.5 note "A \"nice\" note"
|
85
|
+
|
86
|
+
Caption for _A Very Nice Image_.
|
87
|
+
|
88
|
+
```
|
89
|
+
|
90
|
+
In the above example, the `image.haml` template would receive variables for `url`, `position`, `opacity`, and `note`—in addition to a variable called `content`, which would contain the rendered HTML from the Markdown caption below.
|
91
|
+
|
92
|
+
Each inline parameter value may optionally be enclosed in double quotation marks, with space and backslash-escaped quotes inside.
|
93
|
+
|
94
|
+
## Adding panel parameters
|
95
|
+
|
96
|
+
To add functionality to the system, follow the path through the full stack,
|
97
|
+
from back to front:
|
98
|
+
|
99
|
+
1. Add appropriate rules to `panel_params` in `panel.treetop` (consult [Treetop documentation](http://treetop.rubyforge.org/syntactic_recognition.html) for syntax)
|
100
|
+
* Write tests for parsing the new rules in `parser_spec.rb`
|
101
|
+
* Make sure the appropriate `SyntaxNode` subclass (defined in `parser.rb`) puts the Treetop data into the correct param for the template (`panel.haml`). By default, it'll just lump it in as a CSS class.
|
102
|
+
* Add logic to parse the param in `checkForDirectives()` in `alongslide.coffee` adding CSS rules as necessary in `alongslide.sass`
|
103
|
+
|
104
|
+
## Layout Engine
|
105
|
+
|
106
|
+
On the front end, a layout engine written in CoffeeScript parses the layout cues (typically `<div>`s with special meta CSS classes) and renders the content as overlapping horizontally-scrolling **frames** (where one frame is one screen width).
|
107
|
+
|
108
|
+
### Process
|
109
|
+
|
110
|
+
1. **Pull panels out of flowing body text.** Alongslide supports flowing body text and **panels**, which behave like sidebars. In this step, pull those panels out, and index them by ID, to be displayed later.
|
111
|
+
* **Separate sections.** Because Alongslide follows the CSS Regions specification, multiple text sections may not flow from a single "source" in the DOM. In this step, break up the single source element into the appopriate number of DOM elements, and register each as the source for a single NamedFlow, using CSS Regions JS integration.
|
112
|
+
* **Lay out.** Iterate through the NamedFlows (and their respective sections), creating flowing **columns** (and their respective **frames**) one at a time, checking each for relevant cues about panel placement. If a panel directive is found, ensure that the panel appears and is dismissed at the correct points in the piece, and that the flowing text wraps around it (by applying CSS classes). In this step, each frame, whether flowing, panel, or section background, is assigned temporary DOM `data-` attributes (`alongslide-show-at` and `alongslide-hide-at`), to be parsed in the next step.
|
113
|
+
* **Apply scroll transitions.** Once the positioning of every frame has been decided, convert all of the temporary `data-` attributes to specific skrollr transitions, which may scroll, fade, etc.
|
114
|
+
* **Clean up**. Currently, this just entails resizing the frame container to fit total width of the frames (so that the scroll bar is proportional).
|
115
|
+
|
116
|
+
### CSS Regions
|
117
|
+
|
118
|
+
As the CSS Regions specification is under active development, Alongslide does not rely on correct browser implementation. Instead, it forces all browsers to use the Adobe polyfill. (And will continue to until the spec is firmed up.)
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#
|
2
|
+
# render.rb: Extend Redcarpet renderer with Alongslide syntax.
|
3
|
+
#
|
4
|
+
# Good resource for extending Redcarpet:
|
5
|
+
# http://dev.af83.com/2012/02/27/howto-extend-the-redcarpet2-markdown-lib.html
|
6
|
+
#
|
7
|
+
# Copyright 2013 Canopy Canopy Canopy, Inc.
|
8
|
+
# Author Adam Florin
|
9
|
+
#
|
10
|
+
require 'redcarpet'
|
11
|
+
|
12
|
+
module Redcarpet
|
13
|
+
module Render
|
14
|
+
class Alongslide < HTML
|
15
|
+
|
16
|
+
# Run through all indented blocks (normally treated as code blocks)
|
17
|
+
# for Alongslide block definitions.
|
18
|
+
#
|
19
|
+
# @param text is raw Markdown
|
20
|
+
# @param language is ignored
|
21
|
+
#
|
22
|
+
def block_code(text, language)
|
23
|
+
::Alongslide::render_block text
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#
|
2
|
+
# templates.rb: load and parse HAML templates (with Middleman-style YAML
|
3
|
+
# frontmatter).
|
4
|
+
#
|
5
|
+
# Copyright 2013 Canopy Canopy Canopy, Inc.
|
6
|
+
# Author Adam Florin
|
7
|
+
#
|
8
|
+
|
9
|
+
module Alongslide
|
10
|
+
module Templates
|
11
|
+
|
12
|
+
# Template paths.
|
13
|
+
#
|
14
|
+
# A minimum of templates are bundled with the gem.
|
15
|
+
#
|
16
|
+
# The rest are provided by the user, and stored elsewhere.
|
17
|
+
#
|
18
|
+
@@TEMPLATE_DIR = File.expand_path("../../app/views", File.dirname(__FILE__))
|
19
|
+
@@user_template_dir = nil
|
20
|
+
|
21
|
+
# Names of user templates, sorted by type.
|
22
|
+
#
|
23
|
+
@@template_names = {}
|
24
|
+
|
25
|
+
|
26
|
+
@@locals = {}
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
#
|
31
|
+
#
|
32
|
+
def configure
|
33
|
+
yield self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Scan user templates and populate list of names to be used by
|
37
|
+
# Treetop grammar to validate Alongslide directives.
|
38
|
+
#
|
39
|
+
def scan_user_templates
|
40
|
+
if @@user_template_dir
|
41
|
+
@@template_names = {greedy: [], nongreedy: [], independent: []}
|
42
|
+
Dir.glob(File.join @@user_template_dir, "*.haml") do |filename|
|
43
|
+
name = File.basename filename, ".haml"
|
44
|
+
params, template = load name, true
|
45
|
+
greediness = params["greedy"] ? :greedy : :nongreedy
|
46
|
+
@@template_names[greediness] << name
|
47
|
+
@@template_names[:independent] << name if params["independent"]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Load template, which consists of HAML + Middleman-style frontmatter.
|
53
|
+
#
|
54
|
+
# @param is_user_template - true if requested template is user-generated,
|
55
|
+
# not one of the basic bundled ones.
|
56
|
+
#
|
57
|
+
# @return template_params (hash), raw HAML template
|
58
|
+
#
|
59
|
+
def load(name, is_user_template = false)
|
60
|
+
template = IO.read(template_path(name, is_user_template))
|
61
|
+
yaml, haml = split_frontmatter(template)
|
62
|
+
params = YAML.load yaml
|
63
|
+
return params, haml
|
64
|
+
end
|
65
|
+
|
66
|
+
# Do HAML render.
|
67
|
+
#
|
68
|
+
# Use ActionView instead of Haml::Engine because we need our Rails helpers.
|
69
|
+
#
|
70
|
+
def render_template(name, is_user_template, render_params)
|
71
|
+
template_params, haml = load(name, is_user_template)
|
72
|
+
renderer.render(
|
73
|
+
inline: haml,
|
74
|
+
type: :haml,
|
75
|
+
locals: render_params.merge(@@locals))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Filesystem utility
|
79
|
+
#
|
80
|
+
def template_path(name, is_user_template)
|
81
|
+
template_dir = is_user_template ? @@user_template_dir : @@TEMPLATE_DIR
|
82
|
+
File.join template_dir, "#{name}.haml"
|
83
|
+
end
|
84
|
+
|
85
|
+
# HAML templates use Middleman-style YAML frontmatter. Separate it out.
|
86
|
+
#
|
87
|
+
def split_frontmatter(text)
|
88
|
+
text.split("---\n").reject(&:empty?)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Set config value, then reset cache.
|
92
|
+
#
|
93
|
+
def user_template_dir=(user_template_dir)
|
94
|
+
@@user_template_dir = user_template_dir
|
95
|
+
scan_user_templates
|
96
|
+
end
|
97
|
+
|
98
|
+
# Set config value, then reset cache.
|
99
|
+
#
|
100
|
+
def locals=(locals)
|
101
|
+
@@locals = locals
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return an ActionView instance with helpers monkeypatched in.
|
105
|
+
#
|
106
|
+
# https://gist.github.com/aliang/1022384
|
107
|
+
#
|
108
|
+
def renderer
|
109
|
+
action_view = ActionView::Base.new(Rails.configuration.paths["app/views"])
|
110
|
+
action_view.class_eval do
|
111
|
+
include Rails.application.routes.url_helpers
|
112
|
+
include ApplicationHelper
|
113
|
+
|
114
|
+
def protect_against_forgery?
|
115
|
+
false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
return action_view
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
#
|
2
|
+
# parser.rb: Bootstrap templating engine using Treetop syntax.
|
3
|
+
#
|
4
|
+
# Copyright 2013 Canopy Canopy Canopy, Inc.
|
5
|
+
# Author Adam Florin
|
6
|
+
#
|
7
|
+
require 'polyglot'
|
8
|
+
require 'treetop'
|
9
|
+
|
10
|
+
# Init
|
11
|
+
#
|
12
|
+
Treetop.load File.expand_path("../../../grammar/panel", File.dirname(__FILE__))
|
13
|
+
Treetop.load File.expand_path("../../../grammar/alongslide", File.dirname(__FILE__))
|
14
|
+
|
15
|
+
module Treetop
|
16
|
+
module Runtime
|
17
|
+
|
18
|
+
# Root node of parse tree.
|
19
|
+
#
|
20
|
+
class RootNode < SyntaxNode
|
21
|
+
|
22
|
+
# Render tree to HTML.
|
23
|
+
#
|
24
|
+
def render
|
25
|
+
blocks.elements.map do |block_wrapper|
|
26
|
+
if block_wrapper.content.respond_to? :block
|
27
|
+
block_wrapper.content.block.render
|
28
|
+
elsif block_wrapper.content.respond_to? :template
|
29
|
+
block_wrapper.content.template.render
|
30
|
+
end
|
31
|
+
end.join
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Template node base class. Covers panels, panel templates, sections.
|
36
|
+
#
|
37
|
+
class TemplateNode < SyntaxNode
|
38
|
+
|
39
|
+
# Load template and render it, with content, if applicable.
|
40
|
+
#
|
41
|
+
def render
|
42
|
+
::Alongslide::Templates::render_template template_name, is_user_template, render_params
|
43
|
+
end
|
44
|
+
|
45
|
+
# Sections and panels optionally implement generic "class_params",
|
46
|
+
# as well as required identifier.
|
47
|
+
#
|
48
|
+
def render_params
|
49
|
+
params = {content: template_content || []}
|
50
|
+
|
51
|
+
classes = if respond_to? :class_params
|
52
|
+
class_params.elements.map do |item|
|
53
|
+
item.param.text_value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
params[:classes] = (classes || []).join(" ")
|
57
|
+
|
58
|
+
if respond_to? :identifier
|
59
|
+
params[:identifier] = identifier.text_value
|
60
|
+
end
|
61
|
+
|
62
|
+
return params
|
63
|
+
end
|
64
|
+
|
65
|
+
# Template named in directive is what to look for in filesystem.
|
66
|
+
#
|
67
|
+
def template_name
|
68
|
+
command.text_value
|
69
|
+
end
|
70
|
+
|
71
|
+
# If Node has "contents", prepare content for template's variable of the
|
72
|
+
# same name.
|
73
|
+
#
|
74
|
+
# This may mean rendering another subtemplate, or simply some Markdown.
|
75
|
+
#
|
76
|
+
def template_content
|
77
|
+
if respond_to? :contents and contents
|
78
|
+
contents.elements.map do |item|
|
79
|
+
if item.content.respond_to? :template
|
80
|
+
item.content.template.renderable.template.render
|
81
|
+
else
|
82
|
+
::Alongslide::render item.text_value, plain: true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Default.
|
89
|
+
#
|
90
|
+
def is_user_template
|
91
|
+
false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Panel.
|
96
|
+
#
|
97
|
+
class PanelNode < TemplateNode
|
98
|
+
def template_name
|
99
|
+
"panel/#{super}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Templates which go inside panels.
|
104
|
+
#
|
105
|
+
class UserTemplateNode < TemplateNode
|
106
|
+
|
107
|
+
# Params for panel content template.
|
108
|
+
#
|
109
|
+
# Key/value pairs.
|
110
|
+
#
|
111
|
+
# If value is a quoted string, strip quotes.
|
112
|
+
#
|
113
|
+
def render_params
|
114
|
+
params = {}
|
115
|
+
if respond_to? :template_params
|
116
|
+
template_params.elements.map do |item|
|
117
|
+
key = item.param.key.text_value
|
118
|
+
value = item.param.value.text_value
|
119
|
+
params[key.to_sym] = unquote value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
super.merge params
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
#
|
127
|
+
def is_user_template
|
128
|
+
true
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
# If string is quoted, unquote.
|
134
|
+
#
|
135
|
+
def unquote(string)
|
136
|
+
if string =~ /^"/
|
137
|
+
string.gsub!(/^"|"$/, '').gsub(/\\"/, '"')
|
138
|
+
else
|
139
|
+
string
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# A section is a block of flowing text.
|
145
|
+
#
|
146
|
+
class SectionNode < TemplateNode
|
147
|
+
def template_name
|
148
|
+
"section/#{super}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/lib/alongslide.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#
|
2
|
+
# alongslide-redcarpet.rb: top-level bindings and entrypoint for Redcarpet
|
3
|
+
# and Treetop.
|
4
|
+
#
|
5
|
+
# Copyright 2013 Canopy Canopy Canopy, Inc.
|
6
|
+
# Author Adam Florin
|
7
|
+
#
|
8
|
+
require 'yaml'
|
9
|
+
require 'haml'
|
10
|
+
require "alongslide/engine"
|
11
|
+
require "alongslide/templates"
|
12
|
+
require "alongslide/redcarpet/render"
|
13
|
+
require "alongslide/treetop/parser"
|
14
|
+
require "alongslide/syntax_error"
|
15
|
+
|
16
|
+
module Alongslide
|
17
|
+
@@parser = nil
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
# Accept configuration from initializer.
|
22
|
+
#
|
23
|
+
def configure(&block)
|
24
|
+
Templates.configure &block
|
25
|
+
end
|
26
|
+
|
27
|
+
# Render HTML from Markdown.
|
28
|
+
#
|
29
|
+
# @option locals - if present, stash locals in class config static variable.
|
30
|
+
# NOTE! This is not threadsafe. but fine for general usage.
|
31
|
+
# @option plain - use secondary plain renderer
|
32
|
+
#
|
33
|
+
def render(markdown, options={})
|
34
|
+
unless options[:locals].nil?
|
35
|
+
Templates.configure do |config|
|
36
|
+
config.locals = options[:locals]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
(options[:plain] ? plain_renderer : renderer).render markdown
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get Redcarpet renderer, init'ing if necessary.
|
44
|
+
#
|
45
|
+
def renderer
|
46
|
+
Redcarpet::Markdown.new(Redcarpet::Render::Alongslide, :footnotes => true)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Plain vanilla renderer for the Markdown blocks extracted by the first
|
50
|
+
# renderer. (Can't call render() on a renderer in the middle of rendering,
|
51
|
+
# or you'll get Abort Traps.)
|
52
|
+
#
|
53
|
+
def plain_renderer
|
54
|
+
Redcarpet::Markdown.new Redcarpet::Render::HTML
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get Treetop parser, init'ing if necessary.
|
58
|
+
#
|
59
|
+
def parser
|
60
|
+
@@parser ||= Grammar::RootParser.new
|
61
|
+
end
|
62
|
+
|
63
|
+
# Given an indented block of Alongslide-flavored Markdown, parse and return
|
64
|
+
# rendered HTML including appropriate templates.
|
65
|
+
#
|
66
|
+
# @param markdown raw Markdown containing directives and raw text.
|
67
|
+
#
|
68
|
+
# @return HTML for display
|
69
|
+
#
|
70
|
+
def render_block(markdown)
|
71
|
+
rootNode = parser.parse(markdown)
|
72
|
+
if rootNode
|
73
|
+
return rootNode.render
|
74
|
+
else
|
75
|
+
error_line = markdown.split("\n")[parser.failure_line-1]
|
76
|
+
raise SyntaxError.new "Alongslide syntax error around: \"#{error_line}\""
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#
|
2
|
+
# alongslide_generator.rb: Generate (copy) initializer for Rails app.
|
3
|
+
#
|
4
|
+
# Copyright 2013 Canopy Canopy Canopy, Inc.
|
5
|
+
#
|
6
|
+
|
7
|
+
class AlongslideGenerator < Rails::Generators::Base
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
|
10
|
+
def copy_initializer_file
|
11
|
+
copy_file "alongslide.rb", "config/initializers/alongslide.rb"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Alongslide do
|
4
|
+
|
5
|
+
describe "rendering" do
|
6
|
+
it "should render a whole document" do
|
7
|
+
html = Alongslide::render <<-MARKDOWN.strip_heredoc
|
8
|
+
+ panel AnyPanel fullscreen
|
9
|
+
|
10
|
+
Body text.
|
11
|
+
MARKDOWN
|
12
|
+
html.should_not be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should gracefully catch ALS syntax errors" do
|
16
|
+
expect {
|
17
|
+
Alongslide::render <<-MARKDOWN.strip_heredoc
|
18
|
+
+ bogus
|
19
|
+
|
20
|
+
Body text.
|
21
|
+
MARKDOWN
|
22
|
+
}.to raise_error Alongslide::SyntaxError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Redcarpet::Render::Alongslide do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
@markdown = Redcarpet::Markdown.new Redcarpet::Render::Alongslide
|
7
|
+
end
|
8
|
+
|
9
|
+
it "passes text through" do
|
10
|
+
@markdown.render("# Sweet jubilation").should be_a(String)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'alongslide'
|
2
|
+
|
3
|
+
# ActiveSupport monkeypatch modified to work with few requires.
|
4
|
+
#
|
5
|
+
# http://apidock.com/rails/String/strip_heredoc
|
6
|
+
#
|
7
|
+
class String
|
8
|
+
def strip_heredoc
|
9
|
+
indent = scan(/^[ \t]*(?=\S)/).min.send(:size) || 0
|
10
|
+
gsub(/^[ \t]{#{indent}}/, '')
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Alongslide::Grammar::RootParser do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
Alongslide.configure do |config|
|
7
|
+
config.user_template_dir = File.expand_path("../fixtures", File.dirname(__FILE__))
|
8
|
+
end
|
9
|
+
|
10
|
+
@parser = Alongslide::Grammar::RootParser.new
|
11
|
+
@panel_name = "SomePanel"
|
12
|
+
@section_name = "SomeSection"
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "panels" do
|
16
|
+
it "should show and unpin" do
|
17
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
18
|
+
+ panel #{@panel_name} pin bottom
|
19
|
+
|
20
|
+
+ unpin #{@panel_name}
|
21
|
+
MARKDOWN
|
22
|
+
parsed.should_not be_nil
|
23
|
+
parsed.render.should include(@panel_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should support templateless panels" do
|
27
|
+
panel_text = "Some text directly in the panel."
|
28
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
29
|
+
+ panel #{@panel_name}
|
30
|
+
|
31
|
+
#{panel_text}
|
32
|
+
MARKDOWN
|
33
|
+
parsed.should_not be_nil
|
34
|
+
parsed.render.should include(panel_text)
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "parameters" do
|
38
|
+
it "should pin" do
|
39
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
40
|
+
+ panel #{@panel_name} pin left
|
41
|
+
MARKDOWN
|
42
|
+
parsed.should_not be_nil
|
43
|
+
parsed.render.should_not be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should fullscreen" do
|
47
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
48
|
+
+ panel #{@panel_name} fullscreen
|
49
|
+
MARKDOWN
|
50
|
+
parsed.should_not be_nil
|
51
|
+
parsed.render.should_not be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should pin and fade" do
|
55
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
56
|
+
+ panel #{@panel_name} pin top fade-in
|
57
|
+
MARKDOWN
|
58
|
+
parsed.should_not be_nil
|
59
|
+
parsed.render.should_not be_nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "panel templates" do
|
64
|
+
it "should nest templates within greedy templates" do
|
65
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
66
|
+
+ panel #{@panel_name}
|
67
|
+
|
68
|
+
+ gallery
|
69
|
+
|
70
|
+
+ image
|
71
|
+
MARKDOWN
|
72
|
+
parsed.should_not be_nil
|
73
|
+
parsed.render.should include("gallery")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should treat nongreedy templates as siblings" do
|
77
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
78
|
+
+ panel #{@panel_name}
|
79
|
+
|
80
|
+
+ image
|
81
|
+
|
82
|
+
+ image
|
83
|
+
MARKDOWN
|
84
|
+
parsed.should_not be_nil
|
85
|
+
parsed.render.should include("image")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should support nongreedy template (multiline) text contents" do
|
89
|
+
text_contents = "Some Text Contents"
|
90
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
91
|
+
+ panel a
|
92
|
+
|
93
|
+
+ image
|
94
|
+
|
95
|
+
#{text_contents}
|
96
|
+
#{text_contents}
|
97
|
+
MARKDOWN
|
98
|
+
parsed.should_not be_nil
|
99
|
+
parsed.render.should include(text_contents)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should support template parameters" do
|
103
|
+
url = "http://some-image.com/over_there.jpg?q=999"
|
104
|
+
alt_text = "Some \\\"alternate\\\" text."
|
105
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
106
|
+
+ panel a
|
107
|
+
|
108
|
+
+ image url "#{url}" alt "#{alt_text}"
|
109
|
+
MARKDOWN
|
110
|
+
parsed.should_not be_nil
|
111
|
+
parsed.render.should include(url)
|
112
|
+
parsed.render.should include(alt_text.gsub(/\\"/, '"'))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "sections" do
|
118
|
+
it "should enter and exit" do
|
119
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
120
|
+
+ section #{@section_name}
|
121
|
+
|
122
|
+
+ exit #{@section_name}
|
123
|
+
MARKDOWN
|
124
|
+
parsed.should_not be_nil
|
125
|
+
parsed.render.should include(@section_name)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should support transitions" do
|
129
|
+
valid_transition_class = "fade-in"
|
130
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
131
|
+
+ section #{@section_name} #{valid_transition_class}
|
132
|
+
MARKDOWN
|
133
|
+
parsed.should_not be_nil
|
134
|
+
parsed.render.should include(valid_transition_class)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "bad syntax" do
|
139
|
+
it "should catch bad syntax" do
|
140
|
+
parsed = @parser.parse <<-MARKDOWN.strip_heredoc
|
141
|
+
+ bogus
|
142
|
+
MARKDOWN
|
143
|
+
parsed.should be_nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alongslide
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adam Florin
|
9
|
+
- Anthony Tran
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-06-22 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: redcarpet
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: treetop
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: polyglot
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rspec
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
description: Create dynamic web layouts with an extended Markdown syntax
|
80
|
+
email: adam@canopycanopycanopy.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- Rakefile
|
86
|
+
- lib/alongslide/engine.rb
|
87
|
+
- lib/alongslide/redcarpet/render.rb
|
88
|
+
- lib/alongslide/syntax_error.rb
|
89
|
+
- lib/alongslide/templates.rb
|
90
|
+
- lib/alongslide/treetop/parser.rb
|
91
|
+
- lib/alongslide.rb
|
92
|
+
- lib/generators/alongslide_generator.rb
|
93
|
+
- lib/generators/templates/alongslide.rb
|
94
|
+
- spec/alongslide_spec.rb
|
95
|
+
- spec/fixtures/gallery.haml
|
96
|
+
- spec/fixtures/image.haml
|
97
|
+
- spec/redcarpet/render_spec.rb
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
- spec/treetop/parser_spec.rb
|
100
|
+
- README.md
|
101
|
+
homepage: http://github.com/triplecanopy
|
102
|
+
licenses: []
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 1.8.25
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: Create dynamic web layouts with an extended Markdown syntax
|
125
|
+
test_files: []
|