1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | var util = require('racer/lib/util');
|
11 | var derbyTemplates = require('derby-templates');
|
12 | var templates = derbyTemplates.templates;
|
13 | var expressions = derbyTemplates.expressions;
|
14 | var Controller = require('./Controller');
|
15 |
|
16 | exports.Component = Component;
|
17 | exports.ComponentAttribute = ComponentAttribute;
|
18 | exports.ComponentAttributeBinding = ComponentAttributeBinding;
|
19 | exports.ComponentFactory = ComponentFactory;
|
20 | exports.SingletonComponentFactory = SingletonComponentFactory;
|
21 | exports.createFactory = createFactory;
|
22 | exports.extendComponent = extendComponent;
|
23 |
|
24 | function Component(parent, context, id, scope) {
|
25 | this.parent = parent;
|
26 | this.context = context;
|
27 | this.id = id;
|
28 | this._scope = scope;
|
29 | }
|
30 |
|
31 | util.mergeInto(Component.prototype, Controller.prototype);
|
32 |
|
33 | Component.prototype.destroy = function() {
|
34 | this.emit('destroy');
|
35 | this.model.removeContextListeners();
|
36 | this.model.destroy();
|
37 | delete this.page._components[this.id];
|
38 | var components = this.page._eventModel.object.$components;
|
39 | if (components) delete components.object[this.id];
|
40 | };
|
41 |
|
42 | Component.prototype.get = function(viewName, unescaped) {
|
43 | var view = this.getView(viewName);
|
44 | return view.get(this.context, unescaped);
|
45 | };
|
46 |
|
47 | Component.prototype.getFragment = function(viewName) {
|
48 | var view = this.getView(viewName);
|
49 | return view.getFragment(this.context);
|
50 | };
|
51 |
|
52 | Component.prototype.getView = function(viewName) {
|
53 | var contextView = this.context.getView();
|
54 | return (viewName) ?
|
55 | this.app.views.find(viewName, contextView.namespace) : contextView;
|
56 | };
|
57 |
|
58 | Component.prototype.getAttribute = function(key) {
|
59 | var attributeContext = this.context.forAttribute(key);
|
60 | if (!attributeContext) return;
|
61 | var value = attributeContext.attributes[key];
|
62 | if (value instanceof expressions.Expression) {
|
63 | return value.get(attributeContext);
|
64 | }
|
65 | return value && expressions.renderValue(value, attributeContext);
|
66 | };
|
67 |
|
68 | Component.prototype.setAttribute = function(key, value) {
|
69 | this.context.parent.attributes[key] = value;
|
70 | };
|
71 |
|
72 | Component.prototype.setNullAttribute = function(key, value) {
|
73 | var attributes = this.context.parent.attributes;
|
74 | if (attributes[key] == null) attributes[key] = value;
|
75 | };
|
76 |
|
77 | function ComponentAttribute(expression, model, key) {
|
78 | this.expression = expression;
|
79 | this.model = model;
|
80 | this.key = key;
|
81 | }
|
82 | ComponentAttribute.prototype.update = function(context, binding) {
|
83 | var value = this.expression.get(context);
|
84 | binding.condition = value;
|
85 | this.model.setDiff(this.key, value);
|
86 | };
|
87 | function ComponentAttributeBinding(expression, model, key, context) {
|
88 | this.template = new ComponentAttribute(expression, model, key);
|
89 | this.context = context;
|
90 | this.condition = expression.get(context);
|
91 | }
|
92 | ComponentAttributeBinding.prototype = Object.create(templates.Binding.prototype);
|
93 | ComponentAttributeBinding.prototype.constructor = ComponentAttributeBinding;
|
94 |
|
95 | function setModelAttributes(context, model) {
|
96 | var attributes = context.parent.attributes;
|
97 | if (!attributes) return;
|
98 |
|
99 | for (var key in attributes) {
|
100 | var value = attributes[key];
|
101 | setModelAttribute(context, model, key, value);
|
102 | }
|
103 | }
|
104 |
|
105 | function setModelAttribute(context, model, key, value) {
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | if (value instanceof expressions.Expression) {
|
112 | var segments = value.pathSegments(context);
|
113 | if (segments) {
|
114 | model.root.ref(model._at + '.' + key, segments.join('.'), {updateIndices: true});
|
115 | } else {
|
116 | var binding = new ComponentAttributeBinding(value, model, key, context);
|
117 | context.addBinding(binding);
|
118 | model.set(key, binding.condition);
|
119 | }
|
120 | return;
|
121 | }
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | if (value instanceof templates.Template) {
|
135 | var template = new templates.ContextClosure(value, context);
|
136 | model.set(key, template);
|
137 | return;
|
138 | }
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | model.set(key, value);
|
144 | }
|
145 |
|
146 | function createFactory(constructor) {
|
147 | return (constructor.prototype.singleton) ?
|
148 | new SingletonComponentFactory(constructor) :
|
149 | new ComponentFactory(constructor);
|
150 | }
|
151 |
|
152 | function emitInitHooks(context, component) {
|
153 | if (!context.initHooks) return;
|
154 |
|
155 | for (var i = 0, len = context.initHooks.length; i < len; i++) {
|
156 | context.initHooks[i].emit(context, component);
|
157 | }
|
158 | }
|
159 |
|
160 | function ComponentFactory(constructor) {
|
161 | this.constructor = constructor;
|
162 | }
|
163 | ComponentFactory.prototype.init = function(context) {
|
164 | var component = new this.constructor();
|
165 |
|
166 | var parent = context.controller;
|
167 | var id = context.id();
|
168 | var scope = ['$components', id];
|
169 | var model = parent.model.root.eventContext(component);
|
170 | model._at = scope.join('.');
|
171 | model.set('id', id);
|
172 |
|
173 |
|
174 | model.data = model.get();
|
175 | parent.page._components[id] = component;
|
176 |
|
177 | var componentContext = context.componentChild(component);
|
178 | Controller.call(component, parent.app, parent.page, model);
|
179 | Component.call(component, parent, componentContext, id, scope);
|
180 | setModelAttributes(componentContext, model);
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | emitInitHooks(context, component);
|
188 | component.emit('init', component);
|
189 | if (component.init) component.init(model);
|
190 |
|
191 | return componentContext;
|
192 | };
|
193 | ComponentFactory.prototype.create = function(context) {
|
194 | var component = context.controller;
|
195 | component.emit('create', component);
|
196 |
|
197 | if (component.create) {
|
198 | component.create(component.model, component.dom);
|
199 | }
|
200 | };
|
201 |
|
202 | function SingletonComponentFactory(constructor) {
|
203 | this.constructor = constructor;
|
204 | this.component = null;
|
205 | }
|
206 | SingletonComponentFactory.prototype.init = function(context) {
|
207 | if (!this.component) this.component = new this.constructor();
|
208 | return context.componentChild(this.component);
|
209 | };
|
210 |
|
211 | SingletonComponentFactory.prototype.create = function() {};
|
212 |
|
213 | function extendComponent(constructor) {
|
214 |
|
215 | if (constructor.prototype instanceof Component) return;
|
216 | // Otherwise, replace its prototype with an instance of Component
|
217 | var oldPrototype = constructor.prototype;
|
218 | constructor.prototype = new Component();
|
219 | util.mergeInto(constructor.prototype, oldPrototype);
|
220 | }
|