rhet-butler 0.13.0 → 0.14.0

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.
Files changed (57) hide show
  1. checksums.yaml +6 -14
  2. data/default-configuration/assets/fonts.googleapis.com/css/family=Arimo:700|Droid Sans Mono|Cinzel Decorative:700,900|Slackey,subset=latin,latin-ext +30 -0
  3. data/default-configuration/assets/fonts.gstatic.com/s/arimo/v12/P5sBzZCDf9_T_1Wi4TRNrZc.ttf +0 -0
  4. data/default-configuration/assets/fonts.gstatic.com/s/cinzeldecorative/v7/daaHSScvJGqLYhG8nNt8KPPswUAPniZQa9lESTQ.ttf +0 -0
  5. data/default-configuration/assets/fonts.gstatic.com/s/cinzeldecorative/v7/daaHSScvJGqLYhG8nNt8KPPswUAPniZoadlESTQ.ttf +0 -0
  6. data/default-configuration/assets/fonts.gstatic.com/s/droidsansmono/v10/6NUO8FuJNQ2MbkrZ5-J8lKFrp7pRef2u.ttf +0 -0
  7. data/default-configuration/assets/fonts.gstatic.com/s/slackey/v9/N0bV2SdQO-5yM0-dGlNQIQ.ttf +0 -0
  8. data/default-configuration/assets/javascript/presenter.js +317 -0
  9. data/default-configuration/assets/javascript/rhet-butler/child-step.js +13 -0
  10. data/default-configuration/assets/javascript/rhet-butler/step.js +197 -0
  11. data/default-configuration/assets/javascript/rhet-butler/steps/group.js +50 -0
  12. data/default-configuration/assets/javascript/rhet-butler/steps/item.js +60 -0
  13. data/default-configuration/assets/javascript/rhet-butler/steps/root.js +41 -0
  14. data/default-configuration/assets/javascript/rhet-butler/steps/slide.js +96 -0
  15. data/default-configuration/assets/javascript/rhet-butler/transition-states.js +173 -0
  16. data/default-configuration/assets/javascript/rhet-butler/transition-station.js +133 -0
  17. data/default-configuration/assets/javascript/rhet-butler/transition-stations.js +107 -0
  18. data/default-configuration/assets/javascript/rhet-butler/tree-builder.js +103 -0
  19. data/default-configuration/assets/javascript/utils.js +78 -0
  20. data/default-configuration/assets/stylesheets/font.sass +5 -9
  21. data/default-configuration/common/templates/presentation.html.erb +5 -5
  22. data/lib/rhet-butler/file-loading.rb +1 -1
  23. data/lib/rhet-butler/stasis/transform-queue.rb +2 -0
  24. data/spec/command-line.rb +1 -0
  25. data/spec/configuration.rb +27 -0
  26. data/spec/javascripts/fixtures/long-animation-group-1.html +19 -0
  27. data/spec/javascripts/fixtures/long-transition-group-1.html +10 -0
  28. data/spec/javascripts/fixtures/looped-animation-group-1.html +20 -0
  29. data/spec/javascripts/fixtures/quiet_console.js +2 -0
  30. data/spec/javascripts/fixtures/test-presentation.html +49 -0
  31. data/spec/javascripts/helpers/.gitkeep +0 -0
  32. data/spec/javascripts/helpers/jasmine-jquery.js +841 -0
  33. data/spec/javascripts/helpers/jj-fixture-path.js +3 -0
  34. data/spec/javascripts/helpers/jquery-3.4.0.min.js +2 -0
  35. data/spec/javascripts/present_spec.js +728 -0
  36. data/spec/javascripts/support/jasmine.yml +148 -0
  37. data/spec/javascripts/support/jasmine_helper.rb +23 -0
  38. data/spec/javascripts/support/run.html.erb +22 -0
  39. data/spec/javascripts/utils_spec.js +7 -0
  40. data/spec/main-app.rb +18 -0
  41. data/spec/messaging.rb +32 -0
  42. data/spec/presentation-view.rb +4 -4
  43. data/spec/resource-localizer.rb +37 -0
  44. data/spec/sass-functions.rb +25 -0
  45. data/spec/slide-loader.rb +1 -1
  46. data/spec/slide-rendering.rb +58 -0
  47. data/spec/static-generator.rb +54 -0
  48. metadata +211 -178
  49. data/default-configuration/assets/javascript/rhet-present.js +0 -855
  50. data/default-configuration/assets/javascript/rhet-present.min.js +0 -55
  51. data/default-configuration/assets/stylesheets/animate/attention/._pulse.scss.swp +0 -0
  52. data/default-configuration/assets/themes.googleusercontent.com/static/fonts/arimo/v5/K-bXE71xZHgbUS_UdQjugvesZW2xOQ-xsNqO47m55DA.ttf +0 -0
  53. data/default-configuration/assets/themes.googleusercontent.com/static/fonts/cinzeldecorative/v1/pXhIVnhFtL_B9Vb1wq2F9wIh9oxuYcmvOvyh_107lQs.ttf +0 -0
  54. data/default-configuration/assets/themes.googleusercontent.com/static/fonts/cinzeldecorative/v1/pXhIVnhFtL_B9Vb1wq2F9zCUrkmwPfdnoTjOU_kXqBI.ttf +0 -0
  55. data/default-configuration/assets/themes.googleusercontent.com/static/fonts/droidsansmono/v4/ns-m2xQYezAtqh7ai59hJYW_AySPyikQrZReizgrnuw.ttf +0 -0
  56. data/default-configuration/assets/themes.googleusercontent.com/static/fonts/slackey/v3/bJZDrYrGx8atJRHR9DVdqg.ttf +0 -0
  57. data/default-configuration/skels/slides.yaml +0 -7
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ODZlMjBlZjY5N2JhNjhiNjkwYjA0MWE0YjBmNDhmZTRiY2M5MTVhNA==
5
- data.tar.gz: !binary |-
6
- ZWI4OWU5YzZlZWM3ODdjYzBlOTA1NTE5MTAwYTEyYmUyMzJlMWVlYQ==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- MTJjYjA1Nzg4NGI5ZWQyYTczZGYwOTViNWIxYTc5OTJiYmMwODNkN2RkNDFh
10
- MDRjZDIzNTVlNzE2MTVkOGFiNDljYTdhMTZkYWZjMTk2ZTNmMzQ5ODc3MmNh
11
- NDg0Yzc5YzRkM2VmN2UxMDBlOGU2YTVjYTVkMDJkZjNhYzI0YzI=
12
- data.tar.gz: !binary |-
13
- Mzg5OTE4YzNjYjc5ZjliZmU1MGJjNzFkN2M2MTBkMjBiNGNiZTEwZjdjZmRj
14
- ZTFkOGVhYzc3ODFjZDY3MGRiOGY2NGU4NWUwY2VlNGI3NWQxMDQ0NjdkYTll
15
- NzNhMTFiOWFiYmZkOTU4MzQ5YzhiODk0N2Q2YmIyYTkxYmRkM2Q=
2
+ SHA256:
3
+ metadata.gz: bccbe00728c9002f8abfc509425b4f5b24fa4ccb8ea4dd11fad668101284542f
4
+ data.tar.gz: 98aa2c7300f207844ae64039baf9546c909ab802230b143f5c4e3f1660a2f1b6
5
+ SHA512:
6
+ metadata.gz: b0a5639408cde011d9a841d81ae1442d6e10468ab56c825b4fea0179337feed6408c50cb9ed5be3d6316b2c7736f3093ca70f208c03087ffa46ce3b9df079953
7
+ data.tar.gz: 2fc6cee2a0a4ae09629e8c18c6f7b16f7ada43d6877780cb410b28553d7fe7b85a637b0e760afe97efb5064b035fac9b2c2ad5d57e761f16da20d3814cd7c506
@@ -0,0 +1,30 @@
1
+ @font-face {
2
+ font-family: 'Arimo';
3
+ font-style: normal;
4
+ font-weight: 700;
5
+ src: local('Arimo Bold'), local('Arimo-Bold'), url(../../fonts.gstatic.com/s/arimo/v12/P5sBzZCDf9_T_1Wi4TRNrZc.ttf) format('truetype');
6
+ }
7
+ @font-face {
8
+ font-family: 'Cinzel Decorative';
9
+ font-style: normal;
10
+ font-weight: 700;
11
+ src: local('Cinzel Decorative Bold'), local('CinzelDecorative-Bold'), url(../../fonts.gstatic.com/s/cinzeldecorative/v7/daaHSScvJGqLYhG8nNt8KPPswUAPniZoadlESTQ.ttf) format('truetype');
12
+ }
13
+ @font-face {
14
+ font-family: 'Cinzel Decorative';
15
+ font-style: normal;
16
+ font-weight: 900;
17
+ src: local('Cinzel Decorative Black'), local('CinzelDecorative-Black'), url(../../fonts.gstatic.com/s/cinzeldecorative/v7/daaHSScvJGqLYhG8nNt8KPPswUAPniZQa9lESTQ.ttf) format('truetype');
18
+ }
19
+ @font-face {
20
+ font-family: 'Droid Sans Mono';
21
+ font-style: normal;
22
+ font-weight: 400;
23
+ src: local('Droid Sans Mono Regular'), local('DroidSansMono-Regular'), url(../../fonts.gstatic.com/s/droidsansmono/v10/6NUO8FuJNQ2MbkrZ5-J8lKFrp7pRef2u.ttf) format('truetype');
24
+ }
25
+ @font-face {
26
+ font-family: 'Slackey';
27
+ font-style: normal;
28
+ font-weight: 400;
29
+ src: local('Slackey Regular'), local('Slackey-Regular'), url(../../fonts.gstatic.com/s/slackey/v9/N0bV2SdQO-5yM0-dGlNQIQ.ttf) format('truetype');
30
+ }
@@ -0,0 +1,317 @@
1
+ //Presenter
2
+ // The overall interface to the application
3
+ // Maintains some state, receives method calls to e.g. move between slides
4
+ // Builds the step-tree
5
+ // Builds StationLists
6
+
7
+ import * as utils from "./utils.js";
8
+ import TreeBuilder from "./rhet-butler/tree-builder.js";
9
+ import TransitionStations from "./rhet-butler/transition-stations.js";
10
+ import Step from './rhet-butler/step.js';
11
+
12
+ export default class {
13
+ constructor(document, window, rootId){
14
+ this.document = document;
15
+ this.body = document.body;
16
+ this.window = window;
17
+ this.stepsById = {};
18
+
19
+ this.previousSlideIndex = 0;
20
+ this.nextStepIndex = 0;
21
+
22
+ this.root = utils.byId(rootId);
23
+ this.body.classList.remove("rhet-disabled");
24
+ this.body.classList.add("rhet-enabled");
25
+
26
+ // get and init steps
27
+ var stepElements = utils.arrayify(this.root.getElementsByClassName("rhet-butler"));
28
+ var treeBuilder = new TreeBuilder(this.root, "rhet-butler");
29
+ this.rootStep = treeBuilder.buildTree();
30
+ this.rootStep.eachStep(function (step) {
31
+ step.addClass("future");
32
+ });
33
+
34
+ var prev = this.rootStep.firstItem;
35
+
36
+ this.currentTransition = new TransitionStations(this, prev, prev, prev);
37
+ this.currentTransition.forceFinish();
38
+
39
+ this.unbinders = [];
40
+ this.bindHandlers();
41
+
42
+ utils.triggerEvent(this.root, "rhet:init", { api: this });
43
+ }
44
+
45
+ // updateBeforeAndAfter(previousStep, nextStep){
46
+ // this.markRange(0, previousStep.indexes.step, "before");
47
+ // this.markRange(nextStep.indexes.step, undefined, "after");
48
+ // previousStep.eachParent(function(step){ step.removeClass("before"); });
49
+ // nextStep.eachParent(function(step){ step.removeClass("after"); });
50
+ // }
51
+
52
+ teardown(){
53
+ this.unbindHandlers();
54
+ }
55
+
56
+ markRange(start, end, mark){
57
+ this.stepsList.slice(start, end).forEach(function(elem){
58
+ var containers;
59
+ containers = this.containingElements(elem);
60
+ containers.steps.forEach(function(step){ this.thisClassNotThose(step, mark, "before", "after", "passing"); }, this);
61
+ containers.slides.forEach(function(slide){ this.thisClassNotThose(slide, mark, "before", "after", "passing"); }, this);
62
+ }, this);
63
+ }
64
+
65
+ bindHandler(target, event, funk, capture) {
66
+ this.unbinders.push(() => {
67
+ target.removeEventListener(event, funk, capture);
68
+ })
69
+
70
+ target.addEventListener(event, funk, capture);
71
+ }
72
+
73
+ unbindHandlers() {
74
+ for(let f of this.unbinders){
75
+ f();
76
+ }
77
+ }
78
+
79
+ bindHandlers(){
80
+ var presenter = this;
81
+
82
+ this.unbindHandlers();
83
+
84
+ //Our own :init event
85
+ var initListener = function(){
86
+ // last hash detected
87
+ var lastHash = "";
88
+
89
+ // STEP CLASSES
90
+ // this.root.addEventListener("rhet:stepenter", function (event) {
91
+ // this.thisClassNotThose(event.target, "present", "past", "future");
92
+ // }, false);
93
+ //
94
+ // this.root.addEventListener("rhet:stepleave", function (event) {
95
+ // this.thisClassNotThose(event.target, "past", "present", "future");
96
+ // }, false);
97
+
98
+ // `#/step-id` is used instead of `#step-id` to prevent default browser
99
+ // scrolling to element in hash.
100
+ //
101
+ // And it has to be set after animation finishes, because in Chrome it
102
+ // makes transtion laggy.
103
+ // BUG: http://code.google.com/p/chromium/issues/detail?id=62820
104
+
105
+ presenter.bindHandler(presenter.root, "rhet:stepenter", function (event) {
106
+ window.location.hash = lastHash = "#/" + event.target.id;
107
+ }, false);
108
+
109
+
110
+ presenter.bindHandler(window, "hashchange",
111
+ function(event){
112
+ if(window.location.hash !== lastHash) {
113
+ presenter.moveTo( presenter.getElementFromHash() );
114
+ }
115
+ }, false);
116
+
117
+ // START
118
+ // by selecting step defined in url or first step of the presentation
119
+
120
+ presenter.teleport(presenter.getElementFromHash() || presenter.rootStep.firstItem);
121
+ }
122
+
123
+ this.bindHandler(this.root, "rhet:init", initListener, false);
124
+ }
125
+
126
+ resolveStep(reference, thing){
127
+ // find by id
128
+ // find by relavtive pos (next,prev)*(slide,item)
129
+ if(reference instanceof Step){
130
+ return reference;
131
+ } else {
132
+ switch(reference){
133
+ case 'next':
134
+ switch(thing){
135
+ case 'slide':
136
+ return this.currentTransition.lastStep.nextSlide;
137
+ break;
138
+ case 'item':
139
+ return this.currentTransition.lastStep.nextItem;
140
+ break
141
+ default:
142
+ throw "Bad step reference: '" + reference +","+ thing +"'";
143
+ }
144
+ break;
145
+ case 'prev':
146
+ case 'previous':
147
+ switch(thing){
148
+ case 'slide':
149
+ return this.currentTransition.lastStep.prevSlide;
150
+ break;
151
+ case 'item':
152
+ return this.currentTransition.lastStep.prevItem;
153
+ break
154
+ default:
155
+ throw "Bad step reference: '" + reference +","+ thing +"'";
156
+ }
157
+
158
+ break;
159
+ default:
160
+ if(reference in this.rootStep.childrenById){
161
+ return this.rootStep.childrenById[reference];
162
+ }else{
163
+ throw "Bad slide direction: '" + reference +"'";
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ currentStep(){
170
+ return this.currentTransition.currentStep;
171
+ }
172
+
173
+ buildTransition(nextStep){
174
+ var previousStep = this.currentTransition.resumeStep();
175
+ var currentStep = this.currentTransition.currentStep;
176
+ if(typeof quiet_console == "undefined") {
177
+ console.log("New transition list: " +
178
+ "S/C/E: " + previousStep.toString() +
179
+ " / " + currentStep.toString() +
180
+ " / " + nextStep.toString());
181
+ }
182
+
183
+ this.currentTransition.cancel();
184
+ this.currentTransition = new TransitionStations(this, previousStep, currentStep, nextStep);
185
+ }
186
+
187
+ teleport(reference, thing){
188
+ var nextStep = this.resolveStep(reference, thing);
189
+ if(nextStep){
190
+ this.buildTransition(nextStep);
191
+ this.currentTransition.forceFinish();
192
+ }
193
+ }
194
+
195
+ moveTo(reference, thing){
196
+ var nextStep = this.resolveStep(reference, thing);
197
+ if(nextStep){
198
+ this.buildTransition(nextStep);
199
+ this.currentTransition.start();
200
+ }
201
+ }
202
+
203
+ completeTransition(){
204
+ var elem = document.getElementById(this.currentTransition.currentStep.element.id);
205
+ utils.triggerEvent(elem, "rhet:stepenter", { api: this });
206
+ }
207
+
208
+ ///// METHODS BELOW HERE STILL IN NEED OF REFACTORING
209
+
210
+ checkSupport (){
211
+ var ua = navigator.userAgent.toLowerCase();
212
+ var impressSupported = ( pfx("perspective") !== null ) && ( body.classList ) && ( body.dataset )
213
+ //also should check getComputedStyle
214
+
215
+ if (!impressSupported) {
216
+ // we can't be sure that `classList` is supported
217
+ this.body.className += " rhet-not-supported ";
218
+ return false
219
+ } else {
220
+ this.body.classList.remove("rhet-not-supported");
221
+ this.body.classList.add("rhet-supported");
222
+ return true
223
+ }
224
+ }
225
+
226
+ // `getElementFromHash` returns an element located by id from hash part of
227
+ // window location.
228
+ getElementFromHash () {
229
+ // get id from url # by removing `#` or `#/` from the beginning,
230
+ // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
231
+ return window.location.hash.replace(/^#\/?/,"");
232
+ }
233
+
234
+ // `getStep` is a helper function that returns a step element defined by
235
+ // parameter.
236
+ // If a number is given, step with index given by the number is returned, if a string
237
+ // is given step element with such id is returned, if DOM element is given it is returned
238
+ // if it is a correct step element.
239
+ getStep ( step ) {
240
+ if (typeof step === "number") {
241
+ step = step < 0 ? this.stepsList[ stepsList.length + step] : this.stepsList[ step ];
242
+ } else if (typeof step === "string") {
243
+ step = utils.byId(step);
244
+ }
245
+ return (step && step.id && this.stepsData[step.id]) ? step : null;
246
+ }
247
+
248
+ // `prev` API function goes to previous step (in document order)
249
+ prev () {
250
+ var prev = this.steps.indexOf( this.activeStep ) - 1;
251
+ prev = prev >= 0 ? this.steps[ prev ] : this.getStep(-1);
252
+
253
+ return this.moveTo(prev);
254
+ }
255
+
256
+ // `next` API function goes to next step (in document order)
257
+ next () {
258
+ var next = steps.indexOf( activeStep ) + 1;
259
+ next = next < steps.length ? steps[ next ] : steps[ 0 ];
260
+
261
+ return this.moveTo(next);
262
+ }
263
+
264
+ //thisClassNotThose(someEl, "setMe", "unsetMe", "unsetMeToo")
265
+ //e.g.
266
+ //thisClassNotThose(event.target, "present", "past", "future")
267
+ thisClassNotThose(element, setClass) { //...exclusiveClasses
268
+ var idx, length = arguments.length;
269
+ for(idx=2; idx < arguments.length; idx++){
270
+ element.classList.remove(arguments[idx]);
271
+ }
272
+ element.classList.add(setClass);
273
+ }
274
+
275
+ //General classes:
276
+ //past: we have visited this
277
+ //present: we're currently here
278
+ //future: we have never visited
279
+ //
280
+ //previous: we're "leaving" this one
281
+ //next: we're "going to" this one
282
+ //before: the current index > this one
283
+ //after: the current index < this one
284
+ //passing: index between next and previous index
285
+ //
286
+ //XXX Move to stationList
287
+ //Relation between current/next/etc step and slide and group
288
+ unmarkEndpoint(stepIndex, mark){
289
+ var containers = this.containingElements(this.steps[stepIndex]);
290
+ var slide = containers.slides[0];
291
+
292
+ containers.all.forEach(function(elem){ elem.classList.remove(mark); })
293
+
294
+ this.root.classList.remove(mark + "-" + slide.id)
295
+ }
296
+
297
+ markTime(stepIndex, mark){
298
+ var containers = this.containingElements(this.steps[stepIndex]);
299
+ var slide = containers.slides[0];
300
+
301
+ containers.all.forEach(function(elem){
302
+ this.thisClassNotThose(elem, mark, "past", "present", "future");
303
+ }, this)
304
+ }
305
+
306
+ markEndpoint(stepIndex, mark){
307
+ var containers = this.containingElements(this.steps[stepIndex]);
308
+ var slide = containers.slides[0];
309
+
310
+ containers.all.forEach(function(elem){
311
+ this.thisClassNotThose(elem, mark, "before", "after", "passing");
312
+ }, this)
313
+
314
+ this.root.classList.add(mark + "-" + slide.id);
315
+ return containers
316
+ }
317
+ }
@@ -0,0 +1,13 @@
1
+ import Step from './step.js';
2
+
3
+ export default class extends Step {
4
+ constructor(parent, element, indexes){
5
+ super(element, indexes);
6
+ this.parent = parent;
7
+ this.parent.addChild(this);
8
+ };
9
+
10
+ propagateDescendant(newChild){
11
+ this.parent.addDescendant(newChild);
12
+ };
13
+ }
@@ -0,0 +1,197 @@
1
+ //
2
+ // Able to relate selves to other steps in order to calculate motions
3
+ // - "which step is <motion> from you?"
4
+ // - "where is <step> in relation to you?"
5
+ //
6
+ //import Slide from './steps/slide.js';
7
+
8
+ export default class {
9
+ constructor(element, indexes){
10
+ this.element = element;
11
+ this.groups = [];
12
+ this.steps = [];
13
+
14
+ this.children = [];
15
+ this.indexes = {};
16
+ this.childrenById = {};
17
+
18
+ for(let field in indexes) {
19
+ this.indexes[field] = indexes[field];
20
+ };
21
+
22
+ this.element.classList.add("future");
23
+
24
+ this.firstSlide = null;
25
+ this.lastSlide = null;
26
+
27
+ this.prevSlide = null;
28
+ this.nextSlide = null;
29
+
30
+ this.firstItem = null;
31
+ this.lastItem = null;
32
+
33
+ this.prevItem = null;
34
+ this.nextItem = null;
35
+ }
36
+
37
+ toString() {
38
+ return "A Step " + this.element.id
39
+ }
40
+
41
+ treeFinished() {
42
+ }
43
+
44
+ addClass(name){
45
+ this.element.classList.add(name);
46
+ }
47
+
48
+ removeClass(name){
49
+ if(name instanceof RegExp){
50
+ Array.prototype.forEach.call(this.element.classList, function(klass){
51
+ if(name.test(klass)){
52
+ this.element.classList.remove(klass);
53
+ }
54
+ }, this)
55
+ } else {
56
+ this.element.classList.remove(name);
57
+ }
58
+ }
59
+
60
+ hasClass(name){
61
+ return this.element.classList.contains(name);
62
+ }
63
+
64
+ beginDeparture(){
65
+ this.addClass("previous");
66
+ this.removeClass("current");
67
+ this.parent.beginDeparture();
68
+ }
69
+
70
+ completeDeparture(){
71
+ this.removeClass("previous");
72
+
73
+ this.removeClass("present");
74
+ this.removeClass("future");
75
+ this.removeClass("current");
76
+ this.addClass("past");
77
+ this.parent.completeDeparture();
78
+ }
79
+
80
+ beginArrival(){
81
+ this.addClass("next");
82
+ this.parent.beginArrival();
83
+ }
84
+
85
+ completeArrival(){
86
+ this.removeClass("next");
87
+ this.addClass("current");
88
+
89
+ this.removeClass("future");
90
+ this.removeClass("past");
91
+ this.addClass("present");
92
+ this.parent.completeArrival();
93
+ }
94
+
95
+ cancelArrival(){
96
+ this.removeClass("next");
97
+ }
98
+
99
+ eachStep(dothis){
100
+ dothis(this);
101
+ this.children.forEach(function(step){
102
+ step.eachStep(dothis);
103
+ });
104
+ }
105
+
106
+ // Given a structure level, return the kind and direction of transition to another step
107
+ relativeLevelPosition(level, target){
108
+ if(!target){ return ["none", "same", level]; };
109
+ var difference = target.indexes[level] - this.indexes[level];
110
+
111
+ if(difference < -1){
112
+ return ["jump", "backwards", "by-" + level];
113
+ } else if(difference == -1){
114
+ return ["advance", "backwards", "by-" + level];
115
+ } else if(difference == 1){
116
+ return ["advance", "forwards", "by-" + level];
117
+ } else if(difference > 1){
118
+ return ["jump", "forwards", "by-" + level];
119
+ }
120
+
121
+ return ["none", "same", "by-" + level];
122
+ }
123
+
124
+ relativePosition(target){
125
+ var relPos = this.relativeLevelPosition("slide", target);
126
+ if(relPos[0] == "none"){
127
+ relPos = this.relativeLevelPosition("item", target);
128
+ }
129
+ return relPos;
130
+ }
131
+
132
+ addChild(newChild){
133
+ this.debugAssoc("Xanc", newChild);
134
+ var lastChild = this.children.slice(-1)[0];
135
+ if(lastChild){
136
+ this.debugAssoc("Xalc", lastChild);
137
+ newChild.addPrevStep(lastChild);
138
+ lastChild.addNextStep(newChild);
139
+ }
140
+ this.children.push(newChild);
141
+ this.addDescendant(newChild);
142
+ }
143
+
144
+ addDescendant(newChild){
145
+ this.childrenById[newChild.element.id] = newChild;
146
+
147
+ newChild.joinParent(this);
148
+
149
+ this.propagateDescendant(newChild);
150
+ }
151
+
152
+ joinParent(parent){}
153
+
154
+ lastChild(){
155
+ if(this.children.length > 0){
156
+ return this.children.slice(-1)[0];
157
+ } else {
158
+ return this
159
+ }
160
+ }
161
+
162
+ debugAssoc(assoc, other){
163
+ //console.log(assoc, this.toString(), other.toString());
164
+ }
165
+
166
+ addNextRoot(root){
167
+ this.debugAssoc("Xnr", root)
168
+ }
169
+
170
+ addPrevRoot(root){
171
+ this.debugAssoc("Xpr", root)
172
+ }
173
+
174
+ addNextGroup(group){
175
+ this.debugAssoc("Xng", group)
176
+ }
177
+
178
+ addPrevGroup(group){
179
+ this.debugAssoc("Xpg", group)
180
+ }
181
+
182
+ addNextSlide(slide){
183
+ this.debugAssoc("Xns", slide)
184
+ }
185
+
186
+ addPrevSlide(slide){
187
+ this.debugAssoc("Xps", slide)
188
+ }
189
+
190
+ addNextItem(item){
191
+ this.debugAssoc("Xni", item)
192
+ }
193
+
194
+ addPrevItem(item){
195
+ this.debugAssoc("Xpi", item)
196
+ }
197
+ }