alongslide 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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,5 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ task :default => :spec
@@ -0,0 +1,10 @@
1
+ # Tell Rails to check for assets and views here.
2
+ #
3
+ # http://guides.rubyonrails.org/asset_pipeline.html#adding-assets-to-your-gems
4
+ #
5
+ require 'rails'
6
+
7
+ module Alongslide
8
+ class Engine < ::Rails::Engine
9
+ end
10
+ end
@@ -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,11 @@
1
+ #
2
+ # Custom exception class for caller to rescue.
3
+ #
4
+ # Copyright 2013 Canopy Canopy Canopy, Inc.
5
+ # Author Adam Florin
6
+ #
7
+
8
+ module Alongslide
9
+ class SyntaxError < Exception
10
+ end
11
+ 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,9 @@
1
+ #
2
+ # alongslide.rb: Rails initializer for Alongslide Engine
3
+ #
4
+ # Copyright 2013 Canopy Canopy Canopy, Inc.
5
+ #
6
+
7
+ Alongslide.configure do |config|
8
+ config.user_template_dir = Rails.root.join("app/views/alongslide")
9
+ 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,6 @@
1
+ ---
2
+ greedy: true
3
+ ---
4
+ .gallery
5
+ - for item in content
6
+ .item= item
@@ -0,0 +1,9 @@
1
+ ---
2
+ greedy: false
3
+ ---
4
+ - url ||= ""
5
+ - alt ||= ""
6
+ - content ||= ""
7
+ .image
8
+ %img{src: url, alt: alt}
9
+ .caption= content.join
@@ -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
@@ -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: []