bob-rails 0.0.1

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