rhet-butler 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }