1 | var log = require("phnq_log").create(__filename);
|
2 | var phnq_core = require("phnq_core");
|
3 | var fs = require("fs");
|
4 | var cdn = require("./cdn");
|
5 | var _path = require("path");
|
6 | var Widget = require("./widget");
|
7 | var _ = require("underscore");
|
8 | var config = require("./config");
|
9 | var aggregator = require("./aggregator");
|
10 |
|
11 | var instance = null;
|
12 |
|
13 | module.exports =
|
14 | {
|
15 | appRoot: _path.dirname(process.argv[1]),
|
16 |
|
17 | instance: function()
|
18 | {
|
19 | return instance || (instance = new WidgetManager());
|
20 | }
|
21 | };
|
22 |
|
23 | var WidgetManager = phnq_core.clazz(
|
24 | {
|
25 | init: function()
|
26 | {
|
27 | this.scanPaths = [];
|
28 | this.widgets = null;
|
29 | this.lastScanMillis = 0;
|
30 | this.watched = {};
|
31 | this.lessKeys = null;
|
32 | },
|
33 |
|
34 | addScanPath: function(path)
|
35 | {
|
36 | path = _path.join(module.exports.appRoot, path);
|
37 | try
|
38 | {
|
39 | fs.statSync(path);
|
40 | log.debug("Added widget scan path: ", path);
|
41 | this.scanPaths.push(path);
|
42 | }
|
43 | catch(ex)
|
44 | {
|
45 | log.debug("Error adding widget scan path: ", ex.toString());
|
46 | }
|
47 | },
|
48 |
|
49 | getAllTypes: function()
|
50 | {
|
51 | return _.keys(this.widgets);
|
52 | },
|
53 |
|
54 | getWidget: function(type)
|
55 | {
|
56 | this.scan();
|
57 | return this.widgets[type];
|
58 | },
|
59 |
|
60 | watch: function(path)
|
61 | {
|
62 | if(!this.watched[path])
|
63 | {
|
64 | var _this = this;
|
65 | this.watched[path] = true;
|
66 | fs.watch(path, {persistent:false}, function()
|
67 | {
|
68 | _this.widgets = null;
|
69 | });
|
70 | }
|
71 | },
|
72 |
|
73 | getTestCode: function(baseUrl)
|
74 | {
|
75 | var buf = [];
|
76 | this.scan();
|
77 | _.each(this.widgets, function(widget)
|
78 | {
|
79 | buf.push(widget.getTestCode(baseUrl));
|
80 | });
|
81 | return buf.join("");
|
82 | },
|
83 |
|
84 | sortDependencies: function(types)
|
85 | {
|
86 | var _this = this;
|
87 |
|
88 | types.sort(function(t1, t2)
|
89 | {
|
90 | var w1 = _this.getWidget(t1);
|
91 | var w2 = _this.getWidget(t2);
|
92 | if(!w1 || !w2)
|
93 | {
|
94 | return 0;
|
95 | }
|
96 | var d1on2 = w1.dependsOnType(w2.type);
|
97 | var d2on1 = w2.dependsOnType(w1.type);
|
98 |
|
99 | if(d1on2 && d2on1)
|
100 | {
|
101 | var w1Deps = w1.getDependencies();
|
102 | var w2Deps = w2.getDependencies();
|
103 | log.debug("Circular dependency detected: ", w1.type, w1Deps, w2.type, w2Deps);
|
104 | return 0;
|
105 | }
|
106 |
|
107 | if(d1on2)
|
108 | {
|
109 | return 1;
|
110 | }
|
111 | else if(d2on1)
|
112 | {
|
113 | return -1;
|
114 | }
|
115 | else
|
116 | {
|
117 | return 0;
|
118 | }
|
119 | });
|
120 |
|
121 | return types;
|
122 | },
|
123 |
|
124 | scan: function()
|
125 | {
|
126 | if(this.widgets)
|
127 | return;
|
128 |
|
129 | var nowMillis = new Date().getTime();
|
130 |
|
131 | if((nowMillis - this.lastScanMillis) < 2000)
|
132 | return;
|
133 |
|
134 | log.info("Scanning for widgets...");
|
135 |
|
136 | this.lastScanMillis = nowMillis;
|
137 |
|
138 | if(this.scanPaths.length == 0)
|
139 | this.addScanPath("widgets");
|
140 |
|
141 | var _this = this;
|
142 |
|
143 | this.widgets = {};
|
144 | this.lessKeys = [];
|
145 | aggregator.clear();
|
146 | cdn.needsSync = true;
|
147 |
|
148 | var paths = this.scanPaths.slice(0).reverse();
|
149 | paths.push(_path.join(__dirname, "../widgets"));
|
150 | paths = _.uniq(paths);
|
151 |
|
152 | _.each(_.uniq(paths), function(path)
|
153 | {
|
154 | _this.addWidgetsAtPath(path, path);
|
155 | });
|
156 |
|
157 | _.each(this.widgets, function(widget, type)
|
158 | {
|
159 | var script = widget.getScript();
|
160 | if(script)
|
161 | aggregator.registerString("widget_"+type+"_script", script);
|
162 |
|
163 | var style = widget.getStyle();
|
164 | if(style)
|
165 | aggregator.registerString("widget_"+type+"_style", style);
|
166 | });
|
167 |
|
168 | var buf = [];
|
169 | buf.push(".wph {visibility: hidden; width: 1px}\n");
|
170 | buf.push(".loadError { padding: 5px; margin: 5px; background: #c00; color: #fff; }\n");
|
171 | aggregator.registerString("widgetshell_head_style", buf.join(""));
|
172 |
|
173 | aggregator.registerString("client_boot", aggregator.getClientBoot(true));
|
174 | },
|
175 |
|
176 | addWidgetsAtPath: function(path, basePath)
|
177 | {
|
178 | var _this = this;
|
179 |
|
180 | _this.watch(path);
|
181 |
|
182 | var names = fs.readdirSync(path);
|
183 |
|
184 | _.each(names, function(name)
|
185 | {
|
186 | var f = _path.join(path, name);
|
187 | var stat = fs.statSync(f);
|
188 |
|
189 | if(stat && stat.isDirectory())
|
190 | {
|
191 | if(name != "_i18n" && name != "_static")
|
192 | {
|
193 | _this.addWidgetsAtPath(f, basePath);
|
194 | }
|
195 | }
|
196 | else
|
197 | {
|
198 | if(name.match(/.*\.less$/))
|
199 | {
|
200 | var lessKey = "less_"+_path.relative(basePath, f);
|
201 | aggregator.registerString(lessKey, fs.readFileSync(f, "UTF-8"));
|
202 | _this.lessKeys.push(lessKey);
|
203 | }
|
204 | else
|
205 | {
|
206 | var m = /[^\.]*\.(ejs|js|css)$/.exec(name.replace(/\.html$/, ".html.ejs"));
|
207 | if(m)
|
208 | {
|
209 | var type = _path.basename(_path.dirname(f));
|
210 | if(!type.match(/\./))
|
211 | type = _path.relative(basePath, _path.dirname(f)).split("/").join(".");
|
212 |
|
213 | _this.watch(f);
|
214 |
|
215 | var filename = _path.basename(f);
|
216 |
|
217 | var ext = m[1];
|
218 | var widget = _this.widgets[type] || (_this.widgets[type] = new Widget(type, _path.dirname(f)));
|
219 | var partialMatch = /^_([^.]*).html(\.ejs)?/.exec(filename);
|
220 | var handlerMatch = /^_([^.]*)\.js/.exec(filename);
|
221 | var testMatch = /^([^.]*)\.test\.js/.exec(filename);
|
222 | if(partialMatch)
|
223 | {
|
224 | widget.partials[partialMatch[1]] = f;
|
225 | }
|
226 | else if(handlerMatch)
|
227 | {
|
228 | widget.remoteHandlerFile = f;
|
229 | }
|
230 | else if(testMatch)
|
231 | {
|
232 | widget.tests[testMatch[1]] = f;
|
233 | }
|
234 | else
|
235 | {
|
236 | widget[ext+"File"] = f;
|
237 |
|
238 | widget[ext+"Files"] = widget[ext+"Files"] || [];
|
239 | widget[ext+"Files"].push(f);
|
240 | }
|
241 | }
|
242 | }
|
243 | }
|
244 | });
|
245 | }
|
246 | });
|