bob-rails 0.0.1

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.
@@ -0,0 +1,1989 @@
1
+ (function() {
2
+ "use strict";
3
+ var utils$bind$$bind;
4
+
5
+ if (typeof Function.prototype.bind === 'function') {
6
+ utils$bind$$bind = function(func, target) {
7
+ return func && func.bind(target);
8
+ };
9
+ } else {
10
+ utils$bind$$bind = function(func, target) {
11
+ return func && function() {
12
+ func.apply(target, arguments);
13
+ };
14
+ };
15
+ }
16
+
17
+ var utils$bind$$default = utils$bind$$bind;
18
+
19
+ function utils$logger$$NOOP() { }
20
+
21
+ function utils$logger$$consoleMethod(console, name, fallback) {
22
+ var method = typeof console === 'object' ? console[name] : null;
23
+
24
+ if (method) {
25
+ // Older IE doesn't support bind/apply, but Chrome needs it
26
+ if (typeof method.bind === 'function' || typeof method.apply === 'function') {
27
+ return utils$bind$$default(method, console);
28
+ } else {
29
+ return function(message) {
30
+ method(message);
31
+ };
32
+ }
33
+ } else if (fallback) {
34
+ return fallback;
35
+ } else {
36
+ // Use the log method with a tag (e.g. [WARN]) as a default fallback
37
+ var tag = '[' + name.toUpperCase() + '] ';
38
+ return function(message) {
39
+ this.log(tag + message);
40
+ }
41
+ }
42
+ }
43
+
44
+ var utils$logger$$LEVELS = {
45
+ debug: 0,
46
+ info: 1,
47
+ warn: 2,
48
+ error: 3,
49
+ all: 4
50
+ };
51
+
52
+ function utils$logger$$Logger(console, minLevel) {
53
+ minLevel = utils$logger$$LEVELS[minLevel] - 1;
54
+
55
+ /**
56
+ * Logs *message* to the console.
57
+ *
58
+ * @method log
59
+ * @params {Object} [message] The message to log.
60
+ */
61
+ this.log = utils$logger$$consoleMethod(console, 'log', utils$logger$$NOOP);
62
+
63
+ /**
64
+ * Logs *message* to the console at the `debug` level.
65
+ *
66
+ * @method debug
67
+ * @params {Object} [message] The message to log.
68
+ */
69
+ this.debug = (minLevel >= utils$logger$$LEVELS.debug) ? utils$logger$$NOOP : utils$logger$$consoleMethod(console, 'debug');
70
+
71
+ /**
72
+ * Logs *message* to the console at the `info` level.
73
+ *
74
+ * @method info
75
+ * @params {Object} [message] The message to log.
76
+ */
77
+ this.info = (minLevel >= utils$logger$$LEVELS.info) ? utils$logger$$NOOP : utils$logger$$consoleMethod(console, 'info');
78
+
79
+ /**
80
+ * Logs *message* to the console at the `warn` level.
81
+ *
82
+ * @method warn
83
+ * @params {Object} [message] The message to log.
84
+ */
85
+ this.warn = (minLevel >= utils$logger$$LEVELS.warn) ? utils$logger$$NOOP : utils$logger$$consoleMethod(console, 'warn');
86
+
87
+ /**
88
+ * Logs *message* to the console at the `error` level.
89
+ *
90
+ * @method log
91
+ * @params {Object} [message] The message to log.
92
+ */
93
+ this.error = (minLevel >= utils$logger$$LEVELS.error) ? utils$logger$$NOOP : utils$logger$$consoleMethod(console, 'error');
94
+ }
95
+ var utils$logger$$default = utils$logger$$Logger;
96
+ var utils$create$$create;
97
+
98
+ function utils$create$$Factory() {}
99
+
100
+ if (typeof Object.create === "function") {
101
+ utils$create$$create = Object.create;
102
+ } else {
103
+ utils$create$$create = function(proto) {
104
+ if (typeof proto !== "object" && typeof proto !== "function") {
105
+ throw new TypeError("Object prototype may only be an Object");
106
+ }
107
+
108
+ utils$create$$Factory.prototype = proto;
109
+
110
+ return new utils$create$$Factory();
111
+ }
112
+ }
113
+
114
+ var utils$create$$default = utils$create$$create;
115
+ function utils$object$$REQUIRED() {
116
+ return utils$object$$REQUIRED;
117
+ }
118
+
119
+ utils$object$$REQUIRED.toString = function() {
120
+ return "(Required Property)";
121
+ };
122
+
123
+ function utils$object$$LAZY(factory) {
124
+ if (this instanceof utils$object$$LAZY) {
125
+ this.factory = factory;
126
+ } else {
127
+ return new utils$object$$LAZY(factory);
128
+ }
129
+ }
130
+
131
+ utils$object$$LAZY.prototype.toString = function() {
132
+ return "(Lazy Property)";
133
+ };
134
+
135
+ /**
136
+ * The base class for other objects in Bob. The purpose of this base class is to
137
+ * facilate class-based inheritance in JavaScript by providing helpers for
138
+ * subclassing, overriding methods and introspecting the inheritance chain.
139
+ *
140
+ * @class Object
141
+ * @namespace Bob
142
+ */
143
+ var utils$object$$BobObject;
144
+
145
+ function utils$object$$NOOP() { }
146
+
147
+ function utils$object$$wrap(func, superFunc) {
148
+ if (
149
+ typeof superFunc === "undefined" ||
150
+ typeof superFunc !== "function" ||
151
+ superFunc.__class__ ||
152
+ superFunc.__interface__ ||
153
+ superFunc === utils$object$$REQUIRED ||
154
+ superFunc instanceof utils$object$$LAZY
155
+ ) {
156
+ return func;
157
+ }
158
+
159
+ function superWrapper() {
160
+ var ret;
161
+ var sup = this && this.__nextSuper;
162
+ if(this) { this.__nextSuper = superFunc; }
163
+ ret = func.apply(this, arguments);
164
+ if(this) { this.__nextSuper = sup; }
165
+ return ret;
166
+ }
167
+
168
+ return superWrapper;
169
+ }
170
+
171
+ function utils$object$$mergeProtoProps(proto, protoProps) {
172
+ for (var key in protoProps) {
173
+ if (protoProps.hasOwnProperty(key)) {
174
+ if (typeof protoProps[key] === "function") {
175
+ proto[key] = utils$object$$wrap(protoProps[key], proto[key]);
176
+ } else {
177
+ proto[key] = protoProps[key];
178
+ }
179
+ }
180
+ }
181
+
182
+ return proto;
183
+ }
184
+
185
+ function utils$object$$classToString() {
186
+ if (this.__name__) {
187
+ return this.__name__;
188
+ } else {
189
+ var klass = this;
190
+
191
+ while (klass && !klass.__name__) {
192
+ klass = klass.superclass;
193
+ }
194
+
195
+ if (klass && klass.__name__) {
196
+ return "(subclass of " + klass.__name__ + ")";
197
+ } else {
198
+ return "(unknown class)";
199
+ }
200
+ }
201
+ }
202
+
203
+ function utils$object$$initializeLazyProperties(instance, props) {
204
+ props.forEach(function(key) {
205
+ if(instance[key] instanceof utils$object$$LAZY) {
206
+ instance[key] = instance[key].factory.call();
207
+ }
208
+ });
209
+ }
210
+
211
+ function utils$object$$ensureRequired(instance, props) {
212
+ props.forEach(function(key) {
213
+ if(instance[key] === utils$object$$REQUIRED) {
214
+ throw key + " is required for " + instance.constructor.toString();
215
+ }
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Creates a subclass.
221
+ *
222
+ * ```javascript
223
+ * var Vehicle = Bob.Object.extend();
224
+ * var Car = Vehicle.extend();
225
+ *
226
+ * var myBike = new Vehicle();
227
+ *
228
+ * myBike instanceof Object // => true
229
+ * myBike instanceof Bob.Object // => true
230
+ * myBike instanceof Vehicle // => true
231
+ * myBike instanceof Car // => false
232
+ * ```
233
+ *
234
+ * You can also pass an object to `extend` that contains a set of properties to
235
+ * define on the class's prototype. Functions inside the object becomes methods
236
+ * on the subclass. If they override an existing method on the super class, then
237
+ * the superclass' implmentation will be available through `this._super` when
238
+ * the method is invoked (see its documentaion for details). Methods should only
239
+ * be defined on a class at `extend` time, otherwise `this._super` would not
240
+ * function correctly.
241
+ *
242
+ * @method extend
243
+ * @static
244
+ * @param {Object} [protoProps] A object containing the properties and methods
245
+ * to define on the subclass.
246
+ *
247
+ * @return {Class} The constructor function for the newly created subclass.
248
+ */
249
+ function utils$object$$extend(protoProps) {
250
+ var SuperClass = this, required = [], lazy = [];
251
+
252
+ function Class() {
253
+ utils$object$$initializeLazyProperties(this, lazy);
254
+ this.init.apply(this, arguments);
255
+ utils$object$$ensureRequired(this, required);
256
+ };
257
+
258
+ /**
259
+ * @private
260
+ *
261
+ * @property __class__
262
+ * @type Boolean
263
+ * @static
264
+ */
265
+ Class.__class__ = true;
266
+
267
+ /**
268
+ * Points to the class' parent.
269
+ *
270
+ * @property superclass
271
+ * @type Class
272
+ * @static
273
+ */
274
+ Class.superclass = SuperClass;
275
+ Class.extend = utils$object$$extend;
276
+ Class.toString = utils$object$$classToString;
277
+
278
+ Class.prototype = utils$create$$default(SuperClass.prototype);
279
+ Class.prototype = utils$object$$mergeProtoProps(Class.prototype, protoProps);
280
+
281
+ /**
282
+ * Points to the class that created the instance.
283
+ *
284
+ * @property constructor
285
+ * @type Class
286
+ */
287
+ Class.prototype.constructor = Class;
288
+
289
+ // Cache the required & lazy attributes (they can only be added at `extend` time)
290
+ for (var key in Class.prototype) {
291
+ if(Class.prototype[key] === utils$object$$REQUIRED) {
292
+ required.push(key);
293
+ } else if (Class.prototype[key] instanceof utils$object$$LAZY) {
294
+ lazy.push(key);
295
+ }
296
+ }
297
+
298
+ return Class;
299
+ }
300
+
301
+ utils$object$$BobObject = utils$object$$extend.call(Object, {
302
+
303
+ /**
304
+ * Called when an instance is created with the same arguments that is passed to
305
+ * the constructor function.
306
+ *
307
+ * ```javascript
308
+ * var Person = Bob.Object.extend({
309
+ * init: function(name) {
310
+ * this.name = name;
311
+ * }
312
+ * });
313
+ *
314
+ * var bob = new Person("Bob");
315
+ *
316
+ * bob.name // => "Bob"
317
+ * ```
318
+ *
319
+ * @method init
320
+ */
321
+ init: utils$object$$NOOP,
322
+
323
+ /**
324
+ * When invoking an overriden method on a subclass, use this method to access
325
+ * the original implementation:
326
+ *
327
+ * ```javascript
328
+ * var Person = Bob.Object.extend({
329
+ * init: function(name) {
330
+ * this.name = name;
331
+ * },
332
+ *
333
+ * sayGreeting: function() {
334
+ * return "Hello from " + this.name;
335
+ * }
336
+ * });
337
+ *
338
+ * var AngryPerson = Person.extend({
339
+ * sayGreeting: function() {
340
+ * return this._super().toUpperCase() + "!!!";
341
+ * }
342
+ * });
343
+ *
344
+ * var bob = new Person("Bob");
345
+ *
346
+ * bob.sayGreeting() // => "Hello from Bob"
347
+ *
348
+ * var angryBob = new Person("Bob");
349
+ *
350
+ * angryBob.sayGreeting() // => "HELLO FROM BOB!!!"
351
+ * ```
352
+ *
353
+ * NOTE: For this feature to work properly, methods can only be added to a
354
+ * class' prototype at `extend` time.
355
+ *
356
+ * @method _super
357
+ */
358
+ _super: function() {
359
+ var func = this.__nextSuper;
360
+ var ret;
361
+ if (func) {
362
+ this.__nextSuper = null;
363
+ ret = func.apply(this, arguments);
364
+ this.__nextSuper = func;
365
+ }
366
+ return ret;
367
+ },
368
+
369
+ toString: function() {
370
+ return "<" + this.constructor.toString() + ">";
371
+ }
372
+
373
+ });
374
+
375
+ /**
376
+ * @private
377
+ *
378
+ * A descriptor for the class (used by `toString`).
379
+ *
380
+ * @property __name__
381
+ * @type String
382
+ * @static
383
+ */
384
+ utils$object$$BobObject.__name__ = "Bob.Object";
385
+
386
+ var utils$object$$default = utils$object$$BobObject;
387
+
388
+ /**
389
+ * The is somewhat inspired by the Ember container (since "container" has a
390
+ * different meaning in Bob, it's called a registry instead). As usual, we only
391
+ * implement a very small subset of features compared to the Ember-eqivilant.
392
+ */
393
+
394
+ var utils$registry$$NullRegistry = {
395
+ lookup: function() {
396
+ return undefined;
397
+ },
398
+
399
+ hasKey: function() {
400
+ return false;
401
+ }
402
+ };
403
+
404
+ var utils$registry$$default = utils$object$$default.extend({
405
+
406
+ init: function(parent) {
407
+ this.parent = parent || utils$registry$$NullRegistry;
408
+ this.content = {};
409
+ },
410
+
411
+ /**
412
+ * Register an object under *key*.
413
+ *
414
+ * By convention, the format of *key* should be:
415
+ *
416
+ * type:path
417
+ *
418
+ * Where `type` is an `identifier` and `path` is one or more `identifier`s
419
+ * joined by "/". An `identifer` should begin with a lowercase letter (a-z),
420
+ * followed by any number of lowercase letters (a-z), numbers (0-9) or the "-"
421
+ * character (a "-" should not be the last character of an `identifier`).
422
+ *
423
+ * Some examples:
424
+ *
425
+ * - "template:modern"
426
+ * - "view:container"
427
+ * - "view:container/sidebar"
428
+ * - "view:container/main/column-2"
429
+ *
430
+ * @method register
431
+ * @params {String} [key] See above.
432
+ * @params {*} [object] The object to register under *key*.
433
+ */
434
+ register: function(key, object) {
435
+ this.content[key] = object;
436
+ return object;
437
+ },
438
+
439
+ /**
440
+ * Unregister *key*.
441
+ *
442
+ * @method unregister
443
+ * @params {String} [key] See `register`.
444
+ * @return {*} The object previously registered under *key*, if any.
445
+ */
446
+ unregister: function(key) {
447
+ var object = this.content[key];
448
+ delete this.content[key];
449
+ return object;
450
+ },
451
+
452
+ /**
453
+ * Lookup the object registered under *key*.
454
+ *
455
+ * @method lookup
456
+ * @params {String} [key] See `register`.
457
+ * @return {*} The object registered under *key*, if any.
458
+ */
459
+ lookup: function(key) {
460
+ if(this.content.hasOwnProperty(key)) {
461
+ return this.content[key];
462
+ } else {
463
+ return this.parent.lookup(key);
464
+ }
465
+ },
466
+
467
+ /**
468
+ * Check if an object has been registered under *key*.
469
+ *
470
+ * @method hasKey
471
+ * @params {String} [key] See `register`.
472
+ * @params {Boolean} [checkParents=false] Also check if *key* is present on the parents.
473
+ * @return {Boolean} Whether *key* has been registered.
474
+ */
475
+ hasKey: function(key, checkParents) {
476
+ checkParents = checkParents || false;
477
+ return this.content.hasOwnProperty(key) || (checkParents && this.parent.hasKey(key, true));
478
+ }
479
+
480
+ });
481
+
482
+ /**
483
+ * The main Bob namespace.
484
+ *
485
+ * @class Bob
486
+ * @static
487
+ */
488
+ var core$$Bob = {
489
+
490
+ /**
491
+ * A shared logger instance. You can replace this with another logger to
492
+ * control what gets logged and where to log message goes.
493
+ *
494
+ * By default, this propoerty contains a `Bob.Logger` that logs to global
495
+ * `console` object at the `"info"` log level.
496
+ *
497
+ * @property logger
498
+ * @type Bob.Logger
499
+ */
500
+ logger: new utils$logger$$default(console, "info"),
501
+
502
+ /**
503
+ * @private
504
+ *
505
+ * The global registry.
506
+ *
507
+ * @property registry
508
+ * @type Registry
509
+ */
510
+ registry: new utils$registry$$default(null),
511
+
512
+ /**
513
+ * Register an object under *key*.
514
+ *
515
+ * By convention, the format of *key* should be:
516
+ *
517
+ * type:path
518
+ *
519
+ * Where `type` is an `identifier` and `path` is one or more `identifier`s
520
+ * joined by "/". An `identifer` should begin with a lowercase letter (a-z),
521
+ * followed by any number of lowercase letters (a-z), numbers (0-9) or the "-"
522
+ * character (a "-" should not be the last character of an `identifier`).
523
+ *
524
+ * Some examples:
525
+ *
526
+ * - "template:modern"
527
+ * - "view:container"
528
+ * - "view:container/sidebar"
529
+ * - "view:container/main/column-2"
530
+ *
531
+ * @method register
532
+ * @params {String} [key] See above.
533
+ * @params {*} [object] The object to register under *key*.
534
+ */
535
+ register: function(key, object) {
536
+ this.registry.register(key, object);
537
+ }
538
+
539
+ };
540
+
541
+ var core$$default = core$$Bob;
542
+
543
+ function utils$hash$lookup$$test(value) {
544
+ return value !== null && value !== undefined && value !== "";
545
+ }
546
+
547
+ var utils$hash$lookup$$default = utils$object$$default.extend({
548
+
549
+ init: function(hash) {
550
+ this.hash = hash || {};
551
+ },
552
+
553
+ /**
554
+ * Lookup the given `path` on the hash. This performs a recurrsive lookup,
555
+ * i.e. if you pass `"some.nested.key"`, it will return the value stored at
556
+ * `hash.some.nested.key`. If the path or any intermediate keys along it is
557
+ * `undefined`, `null` or an empty string, the `fallback` value (defaults to
558
+ * an empty string) is returned instead.
559
+ *
560
+ * @method get
561
+ * @params {String} [path] The lookup path.
562
+ * @params {Object} [fallback=""] The fallback value.
563
+ * @return {Object} The value at the given `path` on the lookup hash, or the
564
+ * `fallback` value if it does not exist.
565
+ */
566
+ get: function(path, fallback) {
567
+ if (arguments.length < 2) {
568
+ fallback = "";
569
+ }
570
+
571
+ var keys = path.split("."), lookup = this.hash;
572
+
573
+ while(true) {
574
+ if (!utils$hash$lookup$$test(lookup)) {
575
+ return fallback;
576
+ } else if(keys.length === 0) {
577
+ return lookup;
578
+ } else {
579
+ lookup = lookup[keys.shift()];
580
+ }
581
+ }
582
+ },
583
+
584
+ /**
585
+ * Set the given `path` to a specific value.This performs a recurrsive set,
586
+ * i.e. if you pass `"some.nested.key"`, it will set the value stored at
587
+ * `hash.some.nested.key`, creating If any intermediate keys along the path
588
+ * is `undefined` or `null`, they will first be initialized to an empty
589
+ * object (`{}`).
590
+ *
591
+ * @method set
592
+ * @params {String} [path] The path to set.
593
+ * @params {Object} [value] The value to set.
594
+ * @return {Object} The value at the given `path` on the lookup hash, or the
595
+ * `fallback` value if it does not exist.
596
+ */
597
+ set: function(path, value) {
598
+ var keys = path.split("."), lookup = this.hash;
599
+
600
+ while(keys.length > 1) {
601
+ if (!utils$hash$lookup$$test(lookup[keys[0]])) {
602
+ lookup[keys[0]] = {};
603
+ }
604
+
605
+ lookup = lookup[keys.shift()];
606
+ }
607
+
608
+ lookup[keys.shift()] = value;
609
+ }
610
+
611
+ });
612
+
613
+ function dom$document$$emptyNode(node) {
614
+ while (node && node.childNodes.length) {
615
+ node.removeChild(node.firstChild);
616
+ }
617
+
618
+ return node;
619
+ }
620
+
621
+ function dom$document$$doesItWork(func){
622
+ try {
623
+ return !! func.call();
624
+ } catch(e) {
625
+ return false;
626
+ }
627
+ }
628
+
629
+ var dom$document$$supportsCreateHTMLDocument = dom$document$$doesItWork(function() {
630
+ var doc = document.implementation.createHTMLDocument();
631
+
632
+ dom$document$$emptyNode(doc);
633
+
634
+ return doc &&
635
+ (doc.nodeType === Node.DOCUMENT_NODE) &&
636
+ (doc.compatMode === "CSS1Compat") &&
637
+ (doc.children.length === 0) &&
638
+ (doc.documentElement === null);
639
+ });
640
+
641
+ var dom$document$$createDocument;
642
+
643
+ if (dom$document$$supportsCreateHTMLDocument) {
644
+ dom$document$$createDocument = function() {
645
+ return dom$document$$emptyNode(document.implementation.createHTMLDocument());
646
+ };
647
+ } else {
648
+ dom$document$$createDocument = function() {
649
+ var iframe = document.createElement("iframe");
650
+ var parent = document.body || document.documentElement;
651
+
652
+ iframe.style.display = "none";
653
+ iframe.src = "about:blank";
654
+
655
+ try {
656
+ parent.appendChild(iframe);
657
+
658
+ iframe.contentDocument.open();
659
+ iframe.contentDocument.write("<!doctype HTML><html><head></head><body></body></html>");
660
+ iframe.contentDocument.close();
661
+
662
+ return dom$document$$emptyNode(iframe.contentDocument);
663
+ } finally {
664
+ try {
665
+ parent.removeChild(iframe);
666
+ } catch(e) {
667
+ // The insertion probably failed
668
+ }
669
+ }
670
+ };
671
+ }
672
+
673
+ function dom$document$$prepareDocument(doc) {
674
+ return dom$document$$emptyNode(doc);
675
+ }
676
+
677
+ var dom$dom$builder$$default = utils$object$$default.extend({
678
+
679
+ init: function(document) {
680
+ this.stack = [];
681
+
682
+ /**
683
+ * The underlying `Document` object.
684
+ *
685
+ * @property document
686
+ * @type Document
687
+ */
688
+ this.document = document;
689
+ },
690
+
691
+ /**
692
+ * Create an element and push it on to the stack.
693
+ *
694
+ * @method createElement
695
+ * @param {String} [tagName] The tag name for the element.
696
+ */
697
+ createElement: function(tagName) {
698
+ this.stack.push( this.document.createElement(tagName) );
699
+ },
700
+
701
+ /**
702
+ * Get the element on the top of the stack.
703
+ *
704
+ * @method getElement
705
+ * @return {Element} The element on the top of the stack.
706
+ */
707
+ getElement: function() {
708
+ return this.stack[ this.stack.length - 1 ];
709
+ },
710
+
711
+ /**
712
+ * Get the element on the top of the stack with an assertion.
713
+ *
714
+ * @private
715
+ * @method ensureElement
716
+ * @params {Integer} [count=1] Minimum number of elements on the stack.
717
+ * @return {Element} The element on the top of the stack.
718
+ */
719
+ ensureElement: function(count) {
720
+ count = count || 1;
721
+
722
+ if (count === 1 && this.stack.length === 0) {
723
+ throw "This operation requires an element on the stack but there are none.";
724
+ } else if(this.stack.length < count) {
725
+ throw "This operation requires "+ count + " elements on the stack but there are only " + this.stack.length + ".";
726
+ }
727
+
728
+ return this.getElement();
729
+ },
730
+
731
+ /**
732
+ * Set an attribute for the element on the top of the stack. Throws an error
733
+ * if the stack is empty.
734
+ *
735
+ * @method setAttribute
736
+ * @params {String} [name] The name of the attribute to set.
737
+ * @params {String} [value] The value of the attribute to set.
738
+ */
739
+ setAttribute: function(name, value) {
740
+ this.ensureElement().setAttribute(name, value);
741
+ },
742
+
743
+ /**
744
+ * Create a text node and immediately append it to the current element on the
745
+ * top of the stack.
746
+ *
747
+ * @method appendTextNode
748
+ * @params {String} [content] The content of the text node to append.
749
+ */
750
+ appendTextNode: function(content) {
751
+ this.ensureElement().appendChild( this.document.createTextNode(content) );
752
+ },
753
+
754
+ /**
755
+ * Create a comment node and immediately append it to the current element on
756
+ * the top of the stack.
757
+ *
758
+ * @method appendComment
759
+ * @params {String} [content] The content of the comment to append.
760
+ */
761
+ appendComment: function(content) {
762
+ this.ensureElement().appendChild( this.document.createComment(content) );
763
+ },
764
+
765
+ /**
766
+ * Append the top element on the stack to the previous element and pop the top
767
+ * element off the stack.
768
+ *
769
+ * @method appendElement
770
+ */
771
+ appendElement: function() {
772
+ this.ensureElement(2);
773
+ var child = this.stack.pop(), parent = this.getElement();
774
+ parent.appendChild( child );
775
+ }
776
+ });
777
+
778
+ function dom$serialize$$escapeHTML(text) {
779
+ return text
780
+ .replace(/&/g, '&amp;')
781
+ .replace(/</g, '&lt;')
782
+ .replace(/>/g, '&gt;')
783
+ .replace(/\//g, '&#x2F;');
784
+ }
785
+
786
+ // We can't apply the HTML entities escape inside a <style> tag
787
+ function dom$serialize$$escapeStyleTag(text) {
788
+ return text.replace(/<\//g, "\\00003C\\00002F");
789
+ }
790
+
791
+ function dom$serialize$$escapeAttribute(text) {
792
+ return text
793
+ .replace(/"/g, '&quot;')
794
+ .replace(/'/g, '&#x27;');
795
+ }
796
+
797
+ // See http://www.w3.org/TR/html-markup/spec.html#comments
798
+ function dom$serialize$$escapeComment(text) {
799
+ return text
800
+ .replace(/--/g, '')
801
+ .replace(/^-?>/g, '')
802
+ .replace(/-$/g, '');
803
+ }
804
+
805
+ var dom$serialize$$voidTags = [
806
+ "area", "base", "br", "col", "command", "embed", "hr", "img", "input",
807
+ "keygen", "link", "meta", "param", "source", "track", "wbr"
808
+ ];
809
+
810
+ function dom$serialize$$isVoid(element) {
811
+ return dom$serialize$$voidTags.indexOf(element.nodeName.toLowerCase()) >= 0;
812
+ }
813
+
814
+ function dom$serialize$$elementToString(element, xhtml) {
815
+ var i, buffer = [];
816
+
817
+ buffer.push("<");
818
+ buffer.push(element.nodeName.toLowerCase());
819
+
820
+ for (i=0; i<element.attributes.length; i++) {
821
+ buffer.push(" ");
822
+ buffer.push(element.attributes[i].name);
823
+ buffer.push("=\"");
824
+ buffer.push(dom$serialize$$escapeAttribute(element.attributes[i].value));
825
+ buffer.push("\"");
826
+ }
827
+
828
+ if (dom$serialize$$isVoid(element)) {
829
+ buffer.push( xhtml ? " />" : ">" );
830
+ } else {
831
+ buffer.push(">");
832
+
833
+ for (i=0; i<element.childNodes.length; i++) {
834
+ buffer.push(dom$serialize$$serialize(element.childNodes[i], xhtml, element));
835
+ }
836
+
837
+ buffer.push("</");
838
+ buffer.push(element.nodeName.toLowerCase());
839
+ buffer.push(">");
840
+ }
841
+
842
+ return buffer.join("");
843
+ }
844
+
845
+ function dom$serialize$$serialize(node, xhtml, context) {
846
+ switch(node.nodeType) {
847
+ case Node.DOCUMENT_NODE:
848
+ return dom$serialize$$serialize(node.documentElement, xhtml);
849
+
850
+ case Node.ELEMENT_NODE:
851
+ return dom$serialize$$elementToString(node, xhtml);
852
+
853
+ case Node.TEXT_NODE:
854
+ if (context && context.nodeName.toLowerCase() === "style") {
855
+ return dom$serialize$$escapeStyleTag(node.nodeValue);
856
+ } else {
857
+ return dom$serialize$$escapeHTML(node.nodeValue);
858
+ }
859
+
860
+ case Node.COMMENT_NODE:
861
+ return "<!--" + dom$serialize$$escapeComment(node.nodeValue) + "-->";
862
+
863
+ default:
864
+ return "";
865
+ }
866
+ }
867
+ var dom$serialize$$default = dom$serialize$$serialize;
868
+ function utils$interface$$Interface(name, properties) {
869
+ if (properties.hasOwnProperty("constructor")) {
870
+ throw "An interface cannot have a `constructor` property";
871
+ }
872
+
873
+ var required = [], lazy = [];
874
+
875
+ function __Interface__(obj) {
876
+ if (obj instanceof __Interface__) {
877
+ return obj;
878
+ } else if (this instanceof __Interface__) {
879
+ for (var key in obj) {
880
+ if (obj.hasOwnProperty(key)) {
881
+ this[key] = obj[key];
882
+ }
883
+ }
884
+
885
+ utils$object$$initializeLazyProperties(this, lazy);
886
+ utils$object$$ensureRequired(this, required);
887
+ } else {
888
+ return new __Interface__(obj);
889
+ }
890
+ }
891
+
892
+ __Interface__.prototype = utils$create$$default(utils$interface$$Interface.prototype);
893
+
894
+ __Interface__.prototype.constructor = __Interface__;
895
+
896
+ __Interface__.superclass = utils$interface$$Interface;
897
+
898
+ __Interface__.__interface__ = true;
899
+
900
+ __Interface__.__name__ = name;
901
+
902
+ __Interface__.toString = function() {
903
+ return name;
904
+ };
905
+
906
+ for (var key in properties) {
907
+ if (properties.hasOwnProperty(key)) {
908
+ __Interface__.prototype[key] = properties[key];
909
+
910
+ if(properties[key] === utils$object$$REQUIRED) {
911
+ required.push(key);
912
+ } else if (properties[key] instanceof utils$object$$LAZY) {
913
+ lazy.push(key);
914
+ }
915
+ }
916
+ }
917
+
918
+ return __Interface__;
919
+ }
920
+
921
+ var utils$interface$$default = utils$interface$$Interface;
922
+
923
+ utils$interface$$Interface.prototype = {};
924
+
925
+ utils$interface$$Interface.superclass = Object;
926
+
927
+ utils$interface$$Interface.__name__ = "Interface";
928
+
929
+ utils$interface$$Interface.toString = function() {
930
+ return "Interface";
931
+ };
932
+
933
+ utils$interface$$Interface.extend = function(name, props) {
934
+ return utils$interface$$Interface(name, props);
935
+ };
936
+
937
+ var template$layout$$default = utils$interface$$default("Layout", {
938
+
939
+ /**
940
+ * The root element of the layout. Must be an acceptable `documentElement` for
941
+ * the specified `doctype`.
942
+ *
943
+ * @property element
944
+ * @type Element
945
+ * @required
946
+ */
947
+ element: utils$object$$REQUIRED,
948
+
949
+ /**
950
+ * The main container that content blocks can be inserted into.
951
+ *
952
+ * @property container
953
+ * @type Container
954
+ * @required
955
+ */
956
+ container: utils$object$$REQUIRED,
957
+
958
+ /**
959
+ * The editable elements in the layout. These elements should be a child of
960
+ * `element` (or `element` itself) and they should not overlap (i.e. editable
961
+ * elements cannot be nested).
962
+ *
963
+ * @property editables
964
+ * @type Editable[]
965
+ * @default []
966
+ */
967
+ editables: utils$object$$LAZY(Array)
968
+
969
+ });
970
+
971
+ var template$block$$default = utils$interface$$default("Block", {
972
+
973
+ /**
974
+ * The type of the block.
975
+ *
976
+ * @property type
977
+ * @type String
978
+ * @required
979
+ */
980
+ type: utils$object$$REQUIRED,
981
+
982
+ /**
983
+ * The root element of the content block. Must be a block-level element.
984
+ *
985
+ * @property element
986
+ * @type Element
987
+ * @required
988
+ */
989
+ element: utils$object$$REQUIRED,
990
+
991
+ /**
992
+ * The editable elements in the block. These elements should be a child of
993
+ * `element` (or `element` itself) and they should not overlap (i.e. editable
994
+ * elements cannot be nested).
995
+ *
996
+ * @property editables
997
+ * @type Editable[]
998
+ * @default []
999
+ */
1000
+ editables: utils$object$$LAZY(Array)
1001
+
1002
+ });
1003
+
1004
+ var template$editable$$default = utils$interface$$default("Editable", {
1005
+
1006
+ /**
1007
+ * An identifier for the editable element.
1008
+ *
1009
+ * @property name
1010
+ * @type String
1011
+ * @required
1012
+ */
1013
+ name: utils$object$$REQUIRED,
1014
+
1015
+ /**
1016
+ * The type of the editable content.
1017
+ *
1018
+ * @property type
1019
+ * @type String
1020
+ * @required
1021
+ */
1022
+ type: utils$object$$REQUIRED,
1023
+
1024
+ /**
1025
+ * Options for the editor plugin.
1026
+ *
1027
+ * @property options
1028
+ * @type Object
1029
+ * @default {}
1030
+ */
1031
+ options: utils$object$$LAZY(Object),
1032
+
1033
+ /**
1034
+ * The editable element.
1035
+ *
1036
+ * @property element
1037
+ * @type Element
1038
+ * @required
1039
+ */
1040
+ element: utils$object$$REQUIRED
1041
+
1042
+ });
1043
+
1044
+ var template$container$$default = utils$interface$$default("Container", {
1045
+
1046
+ /**
1047
+ * The root element of the content block. The element should initially be
1048
+ * empty and it should be a valid parent for block-level elements.
1049
+ *
1050
+ * @property element
1051
+ * @type Element
1052
+ * @required
1053
+ */
1054
+ element: utils$object$$REQUIRED
1055
+
1056
+ });
1057
+
1058
+ var views$states$$default = [
1059
+ {
1060
+ name: "hasElement",
1061
+ transitions: [
1062
+ {
1063
+ name: "insertElement",
1064
+ next: "inDOM",
1065
+ before: "willInsertElement",
1066
+ after: "didInsertElement"
1067
+ }
1068
+ ]
1069
+ },
1070
+
1071
+ {
1072
+ name: "inDOM",
1073
+ transitions: [
1074
+ {
1075
+ name: "detachElement",
1076
+ next: "detached",
1077
+ before: "willDetachElement"
1078
+ },
1079
+ {
1080
+ name: "destroyElement",
1081
+ next: "destroyed",
1082
+ before: "willDestroyElement",
1083
+ cascade: true
1084
+ }
1085
+ ]
1086
+ },
1087
+
1088
+ {
1089
+ name: "detached",
1090
+ transitions: [
1091
+ {
1092
+ name: "reattachElement",
1093
+ next: "inDOM",
1094
+ before: "willReattachElement",
1095
+ after: "didReattachElement",
1096
+ },
1097
+
1098
+ ]
1099
+ },
1100
+
1101
+ {
1102
+ name: "destroyed"
1103
+ }
1104
+ ];
1105
+
1106
+ /**
1107
+ * @private
1108
+ *
1109
+ * @class State
1110
+ * @namespace Interface
1111
+ */
1112
+ var views$state$machine$$State = utils$interface$$default("State", {
1113
+
1114
+ /**
1115
+ * @property name
1116
+ * @type String
1117
+ * @required
1118
+ */
1119
+ name: utils$object$$REQUIRED,
1120
+
1121
+ /**
1122
+ * @property transitions
1123
+ * @type Transition[]
1124
+ * @default []
1125
+ */
1126
+ transitions: utils$object$$LAZY(Array)
1127
+
1128
+ });
1129
+
1130
+ /**
1131
+ * @private
1132
+ *
1133
+ * @class Transition
1134
+ * @namespace Interface
1135
+ */
1136
+ var views$state$machine$$Transition = utils$interface$$default("Transition", {
1137
+
1138
+ /**
1139
+ * @property name
1140
+ * @type String
1141
+ * @required
1142
+ */
1143
+ name: utils$object$$REQUIRED,
1144
+
1145
+ /**
1146
+ * @property next
1147
+ * @type String
1148
+ * @required
1149
+ */
1150
+ next: utils$object$$REQUIRED,
1151
+
1152
+ /**
1153
+ * @property before
1154
+ * @type String
1155
+ * @default undefined
1156
+ */
1157
+ before: undefined,
1158
+
1159
+ /**
1160
+ * @property after
1161
+ * @type String
1162
+ * @default undefined
1163
+ */
1164
+ after: undefined,
1165
+
1166
+ /**
1167
+ * @property cascade
1168
+ * @type Boolean
1169
+ * @default false
1170
+ */
1171
+ cascade: utils$object$$LAZY(Boolean)
1172
+
1173
+ });
1174
+
1175
+ function views$state$machine$$setState(newState) {
1176
+ this.state = newState;
1177
+ }
1178
+
1179
+ function views$state$machine$$apply(instance, method, args) {
1180
+ if (typeof method !== "function") {
1181
+ method = instance[method];
1182
+ }
1183
+
1184
+ if (args && args.length) {
1185
+ return method.apply(instance, [].slice.call(args));
1186
+ } else {
1187
+ return method.call(instance);
1188
+ }
1189
+ }
1190
+
1191
+ function views$state$machine$$applyPreOrder(instance, method, args) {
1192
+ var childViews = instance.childViews;
1193
+
1194
+ for (var i=0; i<childViews.length; i++) {
1195
+ views$state$machine$$applyPreOrder(childViews[i], method, args);
1196
+ }
1197
+
1198
+ return views$state$machine$$apply(instance, method, args);
1199
+ }
1200
+
1201
+ function views$state$machine$$makeTransition(state, transition) {
1202
+ return function() {
1203
+ var rtn;
1204
+
1205
+ if (this.state !== state.name) {
1206
+ throw "Cannot call " + transition.name + " from the " + this.state + " state: " + this.toString();
1207
+ }
1208
+
1209
+ if (transition.before) {
1210
+ views$state$machine$$applyPreOrder(this, transition.before);
1211
+ }
1212
+
1213
+ if (transition.cascade) {
1214
+ rtn = views$state$machine$$applyPreOrder(this, "_" + transition.name, arguments);
1215
+ } else {
1216
+ rtn = views$state$machine$$apply(this, "_" + transition.name, arguments);
1217
+ }
1218
+
1219
+ views$state$machine$$applyPreOrder(this, views$state$machine$$setState, [transition.next]);
1220
+
1221
+ if (transition.after) {
1222
+ views$state$machine$$applyPreOrder(this, transition.after);
1223
+ }
1224
+
1225
+ return rtn;
1226
+ }
1227
+ }
1228
+
1229
+ var views$state$machine$$default = function(states, klass) {
1230
+ if (states.length === 0) {
1231
+ throw "Need at least one state";
1232
+ }
1233
+
1234
+ var rootState = views$state$machine$$State(states[0]);
1235
+
1236
+ var protoProps = {};
1237
+
1238
+ protoProps.init = function() {
1239
+ this._super.apply(this, arguments);
1240
+ this.state = rootState.name;
1241
+ }
1242
+
1243
+ var state, transition;
1244
+
1245
+ for (var i=0; i<states.length; i++) {
1246
+ state = views$state$machine$$State(states[i]);
1247
+
1248
+ for (var j=0; j<state.transitions.length; j++) {
1249
+ transition = views$state$machine$$Transition(state.transitions[j]);
1250
+ protoProps[transition.name] = views$state$machine$$makeTransition(state, transition);
1251
+ }
1252
+ }
1253
+
1254
+ return klass.extend(protoProps);
1255
+ };
1256
+
1257
+ function views$view$$NOOP() {}
1258
+
1259
+ function views$view$$NULL(value) {
1260
+ if (typeof value === "undefined") {
1261
+ return null;
1262
+ } else {
1263
+ return value;
1264
+ }
1265
+ }
1266
+
1267
+ var views$view$$default = views$state$machine$$default(views$states$$default, utils$object$$default.extend({
1268
+
1269
+ init: function(element, options, data) {
1270
+ this.element = element;
1271
+ this.options = options;
1272
+
1273
+ if (views$view$$NULL(data) === null) {
1274
+ this.data = this.serialize();
1275
+ } else {
1276
+ this.data = data;
1277
+ this.deserialize();
1278
+ }
1279
+ },
1280
+
1281
+ /**
1282
+ * The element managed by this view.
1283
+ *
1284
+ * @property element
1285
+ * @type Element
1286
+ * @required
1287
+ */
1288
+ element: utils$object$$REQUIRED,
1289
+
1290
+ /**
1291
+ * @private
1292
+ *
1293
+ * @property parentView
1294
+ * @type View
1295
+ * @default null
1296
+ */
1297
+ parentView: null,
1298
+
1299
+ /**
1300
+ * @private
1301
+ *
1302
+ * A list of child views managed by this view.
1303
+ *
1304
+ * @property childViews
1305
+ * @type View[]
1306
+ * @default []
1307
+ */
1308
+ childViews: utils$object$$LAZY(Array),
1309
+
1310
+ /**
1311
+ * @private
1312
+ *
1313
+ * Add *view* to the list of managed child views.
1314
+ *
1315
+ * @method addChildView
1316
+ * @param {View} [view] The child view to add.
1317
+ */
1318
+ addChildView: function(view) {
1319
+ this.childViews.push(view);
1320
+ view.parentView = this;
1321
+ },
1322
+
1323
+ /**
1324
+ * @private
1325
+ *
1326
+ * Remove *view* from the list of managed child views.
1327
+ *
1328
+ * @method removeChildCiew
1329
+ * @param {View} [view] The child view to remove.
1330
+ */
1331
+ removeChildView: function(view) {
1332
+ var idx = this.childViews.indexOf(view);
1333
+
1334
+ if (idx < 0) {
1335
+ throw view.toString() + " is not a child view of " + this.toString();
1336
+ }
1337
+
1338
+ this.childViews.splice(idx, 1);
1339
+ view.parentView = null;
1340
+ },
1341
+
1342
+ /**
1343
+ * @private
1344
+ *
1345
+ * Insert the view's `element` into the given *parent* element before the
1346
+ * *reference* element. If no reference element is given, the element will be
1347
+ * inserted at the end of the parent.
1348
+ *
1349
+ * @method insertElement
1350
+ * @param {Element} [parent] The parent element.
1351
+ * @param {Element} [reference] The reference element.
1352
+ */
1353
+ _insertElement: function(parent, reference) {
1354
+ parent.insertBefore(this.element, reference || null);
1355
+ },
1356
+
1357
+ /**
1358
+ * @private
1359
+ *
1360
+ * Temporarily detach the element from its parent.
1361
+ *
1362
+ * @method detachElement
1363
+ */
1364
+ _detachElement: function() {
1365
+ this.element.parentNode.removeChild(this.element);
1366
+ },
1367
+
1368
+ /**
1369
+ * @private
1370
+ *
1371
+ * Reattach the view's `element` into the given *parent* element before the
1372
+ * *reference* element. If no reference element is given, the element will be
1373
+ * inserted at the end of the parent.
1374
+ *
1375
+ * @method reattachElement
1376
+ * @param {Element} [parent] The parent element.
1377
+ * @param {Element} [reference] The reference element.
1378
+ */
1379
+ _reattachElement: function(parent, reference) {
1380
+ this._insertElement(parent, reference);
1381
+ },
1382
+
1383
+ /**
1384
+ * @private
1385
+ *
1386
+ * Destroy a view's element.
1387
+ *
1388
+ * @method destroyElement
1389
+ */
1390
+ _destroyElement: function() {
1391
+ this._detachElement();
1392
+ this.element = null;
1393
+ },
1394
+
1395
+ /**
1396
+ * A hook that is called before the view's element has been inserted into the
1397
+ * DOM.
1398
+ *
1399
+ * @method willInsertElement
1400
+ */
1401
+ willInsertElement: views$view$$NOOP,
1402
+
1403
+ /**
1404
+ * A hook that will be called after the view's element has been inserted into
1405
+ * the DOM.
1406
+ *
1407
+ * @method didInsertElement
1408
+ */
1409
+ didInsertElement: views$view$$NOOP,
1410
+
1411
+ /**
1412
+ * A hook that is called before the view's element is temporarily removed from
1413
+ * the DOM (e.g. before it is moved).
1414
+ *
1415
+ * The key difference between this hook and `willDestroyElement` is that the
1416
+ * removal here is temporary and the same element will be re-inserted into the
1417
+ * DOM at a later time. Because of that, you typically do not have to remove
1418
+ * event listeners on the element itself, but if you have setup any listeners
1419
+ * on the parent view/element, you should remove them here.
1420
+ *
1421
+ * @method willDetachElement
1422
+ */
1423
+ willDetachElement: views$view$$NOOP,
1424
+
1425
+ /**
1426
+ * A hook that is called before a view's (previously detached) element is re-
1427
+ * inserted into the DOM (e.g. after it has been moved).
1428
+ *
1429
+ * @method willReattachElement
1430
+ */
1431
+ willReattachElement: views$view$$NOOP,
1432
+
1433
+ /**
1434
+ * A hook that is called after a view's (previously detached) element is re-
1435
+ * inserted into the DOM (e.g. after it has been moved).
1436
+ *
1437
+ * If you removed any listeners in `willDetachElement`, this is the chance to
1438
+ * set them up again.
1439
+ *
1440
+ * @method didReattachElement
1441
+ */
1442
+ didReattachElement: views$view$$NOOP,
1443
+
1444
+ /**
1445
+ * A hook that is called before the view's element is permanently removed from
1446
+ * the DOM.
1447
+ *
1448
+ * You should remove any listeners that you have set up on the element in
1449
+ * `didInsertElement`, otherwise it might cause a memory leak.
1450
+ *
1451
+ * @method willDestroyElement
1452
+ */
1453
+ willDestroyElement: views$view$$NOOP,
1454
+
1455
+ deserialize: function() {
1456
+ if (this.options && this.options.serializer) {
1457
+ this.options.serializer.deserialize(this.element, this.options, this.data);
1458
+ }
1459
+ },
1460
+
1461
+ serialize: function() {
1462
+ if (this.options && this.options.serializer) {
1463
+ return this.options.serializer.serialize(this.element, this.options);
1464
+ }
1465
+ },
1466
+
1467
+ serializableChildren: function() {
1468
+ return this.childViews;
1469
+ },
1470
+
1471
+ toJSON: function() {
1472
+ var json = {};
1473
+
1474
+ if (typeof this.options.role === "string") {
1475
+ json.role = this.options.role;
1476
+ }
1477
+
1478
+ if (typeof this.options.type === "string") {
1479
+ json.type = this.options.type;
1480
+ }
1481
+
1482
+ if (typeof this.options.name === "string") {
1483
+ json.name = this.options.name;
1484
+ }
1485
+
1486
+ json.data = views$view$$NULL(this.serialize());
1487
+
1488
+ json.children = [];
1489
+
1490
+ this.serializableChildren().forEach(function(child) {
1491
+ json.children.push(child.toJSON());
1492
+ });
1493
+
1494
+ return json;
1495
+ }
1496
+
1497
+ }));
1498
+
1499
+ function views$container$view$$assertIdx(idx, length, insert) {
1500
+ var max = insert ? length + 1 : length;
1501
+
1502
+ if (idx < 0 || idx >= max) {
1503
+ throw "Index out of range: " + idx;
1504
+ }
1505
+ }
1506
+
1507
+ var views$container$view$$default = views$view$$default.extend({
1508
+
1509
+ init: function(element, options, data) {
1510
+ this._super(element, options, data);
1511
+ this.containerElement = this.element;
1512
+ },
1513
+
1514
+ insertChildViewAt: function(view, idx) {
1515
+ idx = idx || 0;
1516
+
1517
+ views$container$view$$assertIdx(idx, this.childViews.length, true);
1518
+
1519
+ this.childViews.splice(idx, 0, view);
1520
+ view.parentView = this;
1521
+
1522
+ var referenceElement = this.containerElement.children[idx];
1523
+
1524
+ if (this.state === "inDOM") {
1525
+ view.insertElement(this.containerElement, referenceElement);
1526
+ } else {
1527
+ this.containerElement.insertBefore(view.element, referenceElement || null);
1528
+ }
1529
+ },
1530
+
1531
+ appendChildView: function(view) {
1532
+ this.insertChildViewAt(view, this.childViews.length);
1533
+ },
1534
+
1535
+ moveChildView: function(fromIdx, toIdx) {
1536
+ views$container$view$$assertIdx(fromIdx, this.childViews.length);
1537
+ views$container$view$$assertIdx(toIdx, this.childViews.length);
1538
+
1539
+ if (fromIdx === toIdx) {
1540
+ return;
1541
+ }
1542
+
1543
+ var view = this.childViews.splice(fromIdx, 1)[0];
1544
+ view.detachElement();
1545
+
1546
+ this.childViews.splice(toIdx, 0, view);
1547
+ view.reattachElement(this.containerElement, this.containerElement.children[toIdx]);
1548
+ },
1549
+
1550
+ removeChildViewAt: function(idx) {
1551
+ views$container$view$$assertIdx(idx, this.childViews.length);
1552
+ this.childViews.splice(idx, 1)[0].destroyElement();
1553
+ }
1554
+
1555
+ });
1556
+
1557
+ function builder$builder$$ownKeys(object) {
1558
+ var keys = [];
1559
+
1560
+ for (var key in object) {
1561
+ if (object.hasOwnProperty(key)) {
1562
+ keys.push(key);
1563
+ }
1564
+ }
1565
+
1566
+ return keys;
1567
+ }
1568
+
1569
+ function builder$builder$$buildView(element, options, data, lookupPaths, parentView) {
1570
+ if (!lookupPaths.shift) {
1571
+ lookupPaths = [lookupPaths];
1572
+ }
1573
+
1574
+ lookupPaths = lookupPaths.slice();
1575
+
1576
+ var ViewClass, path;
1577
+
1578
+ while (!ViewClass && lookupPaths.length) {
1579
+ var path = lookupPaths.shift();
1580
+
1581
+ if (typeof path === "function") {
1582
+ ViewClass = path;
1583
+ } else {
1584
+ ViewClass = core$$default.registry.lookup(path);
1585
+ }
1586
+ }
1587
+
1588
+ ViewClass = ViewClass || views$view$$default;
1589
+
1590
+ var placeholder;
1591
+
1592
+ // If element already has a parent, temporarily remove it so that the view
1593
+ // has a chance to wrap the element at `init`
1594
+ if (element.parentNode) {
1595
+ placeholder = element.cloneNode(false);
1596
+ element.parentNode.replaceChild(placeholder, element);
1597
+ }
1598
+
1599
+ var view = new ViewClass(element, options, data);
1600
+
1601
+ if (placeholder) {
1602
+ placeholder.parentNode.replaceChild(view.element, placeholder);
1603
+ }
1604
+
1605
+ if (parentView) {
1606
+ parentView.addChildView(view);
1607
+ }
1608
+
1609
+ return view;
1610
+ }
1611
+
1612
+ function builder$builder$$buildBlock(document, template, type, data, env, headless) {
1613
+ var factory = template.blocks[type];
1614
+
1615
+ if (typeof factory !== "function") {
1616
+ throw "The template does not define the block " + type;
1617
+ }
1618
+
1619
+ var block = template$block$$default( factory.call(template, new dom$dom$builder$$default(document), env) );
1620
+
1621
+ var view = builder$builder$$buildView(
1622
+ block.element,
1623
+ {
1624
+ role: "block",
1625
+ type: type
1626
+ },
1627
+ data && data.data,
1628
+ headless ? [] : ["view:block/" + type, "view:block"]
1629
+ );
1630
+
1631
+ builder$builder$$initializeEditors(block.editables, view, data, headless);
1632
+
1633
+ return view;
1634
+ }
1635
+
1636
+ function builder$builder$$findData(data, options) {
1637
+ if (data && data.children.length) {
1638
+ var entry;
1639
+
1640
+ for (var i=0; i<data.children.length; i++) {
1641
+ entry = data.children[i];
1642
+
1643
+ if (typeof options.role === "string" && entry.role != options.role) {
1644
+ continue;
1645
+ }
1646
+
1647
+ if (typeof options.type === "string" && entry.type != options.type) {
1648
+ continue;
1649
+ }
1650
+
1651
+ if (typeof options.name === "string" && entry.name != options.name) {
1652
+ continue;
1653
+ }
1654
+
1655
+ return entry;
1656
+ }
1657
+ }
1658
+ }
1659
+
1660
+ function builder$builder$$initializeEditors(editables, parentView, data, headless) {
1661
+ editables.forEach(function(editable) {
1662
+ var editable = template$editable$$default(editable);
1663
+
1664
+ var serializer = core$$default.registry.lookup("serializer:" + editable.type);
1665
+
1666
+ if (!serializer) {
1667
+ core$$default.logger.error("No serializer were registered to handle " + editable.type);
1668
+ return;
1669
+ }
1670
+
1671
+ var options = {};
1672
+
1673
+ for (var key in editable.options) {
1674
+ if (editable.options.hasOwnProperty(key)) {
1675
+ options[key] = editable.options[key];
1676
+ }
1677
+ }
1678
+
1679
+ options.role = "editable";
1680
+ options.type = editable.type;
1681
+ options.name = editable.name;
1682
+ options.serializer = serializer;
1683
+
1684
+ var editorData = builder$builder$$findData(data, options);
1685
+
1686
+ builder$builder$$buildView(editable.element, options, editorData && editorData.data, headless ? [] : ["view:editor/" + editable.type], parentView);
1687
+ });
1688
+ }
1689
+
1690
+ function builder$builder$$initializeBlocks(builder, data) {
1691
+ if (data && data.children.length) {
1692
+ var entry;
1693
+
1694
+ for (var i=0; i<data.children.length; i++) {
1695
+ entry = data.children[i];
1696
+
1697
+ if (entry.role === "block") {
1698
+ builder.appendBlock(entry.type, entry);
1699
+ }
1700
+ }
1701
+ }
1702
+ }
1703
+
1704
+ /**
1705
+ * @class Builder
1706
+ * @namespace Bob
1707
+ * @param {String} [templateKey] The lookup key of the template to build from.
1708
+ * @param {Document} [document] The `Document` to build on. If not provided, a
1709
+ * fresh document will be created.
1710
+ */
1711
+ var builder$builder$$Builder = utils$object$$default.extend({
1712
+
1713
+ init: function(template, window, data, env) {
1714
+ var templateKey;
1715
+
1716
+ if (typeof template === "string") {
1717
+ templateKey = template;
1718
+ template = core$$default.registry.lookup("template:" + templateKey);
1719
+ }
1720
+
1721
+ if (!template) {
1722
+ throw "Template not found: " + templateKey;
1723
+ }
1724
+
1725
+ if (window) {
1726
+ this.headless = false;
1727
+ this.contentWindow = window;
1728
+ this.contentDocument = dom$document$$prepareDocument(window.document);
1729
+ } else {
1730
+ this.headless = true;
1731
+ this.contentWindow = null;
1732
+ this.contentDocument = dom$document$$createDocument();
1733
+ }
1734
+
1735
+ if (env instanceof utils$hash$lookup$$default) {
1736
+ this.env = env;
1737
+ } else {
1738
+ this.env = new utils$hash$lookup$$default(env);
1739
+ }
1740
+
1741
+ /**
1742
+ * The block types supported by the template.
1743
+ *
1744
+ * @property supportedBlockTypes
1745
+ * @type String[]
1746
+ */
1747
+ this.supportedBlockTypes = builder$builder$$ownKeys(template.blocks);
1748
+
1749
+ var doc = this.contentDocument;
1750
+
1751
+ var layout = template$layout$$default( template.layout( new dom$dom$builder$$default(doc), this.env ) );
1752
+
1753
+ var layoutData = builder$builder$$findData(data, { role: "layout" });
1754
+
1755
+ var layoutView = builder$builder$$buildView(
1756
+ layout.element,
1757
+ { role: "layout" },
1758
+ layoutData && layout.data,
1759
+ this.headless ? [] : ["view:layout"]
1760
+ );
1761
+
1762
+ builder$builder$$initializeEditors(layout.editables, layoutView, layoutData, this.headless);
1763
+
1764
+ var container = template$container$$default( layout.container );
1765
+
1766
+ var containerData = builder$builder$$findData(layoutData, { role: "container" });
1767
+
1768
+ var containerView = builder$builder$$buildView(
1769
+ container.element,
1770
+ { role: "container" },
1771
+ containerData && containerData.data,
1772
+ this.headless ? views$container$view$$default : ["view:container", views$container$view$$default],
1773
+ layoutView
1774
+ );
1775
+
1776
+ this.template = template;
1777
+ this.layoutView = layoutView;
1778
+ this.containerView = containerView;
1779
+
1780
+ builder$builder$$initializeBlocks(this, containerData);
1781
+
1782
+ layoutView.insertElement(doc);
1783
+ },
1784
+
1785
+ blocks: utils$object$$LAZY(Array),
1786
+
1787
+ /**
1788
+ * Insert a new block of *type* to the main container at the specific index.
1789
+ * If the given index is out-of-range an error will be thrown.
1790
+ *
1791
+ * @method insertBlock
1792
+ * @param {String} [type] The block type.
1793
+ * @param {Integer} [idx=0] The index to insert at.
1794
+ */
1795
+ insertBlock: function(type, idx, data) {
1796
+ var block = builder$builder$$buildBlock(this.contentDocument, this.template, type, data, this.env, this.headless);
1797
+ this.containerView.insertChildViewAt(block, idx);
1798
+ },
1799
+
1800
+ /**
1801
+ * Append a new block of *type* to the end of the main container.
1802
+ *
1803
+ * @method appendBlock
1804
+ * @param {String} [type] The block type.
1805
+ */
1806
+ appendBlock: function(type, data) {
1807
+ var block = builder$builder$$buildBlock(this.contentDocument, this.template, type, data, this.env, this.headless);
1808
+ this.containerView.appendChildView(block);
1809
+ },
1810
+
1811
+ /**
1812
+ * Move an existing block within the main container. If either of the indices
1813
+ * are out-of-range, an error will be thrown.
1814
+ *
1815
+ * @method moveBlock
1816
+ * @param {Integer} [fromIdx] The current index of the block.
1817
+ * @param {Integer} [toIdx] The new index of the block.
1818
+ */
1819
+ moveBlock: function(fromIdx, toIdx) {
1820
+ this.containerView.moveChildView(fromIdx, toIdx);
1821
+ },
1822
+
1823
+ /**
1824
+ * Remove an existing block from the main container. Any views and
1825
+ * editors associated with the block will be deactivated and then destroyed.
1826
+ * If the given index is out-of-range an error will be thrown.
1827
+ *
1828
+ * @method removeBlock
1829
+ * @param {Integer} [idx] The index of the block.
1830
+ */
1831
+ removeBlock: function(idx) {
1832
+ this.containerView.removeChildViewAt(idx);
1833
+ },
1834
+
1835
+ /**
1836
+ * Teardown the builder.
1837
+ *
1838
+ * @method destroy
1839
+ */
1840
+ destroy: function() {
1841
+ this.layoutView.destroyElement();
1842
+
1843
+ dom$document$$prepareDocument(this.contentDocument);
1844
+
1845
+ this.contentWindow = null;
1846
+ this.contentDocument = null;
1847
+ this.layoutView = null;
1848
+ this.containerView = null;
1849
+ },
1850
+
1851
+ /**
1852
+ * Refresh the builder with an updated env hash.
1853
+ *
1854
+ * @method refresh
1855
+ * @params {Object} [env] The updated env hash.
1856
+ */
1857
+ refresh: function(env) {
1858
+ var template = this.template,
1859
+ window = this.contentWindow,
1860
+ data = this.toJSON(),
1861
+ env = env || this.env;
1862
+
1863
+ this.destroy();
1864
+
1865
+ this.init(template, window, data, env);
1866
+ },
1867
+
1868
+ toJSON: function() {
1869
+ return {
1870
+ data: { version: 0 },
1871
+ children: [
1872
+ this.layoutView.toJSON()
1873
+ ]
1874
+ };
1875
+ },
1876
+
1877
+ toHTML: function() {
1878
+ var headless = new builder$builder$$Builder(this.template, null, this.toJSON(), this.env);
1879
+ var root = headless.contentDocument.documentElement;
1880
+
1881
+ if (this.template.doctype) {
1882
+ return this.template.doctype + "\n" + dom$serialize$$default(root);
1883
+ } else {
1884
+ return dom$serialize$$default(root);
1885
+ }
1886
+ }
1887
+
1888
+ });
1889
+
1890
+ var builder$builder$$default = builder$builder$$Builder;
1891
+
1892
+ /**
1893
+ * Describes a template.
1894
+ *
1895
+ * @class Template
1896
+ * @namespace Interface
1897
+ */
1898
+ var template$template$$Template = utils$interface$$default("Template", {
1899
+
1900
+ /**
1901
+ * The doctype of the template.
1902
+ *
1903
+ * @property doctype
1904
+ * @type String
1905
+ * @default null
1906
+ */
1907
+ doctype: null,
1908
+
1909
+ /**
1910
+ * A display name for the template.
1911
+ *
1912
+ * @property name
1913
+ * @type String
1914
+ * @required
1915
+ */
1916
+ name: utils$object$$REQUIRED,
1917
+
1918
+ /**
1919
+ * A factory function to build the template's layout.
1920
+ *
1921
+ * @method layout
1922
+ * @param {Document} [document] The `document` object to use.
1923
+ * @return {Layout} The layout that was built.
1924
+ * @required
1925
+ */
1926
+ layout: utils$object$$REQUIRED,
1927
+
1928
+ /**
1929
+ * An object containing the blocks supported by the template keyed by their
1930
+ * types. The functions should take a `Document` object as argument and return
1931
+ * a `Block`.
1932
+ *
1933
+ * @property blocks
1934
+ * @type Object
1935
+ * @default {}
1936
+ */
1937
+ blocks: utils$object$$LAZY(Object)
1938
+
1939
+ });
1940
+
1941
+ var template$template$$default = template$template$$Template;
1942
+ function template$template$$registerTemplate(key, template) {
1943
+ key = "template:" + key;
1944
+ template = template$template$$Template(template);
1945
+
1946
+ core$$default.register(key, template);
1947
+ }
1948
+
1949
+ /**
1950
+ * Exports all public classes and functions.
1951
+ */
1952
+
1953
+ function bob$$exportAs(name, value) {
1954
+ core$$default[name] = value;
1955
+
1956
+ if(value.__class__) {
1957
+ value.__name__ = "Bob." + name;
1958
+ }
1959
+ }
1960
+
1961
+ bob$$exportAs("Logger", utils$logger$$default);
1962
+
1963
+ bob$$exportAs("Object", utils$object$$default);
1964
+ bob$$exportAs("REQUIRED", utils$object$$REQUIRED);
1965
+ bob$$exportAs("LAZY", utils$object$$LAZY);
1966
+
1967
+ bob$$exportAs("Builder", builder$builder$$default);
1968
+
1969
+ bob$$exportAs("registerTemplate", template$template$$registerTemplate);
1970
+
1971
+ bob$$exportAs("View", views$view$$default);
1972
+
1973
+ bob$$exportAs("ContainerView", views$container$view$$default);
1974
+
1975
+ bob$$exportAs("DomBuilder", dom$dom$builder$$default);
1976
+
1977
+ bob$$exportAs("HashLookup", utils$hash$lookup$$default);
1978
+
1979
+ // Global export
1980
+ this.Bob = core$$default;
1981
+
1982
+ var bob$$default = core$$default;
1983
+
1984
+ var utils$required$$default = {
1985
+ toString: function() {
1986
+ return "(Required Property)";
1987
+ }
1988
+ };
1989
+ }).call(this);