alongslide 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Alongslide
2
2
 
3
- Alongslide is a layout engine extending Markdown syntax.
3
+ Alongslide is a layout engine extending Markdown syntax for use in Rails-based
4
+ web applications
4
5
 
5
6
  It was developed by [Triple Canopy](http://canopycanopycanopy.com/) as a
6
7
  text-editable yet sophisticated platform for producing long-form reading
@@ -8,6 +9,8 @@ content on the web.
8
9
 
9
10
  Try the demo at [alongslide.com](http://alongslide.com) or [read some theory](http://canopycanopycanopy.com/contents/announcing_alongslide) underlying the project.
10
11
 
12
+ *Copyright 2013-2014 Canopy Canopy Canopy, Inc.*
13
+
11
14
  ## Dependencies
12
15
 
13
16
  The Ruby backend uses Redcarpet to extract layout directives from the
@@ -15,10 +18,79 @@ Markdown, Treetop to parse their grammar, and HAML to render them into
15
18
  templates.
16
19
 
17
20
  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.
21
+ cues, using a custom CSS Regions polyfill to flow the body text,
22
+ skrollr to build transition animations and respond to user scrolling,
23
+ plus jQuery and underscore.js as utilities.
24
+
25
+ ## Installing
26
+
27
+ ```bash
28
+ gem install alongslide
29
+ ```
30
+
31
+ The gem includes both the Ruby and frontend components.
32
+
33
+ ## Usage
34
+
35
+ To use Alongslide, begin by setting up your view:
36
+
37
+ ```erb
38
+ <article id="frames">
39
+ <div class="backgrounds"></div>
40
+ <div class="flow"></div>
41
+ <div class="panels"></div>
42
+ </article>
43
+ <div id="content" style="display: none">
44
+ <%= Alongslide::render(your_markdown_string) %>
45
+ </div>
46
+ ```
47
+
48
+ Where `your_markdown_string` is a string of Alongslide-flavored Markdown.
49
+
50
+ Note that the Alongslide HTML is rendered into a hidden DOM element. The empty
51
+ elements above are there for Alongslide's JS to write into.
52
+
53
+ Then, once the page is loaded and ready, you can kick off the Alongslide render:
54
+
55
+ ```javascript
56
+ MIN_WINDOW_WIDTH = 980
57
+ window.alongslide = new Alongslide({
58
+ source: '#content',
59
+ to: '#frames'
60
+ })
61
+ frameAspect = FixedAspect.prototype.fitFrame(MIN_WINDOW_WIDTH)
62
+ window.alongslide.render(frameAspect, function({
63
+ FixedAspect.prototype.fitPanels(frameAspect)
64
+ }))
65
+ ```
66
+
67
+ For RegionFlow (our CSS Regions polyfill) to work properly, it is critical that
68
+ _all_ styles and webfonts be loaded in browser memory before the Alongslide
69
+ render begins. For that reason, it is recommend that you use the included
70
+ `Styles.prototype.doLoad()` utility to force CSS to load (by loading it via
71
+ Ajax and writing it to the DOM) and/or a tool such as
72
+ [webfontloader](https://github.com/typekit/webfontloader).
73
+
74
+ After the inital render, you can use `window.alongslide.refresh(frameAspect)`
75
+ to update just the scroll positioning (after a window resize for example) without
76
+ incurring the full cost of doing the layout over again.
77
+
78
+ ### Creating custom templates
79
+
80
+ You may specify a directory in your application where you can specify your own
81
+ custom Alongslide templates. To do so, create an `alongslide.rb` in
82
+ `config/initializers` with the contents:
83
+
84
+ ```ruby
85
+ Alongslide.configure do |config|
86
+ config.user_template_dir = Rails.root.join("app/views/alongslide")
87
+ end
88
+ ```
89
+
90
+ Then you may create views in that directory using the YAML frontmatter format
91
+ described below.
20
92
 
21
- *Copyright 2013 Canopy Canopy Canopy, Inc.*
93
+ If these template have JS/CSS components, those may go wherever you like.
22
94
 
23
95
  ## Syntax
24
96
 
data/alongslide.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'alongslide'
3
- gem.version = '0.9.2'
3
+ gem.version = '0.9.3'
4
4
 
5
5
  gem.summary = "Create dynamic web layouts with an extended Markdown syntax"
6
6
  gem.description = "Create dynamic web layouts with an extended Markdown syntax"
@@ -8,6 +8,10 @@
8
8
  #= require prefix
9
9
  #= require jquery.fitvids
10
10
  #
11
+ # Utility
12
+ #= require ./styles
13
+ #= require ./fixedAspect
14
+ #
11
15
  # Core
12
16
  #= require alongslide/alongslide
13
17
  #= require alongslide/parser
@@ -0,0 +1,145 @@
1
+ #
2
+ # fixedAspect.coffee: Maintain fixed aspect ratio for given element.
3
+ #
4
+ # Copyright 2013 Canopy Canopy Canopy, Inc.
5
+ #
6
+
7
+ class window.FixedAspect
8
+
9
+ # Fit the content elements so that they maintain a consistent aspect ratio
10
+ # no matter the aspect ratio of the browser window.
11
+ #
12
+ # @param minWidthPx - minimum width in pixels to allow. Don't shrink smaller!
13
+ # @param marginTopPx - margin top in pixels to leave open.
14
+ #
15
+ # @return frame - bounding rect of content area, expressed as percentages
16
+ # relative to the browser window (0.0-1.0).
17
+ #
18
+ fitFrame: (minWidthPx, marginTopPx = 0) ->
19
+ FRAME_ASPECT = 1.6
20
+ FRAME_SELECTOR = '#frames > .flow > .frame'
21
+
22
+ frameAspect =
23
+ width: 1.0
24
+ height: 1.0
25
+ left: 0
26
+ top: 0
27
+
28
+ # aspect ratio calculations
29
+ window_aspect = $(window).width() / $(window).height()
30
+ if window_aspect > FRAME_ASPECT
31
+ frameAspect.width = FRAME_ASPECT / window_aspect
32
+ frameAspect.left = (1.0 - frameAspect.width) / 2
33
+ else
34
+ frameAspect.height = window_aspect / FRAME_ASPECT
35
+ frameAspect.top = (1.0 - frameAspect.height) / 2
36
+
37
+ # enforce margins, squeezing content down if necessary
38
+ marginTop = marginTopPx / $(window).height()
39
+ if frameAspect.top < marginTop
40
+ frameAspect.top = marginTop
41
+ scaleDown = (1.0 - frameAspect.top) / frameAspect.height
42
+ frameAspect.height *= scaleDown
43
+ frameAspect.width *= scaleDown
44
+ frameAspect.left = (1.0 - frameAspect.width) / 2
45
+
46
+ # enforce minWidthPx
47
+ frameWidthPx = frameAspect.width * $(window).width()
48
+ scaleUp = minWidthPx / frameWidthPx
49
+ if scaleUp > 1.0
50
+ frameAspect.height *= scaleUp
51
+ frameAspect.width *= scaleUp
52
+
53
+ # write to DOM, except for offset value for ALS
54
+ Styles::write 'debug-grid-aspect', Styles::formatPercentages('#als-debug-grid', frameAspect)
55
+ Styles::write 'frame-aspect', Styles::formatPercentages(FRAME_SELECTOR, _(frameAspect).omit('left'))
56
+
57
+ # write font-size / line-height
58
+ scalar = Math.max(1.0, (1.0 / scaleUp))
59
+ textStyles =
60
+ 'font-size': parseInt($('#content-display').css('font-size')) * scalar
61
+ 'line-height': parseInt($('#content-display').css('line-height')) * scalar
62
+ Styles::write 'frame-text', Styles::formatPixels('#frames', textStyles)
63
+
64
+ return frameAspect
65
+
66
+ # Go through all panels and manually turn absolute pixel-based values into
67
+ # percentage-based values.
68
+ #
69
+ # Do this after render, as once the operation is done, it can't be undone.
70
+ #
71
+ # Because of the complex interrelation between the CSS values (specified in a
72
+ # long, structural SASS doc) and the frame aspect value (computed on the fly),
73
+ # there's just no other way to do this but to get dirty and effectively write
74
+ # part of a CSS engine.
75
+ #
76
+ # Every `.panel` contains a `.contents`. The latter class has top/left/width/height
77
+ # specified--relative to the `.panel`. We re-derive the percentages from these
78
+ # relative values, because JS can't easily access CSS percentage values. And this
79
+ # method of dynamic re-percentifying seemed more sensible than putting large amounts
80
+ # of style data into JSON.
81
+ #
82
+ fitPanels: (frameAspect) ->
83
+ $('#frames > .panels > .panel').each (index, panel) =>
84
+ $panel = $(panel)
85
+ $contents = $panel.find('> .contents')
86
+
87
+ alignment = Alongslide::Layout::panelAlignment($panel)
88
+
89
+ innerFrame = $contents.data('innerFrame')
90
+
91
+ # If innerFrame isn't already stored, compute it now and store it.
92
+ unless innerFrame?
93
+ innerFrame = @innerFrame($panel)
94
+ $contents.data('innerFrame', innerFrame)
95
+
96
+ # .panel
97
+ #
98
+ panelFrame =
99
+ width: 1.0
100
+ height: 1.0
101
+
102
+ switch alignment
103
+ when "left"
104
+ panelFrame.width = frameAspect.left + (innerFrame.left + innerFrame.width) * frameAspect.width
105
+ when "right"
106
+ panelFrame.width = frameAspect.left + (1.0 - innerFrame.left) * frameAspect.width
107
+ when "top"
108
+ panelFrame.height = frameAspect.top + (innerFrame.top + innerFrame.height) * frameAspect.height
109
+ when "bottom"
110
+ # can't assume that frameAspect is vertically centered,
111
+ # due to `marginTop` (above). re-compute bottom.
112
+ frameAspectBottom = 1.0 - frameAspect.top - frameAspect.height
113
+ panelFrame.height = frameAspectBottom + (1.0 - innerFrame.top) * frameAspect.height
114
+
115
+ # .panel .contents
116
+ #
117
+ contentsFrame =
118
+ left: (frameAspect.left + innerFrame.left * frameAspect.width) / panelFrame.width
119
+ top: (frameAspect.top + innerFrame.top * frameAspect.height) / panelFrame.height
120
+ width: (innerFrame.width * frameAspect.width) / panelFrame.width
121
+ height: (innerFrame.height * frameAspect.height) / panelFrame.height
122
+
123
+ switch alignment
124
+ when "right"
125
+ # must use margin-left as skrollr uses left (so right-aligning won't work)
126
+ panelFrame['margin-left'] = (frameAspect.left + innerFrame.left * frameAspect.width)
127
+ contentsFrame.left = 0
128
+ when "bottom"
129
+ panelFrame.bottom = 0
130
+ contentsFrame.top = 0
131
+
132
+ # burn styles into CSS
133
+ #
134
+ $panel.css Styles::formatPercentageValues panelFrame
135
+ $contents.css Styles::formatPercentageValues contentsFrame
136
+
137
+ # Given a panel, determine its innerFrame.
138
+ #
139
+ innerFrame: ($panel) ->
140
+ $contents = $panel.find('> .contents')
141
+
142
+ left: parseInt($contents.css('left')) / $panel.width()
143
+ top: parseInt($contents.css('top')) / $panel.height()
144
+ width: $contents.width() / $panel.width()
145
+ height: $contents.height() / $panel.height()
@@ -0,0 +1,57 @@
1
+ #
2
+ # styles.coffee: Load CSS styles manually to be sure when they're loaded.
3
+ #
4
+ # Copyright 2013 Canopy Canopy Canopy, Inc.
5
+ #
6
+
7
+ class window.Styles
8
+ urls: []
9
+
10
+ # Break open HTML snippet and store href attributes of contained <link>s.
11
+ #
12
+ loadLater: (snippet) ->
13
+ $(snippet).filter('link').each (index, link) =>
14
+ @urls.push $(link).attr('href')
15
+
16
+ # Load all CSS files, then execute callback.
17
+ #
18
+ # For now, load synchronously, to make sure CSS is loaded in the correct
19
+ # order. Async loading is a possible optimization, but would have to do
20
+ # more legwork to keep track of all requests.
21
+ #
22
+ doLoad: (callback) ->
23
+ styles = $('<style type="text/css"/>').appendTo('body')
24
+ $.each @urls, (index, url) =>
25
+ $.ajax url,
26
+ async: false
27
+ success: (data, textStatus, jqXHR) =>
28
+ styles.append data
29
+ callback()
30
+
31
+ # Util: CSS formatter
32
+ #
33
+ formatPercentages: (selector, values) ->
34
+ declarations = (_.map values, (value, key) => "#{key}: #{@formatPercentageValue(value)}").join(';')
35
+ return "#{selector} { #{declarations} }"
36
+
37
+ #
38
+ #
39
+ formatPercentageValues: (values) ->
40
+ _.object _(values).map (percent, key) => [key, @formatPercentageValue percent]
41
+
42
+ # Util.
43
+ #
44
+ formatPercentageValue: (value) ->
45
+ "#{value * 100}%"
46
+
47
+ # Util: CSS formatter
48
+ #
49
+ formatPixels: (selector, values) ->
50
+ declarations = (_.map values, (value, key) -> "#{key}: #{value}px").join(';')
51
+ return "#{selector} { #{declarations} }"
52
+
53
+ # Util: set CSS by writing <style> to the DOM.
54
+ #
55
+ write: (id, styles) ->
56
+ $("style##{id}").remove()
57
+ $('<style type="text/css" id="'+id+'"/>').append(styles).appendTo('body')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alongslide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -92,6 +92,8 @@ files:
92
92
  - app/assets/javascripts/alongslide/layout.coffee
93
93
  - app/assets/javascripts/alongslide/parser.coffee
94
94
  - app/assets/javascripts/alongslide/scrolling.coffee
95
+ - app/assets/javascripts/fixedAspect.coffee
96
+ - app/assets/javascripts/styles.coffee
95
97
  - app/assets/stylesheets/alongslide.sass
96
98
  - app/views/panel/panel.haml
97
99
  - app/views/panel/unpin.haml