##// END OF EJS Templates
repository reorganized
cin -
r0:6a5387d69bf1 default
parent child
Show More
@@ -0,0 +1,17
1 <?xml version="1.0" encoding="UTF-8"?>
2 <projectDescription>
3 <name>core</name>
4 <comment>Project core created by Buildship.</comment>
5 <projects>
6 </projects>
7 <buildSpec>
8 <buildCommand>
9 <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
10 <arguments>
11 </arguments>
12 </buildCommand>
13 </buildSpec>
14 <natures>
15 <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
16 </natures>
17 </projectDescription>
@@ -0,0 +1,2
1 connection.project.dir=..
2 eclipse.preferences.version=1
@@ -0,0 +1,19
1
2 println "version: $version"
3
4 task prepare(type: Copy) {
5 from('src')
6 from('.') {
7 include 'readme.md', 'license', 'history.md', 'package.json'
8 }
9 into(buildDir)
10 }
11
12 task build(dependsOn: prepare) {
13 }
14
15 task pack(dependsOn: build, type: Exec) {
16 workingDir = buildDir
17
18 commandLine 'npm', 'pack'
19 } No newline at end of file
@@ -0,0 +1,12
1 HISTORY
2 =======
3
4 1.0.1
5 -----
6
7 First release, intorduces the followinf features
8
9 - `di` - dependency injection conyainer
10 - `log` - log4 style logging system
11 - `text` - simple and fast text templating and formatting
12 - `Uuid` - uuid generation traits No newline at end of file
1 NO CONTENT: new file 100644
@@ -0,0 +1,18
1 {
2 "name": "@implab/core",
3 "version": "1.0.1",
4 "description": "Dependency injection, logging, simple and fast text template engine",
5 "main": "main.js",
6 "scripts": {
7 "test": "echo \"Error: no test specified\" && exit 1"
8 },
9 "keywords": [
10 "di",
11 "ioc",
12 "logging",
13 "template engine",
14 "dependency injection"
15 ],
16 "author": "Sergey Smirnov",
17 "license": "MIT"
18 }
1 NO CONTENT: new file 100644
@@ -0,0 +1,3
1 define(["dojo/Deferred"], function(Deferred) {
2 return Deferred;
3 }); No newline at end of file
@@ -0,0 +1,232
1 define(
2 [ "./declare" ],
3 function(declare) {
4 function parseURI(uri) {
5 var schema, host, port, path, query, hash, i;
6 if (typeof (uri) == "string") {
7 if ((i = uri.indexOf(":")) >= 0 &&
8 uri.substr(0, i).match(/^\w+$/)) {
9 schema = uri.substr(0, i);
10 uri = uri.substr(i + 1);
11 }
12
13 if (uri.indexOf("//") === 0) {
14 uri = uri.substr(2);
15 if ((i = uri.indexOf("/")) >= 0) {
16 host = uri.substr(0, i);
17 uri = uri.substr(i);
18 } else {
19 host = uri;
20 uri = "";
21 }
22 }
23
24 if ((i = uri.indexOf("?")) >= 0) {
25 path = uri.substr(0, i);
26 uri = uri.substr(i + 1);
27
28 } else {
29 path = uri;
30 uri = "";
31
32 if ((i = path.indexOf("#")) >= 0) {
33 hash = path.substr(i + 1);
34 path = path.substr(0, i);
35 }
36 }
37
38 if ((i = uri.indexOf("#")) >= 0) {
39 query = uri.substr(0, i);
40 hash = uri.substr(i + 1);
41 } else {
42 query = uri;
43 }
44 }
45
46 if (host && (i = host.lastIndexOf(":")) >= 0) {
47 port = host.substr(i + 1);
48 host = host.substr(0, i);
49 }
50
51 return {
52 schema : schema,
53 host : host,
54 port : port,
55 path : path,
56 query : query,
57 hash : hash
58 };
59 }
60
61 function makeURI(options) {
62 var uri = [];
63
64 if (options.schema)
65 uri.push(options.schema, ":");
66 if (options.host)
67 uri.push("//", options.host);
68 if (options.host && options.port)
69 uri.push(":", options.port);
70
71 if (options.path) {
72 if (options.host && options.path[0] != "/")
73 uri.push("/");
74 uri.push(options.path);
75 } else if (options.host) {
76 uri.push("/");
77 }
78
79 if (options.query)
80 uri.push("?", options.query);
81 if (options.hash)
82 uri.push("#", options.hash);
83
84 return uri.join("");
85 }
86
87 function reducePath(parts) {
88 var balance = 0, result = [], isRoot;
89
90 for (var i = 0; i < parts.length; i++) {
91 var part = parts[i];
92 switch (part) {
93 case "..":
94 if (balance > 0) {
95 result.pop();
96 } else {
97 if (isRoot)
98 throw new Error("Unbalanced path: " + parts);
99
100 result.push(part);
101 }
102 balance--;
103 break;
104 case ".":
105 break;
106 case "":
107 if (i === 0) {
108 isRoot = true;
109 result.push(part);
110 }
111 break;
112 default:
113 result.push(part);
114 balance++;
115 break;
116 }
117 }
118
119 return result.join("/");
120 }
121
122 var meta = {
123 schema : null,
124 host : null,
125 port : null,
126 path : null,
127 query : null,
128 hash : null
129 };
130
131 var URI = declare(null, {
132 constructor : function(opts) {
133 if (typeof (opts) == "string")
134 opts = parseURI(opts);
135 for ( var p in meta)
136 if (p in opts)
137 this[p] = opts[p];
138 },
139
140 clone : function() {
141 return new URI(this);
142 },
143
144 combine : function(rel) {
145 var me = this;
146
147 if (typeof (rel) === "string")
148 rel = new URI(rel);
149 else
150 rel = rel.clone();
151
152 // //some.host:123/path?q=a#123
153 if (rel.host)
154 return rel;
155
156 // /abs/path?q=a#123
157 if (rel.path && rel.path[0] == "/") {
158 if (me.host) {
159 rel.schema = me.schema;
160 rel.host = me.host;
161 rel.port = me.port;
162 }
163 return rel;
164 }
165
166 var base = me.clone();
167
168 // rel/path?a=b#cd
169 if (rel.path) {
170 var segments = base.getSegments();
171 segments.pop();
172 segments.push.apply(segments, rel.getSegments());
173
174 base.path = reducePath(segments);
175 }
176
177 // ?q=a#123
178 if (rel.query)
179 base.query = rel.query;
180 if (rel.hash)
181 base.hase = rel.hash;
182
183 return base;
184 },
185
186 optimize : function() {
187 this.path = reducePath(this.getSegments());
188 },
189
190 getSegments : function() {
191 if (typeof (this.path) === "string")
192 return this.path.split("/");
193 else
194 return [];
195 },
196
197 toString : function() {
198 var uri = [], me = this;
199
200 if (me.schema)
201 uri.push(me.schema, ":");
202 if (me.host)
203 uri.push("//", me.host);
204 if (me.host && me.port)
205 uri.push(":", me.port);
206
207 if (me.path) {
208 if (me.host && me.path[0] != "/")
209 uri.push("/");
210 uri.push(me.path);
211 } else if (me.host) {
212 uri.push("/");
213 }
214
215 if (me.query)
216 uri.push("?", me.query);
217 if (me.hash)
218 uri.push("#", me.hash);
219
220 return uri.join("");
221 }
222
223 });
224
225 URI.combine = function(base, rel) {
226 if (typeof (base) === "string")
227 base = new URI(base);
228 return base.combine(rel).toString();
229 };
230
231 return URI;
232 }); No newline at end of file
@@ -0,0 +1,278
1 // uuid.js
2 //
3 // Copyright (c) 2010-2012 Robert Kieffer
4 // MIT License - http://opensource.org/licenses/mit-license.php
5 define([], function () {
6 'use strict';
7
8 var _window = 'undefined' !== typeof window ? window : null;
9
10 // Unique ID creation requires a high quality random # generator. We
11 // feature
12 // detect to determine the best RNG source, normalizing to a function
13 // that
14 // returns 128-bits of randomness, since that's what's usually required
15 var _rng, _mathRNG, _nodeRNG, _whatwgRNG, _previousRoot;
16
17 function setupBrowser() {
18 // Allow for MSIE11 msCrypto
19 var _crypto = _window.crypto || _window.msCrypto;
20
21 if (!_rng && _crypto && _crypto.getRandomValues) {
22 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
23 //
24 // Moderately fast, high quality
25 try {
26 var _rnds8 = new Uint8Array(16);
27 _whatwgRNG = _rng = function whatwgRNG() {
28 _crypto.getRandomValues(_rnds8);
29 return _rnds8;
30 };
31 _rng();
32 } catch (e) { /**/ }
33 }
34
35 if (!_rng) {
36 // Math.random()-based (RNG)
37 //
38 // If all else fails, use Math.random(). It's fast, but is of
39 // unspecified
40 // quality.
41 var _rnds = new Array(16);
42 _mathRNG = _rng = function () {
43 for (var i = 0, r; i < 16; i++) {
44 if ((i & 0x03) === 0) {
45 r = Math.random() * 0x100000000;
46 }
47 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
48 }
49
50 return _rnds;
51 };
52 if ('undefined' !== typeof console && console.warn) {
53 console
54 .warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()");
55 }
56 }
57 }
58
59 function setupNode() {
60 // Node.js crypto-based RNG -
61 // http://nodejs.org/docs/v0.6.2/api/crypto.html
62 //
63 // Moderately fast, high quality
64 if ('function' === typeof require) {
65 try {
66 var _rb = require('crypto').randomBytes;
67 _nodeRNG = _rng = _rb && function () {
68 return _rb(16);
69 };
70 _rng();
71 } catch (e) { /**/ }
72 }
73 }
74
75 if (_window) {
76 setupBrowser();
77 } else {
78 setupNode();
79 }
80
81 // Buffer class to use
82 var BufferClass = ('function' === typeof Buffer) ? Buffer : Array;
83
84 // Maps for number <-> hex string conversion
85 var _byteToHex = [];
86 var _hexToByte = {};
87 for (var i = 0; i < 256; i++) {
88 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
89 _hexToByte[_byteToHex[i]] = i;
90 }
91
92 // **`parse()` - Parse a UUID into it's component bytes**
93 function parse(s, buf, offset) {
94 var i = (buf && offset) || 0,
95 ii = 0;
96
97 buf = buf || [];
98 s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) {
99 if (ii < 16) { // Don't overflow!
100 buf[i + ii++] = _hexToByte[oct];
101 }
102 });
103
104 // Zero out remaining bytes if string was short
105 while (ii < 16) {
106 buf[i + ii++] = 0;
107 }
108
109 return buf;
110 }
111
112 // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
113 function unparse(buf, offset) {
114 var i = offset || 0,
115 bth = _byteToHex;
116 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
117 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' +
118 bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] +
119 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] +
120 bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
121 }
122
123 // **`v1()` - Generate time-based UUID**
124 //
125 // Inspired by https://github.com/LiosK/UUID.js
126 // and http://docs.python.org/library/uuid.html
127
128 // random #'s we need to init node and clockseq
129 var _seedBytes = _rng();
130
131 // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit =
132 // 1)
133 var _nodeId = [
134 _seedBytes[0] | 0x01,
135 _seedBytes[1],
136 _seedBytes[2],
137 _seedBytes[3],
138 _seedBytes[4],
139 _seedBytes[5]
140 ];
141
142 // Per 4.2.2, randomize (14 bit) clockseq
143 var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
144
145 // Previous uuid creation time
146 var _lastMSecs = 0,
147 _lastNSecs = 0;
148
149 // See https://github.com/broofa/node-uuid for API details
150 function v1(options, buf, offset) {
151 var i = buf && offset || 0;
152 var b = buf || [];
153
154 options = options || {};
155
156 var clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
157
158 // UUID timestamps are 100 nano-second units since the Gregorian
159 // epoch,
160 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
161 // time is handled internally as 'msecs' (integer milliseconds) and
162 // 'nsecs'
163 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01
164 // 00:00.
165 var msecs = (options.msecs != null) ? options.msecs : new Date()
166 .getTime();
167
168 // Per 4.2.1.2, use count of uuid's generated during the current
169 // clock
170 // cycle to simulate higher resolution clock
171 var nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1;
172
173 // Time since last uuid creation (in msecs)
174 var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
175
176 // Per 4.2.1.2, Bump clockseq on clock regression
177 if (dt < 0 && options.clockseq == null) {
178 clockseq = clockseq + 1 & 0x3fff;
179 }
180
181 // Reset nsecs if clock regresses (new clockseq) or we've moved onto
182 // a new
183 // time interval
184 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
185 nsecs = 0;
186 }
187
188 // Per 4.2.1.2 Throw error if too many uuids are requested
189 if (nsecs >= 10000) {
190 throw new Error(
191 'uuid.v1(): Can\'t create more than 10M uuids/sec');
192 }
193
194 _lastMSecs = msecs;
195 _lastNSecs = nsecs;
196 _clockseq = clockseq;
197
198 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
199 msecs += 12219292800000;
200
201 // `time_low`
202 var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
203 b[i++] = tl >>> 24 & 0xff;
204 b[i++] = tl >>> 16 & 0xff;
205 b[i++] = tl >>> 8 & 0xff;
206 b[i++] = tl & 0xff;
207
208 // `time_mid`
209 var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
210 b[i++] = tmh >>> 8 & 0xff;
211 b[i++] = tmh & 0xff;
212
213 // `time_high_and_version`
214 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
215 b[i++] = tmh >>> 16 & 0xff;
216
217 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
218 b[i++] = clockseq >>> 8 | 0x80;
219
220 // `clock_seq_low`
221 b[i++] = clockseq & 0xff;
222
223 // `node`
224 var node = options.node || _nodeId;
225 for (var n = 0; n < 6; n++) {
226 b[i + n] = node[n];
227 }
228
229 return buf ? buf : unparse(b);
230 }
231
232 // **`v4()` - Generate random UUID**
233
234 // See https://github.com/broofa/node-uuid for API details
235 function v4(options, buf, offset) {
236 // Deprecated - 'format' argument, as supported in v1.2
237 var i = buf && offset || 0;
238
239 if (typeof (options) === 'string') {
240 buf = (options === 'binary') ? new BufferClass(16) : null;
241 options = null;
242 }
243 options = options || {};
244
245 var rnds = options.random || (options.rng || _rng)();
246
247 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
248 rnds[6] = (rnds[6] & 0x0f) | 0x40;
249 rnds[8] = (rnds[8] & 0x3f) | 0x80;
250
251 // Copy bytes to buffer, if provided
252 if (buf) {
253 for (var ii = 0; ii < 16; ii++) {
254 buf[i + ii] = rnds[ii];
255 }
256 }
257
258 return buf || unparse(rnds);
259 }
260
261 // Export public API
262 var uuid = function () {
263 return new String(v4());
264 };
265 uuid.v1 = v1;
266 uuid.v4 = v4;
267 uuid.create = v4;
268 uuid.empty = "00000000-0000-0000-0000-000000000000";
269 uuid.parse = parse;
270 uuid.unparse = unparse;
271 uuid.BufferClass = BufferClass;
272 uuid._rng = _rng;
273 uuid._mathRNG = _mathRNG;
274 uuid._nodeRNG = _nodeRNG;
275 uuid._whatwgRNG = _whatwgRNG;
276
277 return uuid;
278 }); No newline at end of file
@@ -0,0 +1,120
1 define(["dojo/_base/declare", "../guard", "../safe", "../log/_LogMixin"], function (declare, guard, safe, _LogMixin) {
2 "use strict";
3 return declare([_LogMixin], {
4
5 _current: null,
6
7 _pending: false,
8
9 getCurrent: function () {
10 return this._current;
11 },
12
13 _start: function () {
14 if (this._pending)
15 throw new Error("The activation/decativation is already pending");
16 this._pending = true;
17 },
18
19 _await: function (d) {
20 var me = this;
21 return d.then(function (x) {
22 me._pending = false;
23 return x;
24 }, function (e) {
25 me._pending = false;
26 throw e;
27 });
28 },
29
30 activate: function (component) {
31 safe.argumentNotNull(component, "component");
32 var me = this;
33 if (component.getController() !== this)
34 throw new Error("The specified component doesn't belong to this controller");
35
36 return me._await(guard(me, "_start").then(function () {
37 me._activate(component);
38 }));
39 },
40
41 _activate: function (component) {
42 var me = this;
43 if (me._current === component)
44 return guard(false);
45
46 // before activation hook
47 return guard(me, "onActivating", [component]).then(function () {
48 // deactivate curent
49 if (me._current)
50 return me._current.deactivate(true).then(function () {
51 try {
52 me._current.onDeactivated();
53 } catch (err) {
54 me.error(err);
55 }
56 // HACK raise deactivated event
57 try {
58 me.onDeactivated(me._current, component);
59 } catch (err) {
60 // deactivated shouldn't affect the process
61 me.error(err);
62 }
63 me._current = null;
64
65 });
66 }).then(function () {
67 return component.activate(true);
68 }).then(function () {
69 me._current = component;
70 try {
71 me.onActivated(component);
72 } catch (err) {
73 me.error(err);
74 }
75
76 });
77
78 },
79
80 /**
81 * Деактивирует текущую компоненту.
82 *
83 * @async
84 * @returns true - компонента была деактивирована, либо нет активной
85 * компоненты. false - запрос на деактивацию - отклонен.
86 */
87 deactivate: function () {
88 var me = this;
89 return me._await(guard(me, "_start").then(function () {
90 return me._deactivate();
91 }));
92 },
93
94 _deactivate: function () {
95 var me = this;
96 if (!me._current)
97 return guard(false);
98
99 return guard(me, "onDeactivating").then(function () {
100 return me._current.deactivate(true);
101 }).then(function () {
102 // HACK raise deactivated event
103 try {
104 me.onDeactivated(me._current);
105 } catch (err) {
106 me.error(err);
107 }
108 me._current = null;
109 });
110 },
111
112 onActivating: function (component) {},
113
114 onDeactivating: function (component) {},
115
116 onDeactivated: function (component, next) {},
117
118 onActivated: function (component) {}
119 });
120 }); No newline at end of file
@@ -0,0 +1,34
1 define([ "dojo/_base/declare", "../safe", "../text/format" ], function(declare, safe, format) {
2 return declare(null, {
3 states : null,
4
5 current : null,
6
7 constructor : function(opts) {
8 safe.argumentNotNull(opts, "opts");
9 safe.argumentNotNull(opts.states, "opts.states");
10 safe.argumentNotNull(opts.initial, "opts.initial");
11
12 this.states = opts.states;
13 this.current = opts.initial;
14
15 if (safe.isNull(this.states[this.current]))
16 throw new Error("Invalid initial state " + this.current);
17 },
18
19 move : function(input, noThrow) {
20 safe.argumentNotNull(input, "input");
21
22 var next = this.states[this.current][input];
23 if(safe.isNull(next)) {
24 if (noThrow)
25 return false;
26 else
27 throw new Error(format("Invalid transition {0}-{1}->?", this.current, input));
28 } else {
29 this.current = next;
30 return true;
31 }
32 }
33 });
34 }); No newline at end of file
@@ -0,0 +1,153
1 define(["dojo/_base/declare", "../guard", "./StateMachine", "../log/_LogMixin", ], function (declare, guard, StateMachine, _LogMixin) {
2
3 var states = {
4 inactive: {
5 activate: "activating"
6 },
7 activating: {
8 success: "active",
9 failed: "inactive"
10 },
11 active: {
12 deactivate: "deactivating"
13 },
14 deactivating: {
15 success: "inactive",
16 failed: "active"
17 }
18 };
19
20 return declare([_LogMixin], {
21 _controller: null,
22
23 _active: null,
24
25 constructor: function () {
26 this._active = new StateMachine({
27 states: states,
28 initial: "inactive"
29 });
30 },
31
32 /**
33 * @returns {Object} контроллер для активации текущей компоненты
34 */
35 getController: function () {
36 return this._controller;
37 },
38
39 /**
40 * @param {Object}
41 * v Контроллер для активации текущей компоненты
42 */
43 setController: function (v) {
44 this._controller = v;
45 },
46
47 /**
48 * @returns {Boolean} текущая компонента активна
49 */
50 isActive: function () {
51 return this._active.current == "active";
52 },
53
54 assertActive: function () {
55 if (!this.isActive())
56 throw new Error("The object must be active to perform the operation");
57 },
58
59 /**
60 * Активирует текущую компоненту, если у текущей компоненты задан
61 * контроллер, то активация будет осуществляться через него
62 *
63 * @async
64 * @param{Boolean}
65 * direct вызов должен осуществится напрямую, без участия
66 * контроллера.
67 * @return{Boolean} успешно/неуспешно
68 */
69 activate: function (direct) {
70 var me = this;
71 if (!direct && this._controller)
72 return me._controller.activate(me).then(function () {
73 me.onActivated();
74 });
75
76 me._active.move("activate");
77 return guard(me, "onActivating").then(function () {
78 me.log("Activated");
79 me._active.move("success");
80 if (!me._controller)
81 me.onActivated();
82 }, function (err) {
83 console.error(err);
84 me.error("Activation failed: {0}", err);
85 me._active.move("failed");
86 throw err;
87 });
88 },
89
90 /**
91 * Деактивирует текущую компоненту, если у компоненты задан контроллер,
92 * то деактивация будет осуществляться через него.
93 *
94 * @async
95 * @param{Boolean} direct вызов должен осуществится напрямую, без
96 * участия контроллера.
97 *
98 */
99 deactivate: function (direct) {
100 var me = this;
101 if (!direct && me._controller)
102 return me._controller.deactivate(me).then(function () {
103 me.onDeactivated();
104 });
105
106 me._active.move("deactivate");
107 return guard(me, "onDeactivating").then(function () {
108 me.log("Deactivated");
109 me._active.move("success");
110 if (!me._controller)
111 me.onDeactivated();
112 }, function (err) {
113 console.error(err);
114 me.error("Deactivation failed: {0}", err);
115 me.move("failed");
116 throw err;
117 });
118
119 },
120
121 toogleActive: function () {
122 var me = this;
123 return (me.isActive() ? me.deactivate() : me.activate()).then(function () {
124 return me.isActive();
125 });
126 },
127
128 /**
129 * Событие вызывается перед активацией текущей компоненты
130 *
131 * @returns{Boolean|undefined} если false - активация будет отменена
132 */
133 onActivating: function () {},
134
135 /**
136 * Событие вызывается перед деактивацией текущей компоненты
137 *
138 * @returns {Boolean|undefined} если false - деактивация будет отменена
139 */
140 onDeactivating: function () {},
141
142 /**
143 * Событие вызывается после активации текущей компоненты
144 */
145 onActivated: function () {},
146
147 /**
148 * Событие вызывается после деактивации текущей компоненты
149 */
150 onDeactivated: function () {}
151
152 });
153 }); No newline at end of file
@@ -0,0 +1,45
1 define([ "dojo/_base/declare", "../safe" ], function(declare, safe) {
2 return declare(
3 null,
4 {
5 _params : null,
6
7 _repositories : null,
8
9 constructor : function(opts) {
10 this._params = opts || {};
11 this._repositories = {};
12 },
13
14 getRepository : function(name) {
15 safe.argumentNotEmptyString(name, "name");
16 var repo = this._repositories[name];
17 if (!repo) {
18 repo = this._params[name];
19 if (!repo)
20 throw new Error("The repository '" + name +
21 "' isn't found");
22 if (repo instanceof Function)
23 repo = new repo(); // factory method or constructor
24 if (repo.initialize) {
25 repo.initialize({
26 dataContext : this
27 });
28 } else if (repo.setDataContext) {
29 repo.setDataContext(this);
30 }
31 this._repositories[name] = repo;
32 }
33
34 return repo;
35 },
36
37 dispose : function() {
38 for( var name in this._repositories) {
39 var r = this._repositories[name];
40 if (r.dispose)
41 r.dispose();
42 }
43 }
44 });
45 }); No newline at end of file
@@ -0,0 +1,67
1 define([ "dojo/_base/declare", "../safe" ], function(declare, safe) {
2 return declare(null, {
3 /**
4 * Отображение одного типа объектов в другой.
5 *
6 * @remarks Отображения являются односторонними, т.е. позволяют
7 * перенести часть содержимого одного объекта в другой. Каждая
8 * схема отображения строится из набора примитивных
9 * отображений, которые будут применены в произвольном порядке.
10 */
11 _schema : null,
12
13 constructor : function(schema) {
14 this._schema = schema;
15 },
16
17 /**
18 * Осуществляет отображение одного объекта в другой
19 *
20 * @src{Object} Исходный объект из которого будут взяты данные
21 * @dst{Object}
22 */
23 map : function(src, dst, ctx) {
24 safe.argumentNotNull(src, "src");
25 safe.argumentNotNull(dst, "dst");
26
27 for ( var p in this._schema) {
28 var mapper = this._schema[p];
29 if (mapper instanceof Function) {
30 dst[p] = mapper(src[p]);
31 } else if (mapper && mapper.map) {
32 mapper.map(src, dst, p, ctx);
33 } else {
34 this._defaultMapper(src, dst, p, mapper, ctx);
35 }
36 }
37 },
38
39 _defaultMapper : function(src, dst, prop, opts) {
40 if (typeof (opts) == "string") {
41 if (opts in src)
42 dst[prop] = src[opts];
43 } else if (opts && opts.type instanceof Function) {
44 if (src[prop] instanceof opts.type)
45 dst[prop] = src[prop];
46 else
47 dst[prop] = this._isPrimitiveType(opts.type) ? opts.type
48 .call(null, src[prop]) : new opts.type(src[prop]);
49
50 } else {
51 if (!(prop in src))
52 if (opts && opts.required)
53 throw new Error("The " + prop + "is missing");
54 else
55 return;
56 dst[prop] = src[prop];
57 }
58 },
59
60 _isPrimitiveType : function(type) {
61 return (type === String || type === Number || type === Boolean
62 || type === Number || type === Date);
63 }
64
65 });
66
67 }); No newline at end of file
@@ -0,0 +1,174
1 define([ "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array",
2 "../safe", "dojo/when", "dojo/Deferred", "dojo/store/util/QueryResults" ], function(declare,
3 lang, array, safe, when, Deferred, QueryResults) {
4 /**
5 * @module implab/data/RestStore
6 *
7 * Реализует шаблон репозитария dojo/store над уже имеющимся хранилищем. При получении и
8 * отправке данных в нижележащие хранилище используется implab/data/MapSchema для преобразования
9 * данных.
10 */
11 return declare(null, {
12
13 model: null,
14
15 mapping: null,
16
17 _dataContext: null,
18
19 _store : null, // backing store
20
21 _cache : null,
22
23 constructor : function(options) {
24 options = options || {};
25
26 if (options.store)
27 this._store = options.store;
28
29 if (options.dataContext) {
30 this._dataContext = options.dataContext;
31 }
32
33 if (options.cache === false) {
34 // no cache at all
35 } else if (options.cache === "string" && options.dataContext) {
36 this._cache = this._dataContext.getCache(options.cache);
37 } else {
38 this._cache = {};
39 }
40 },
41
42 getDataContext : function() {
43 return this._dataContext;
44 },
45
46 // READ
47 get : function(id) {
48 var me = this;
49 var cache = me.getCacheEntry(id);
50 if (cache)
51 return cache;
52 else
53 return when(me._store.get(id), function(data) {
54 return me._mapToObject(id, data);
55 });
56 },
57
58 query : function(query, options) {
59 var me = this;
60 var d = me._store.query(query, options);
61 var result = QueryResults(when(d, function(data) {
62 return array.map(data, function(item) {
63 return me._mapToObject(me._store.getIdentity(item), item);
64 });
65 }));
66 result.total = d.total;
67 return result;
68 },
69
70 getIdentity : function(object) {
71 return object.getId();
72 },
73
74 // UPDATE
75 put : function(object, directives) {
76 return this._store.put(this._mapFromObject(object), directives);
77 },
78
79 // INSERT
80 add : function(object, directives) {
81 var me = this;
82 // добавляем в хранилище данные, сохраняем в кеше объект с
83 // полученным идентификатором
84 return when(
85 me._store.add(this._mapFromObject(object), directives),
86 function(id) {
87 object.attach(id, me);
88 me.storeCacheEntry(id, object);
89 return id;
90 });
91 },
92
93 // DELETE
94 remove : function(id) {
95 var me = this;
96 return when(me._store.remove(id), function() {
97 me.removeCacheEntry(id);
98 });
99 },
100
101 _mapToObject : function(id, data) {
102 var instance = this.createInstance(id);
103 this.populateInstance(instance, data);
104 return instance;
105 },
106
107 _mapFromObject : function(object) {
108 return this.serializeInstance(object);
109 },
110
111 getCacheEntry : function(id) {
112 safe.argumentNotNull(id, "id");
113 id = id.toString();
114
115 return this._cache[id];
116 },
117
118 storeCacheEntry : function(id, object) {
119 safe.argumentNotNull(id, "id");
120 id = id.toString();
121
122 this._cache[id] = object;
123 },
124
125 removeCacheEntry : function(id) {
126 safe.argumentNotNull(id, "id");
127 id = id.toString();
128 delete this._cache[id];
129 },
130
131 /** Создает экземпляр сущности с указанным идентификатором, либо извлекает из кеша, если таковая уже имеется.
132 * @remarks
133 * Технически сюда можно было бы дополнительно передать данные для ининциализации объекта,
134 * но концептуально это не верно, поскольку процесс чтения объекта состоит из двух этапов:
135 * 1. Создание пустого объекта (createInstance)
136 * 2. Заполнение объекта при помощи схемы отображения (populateInstance)
137 * при этом первый этап может быть выполнен за долго до второго, например,
138 * при создании заглушек в процессе установления ссылок между объектами.
139 */
140 createInstance : function(id) {
141 var instance = this.getCacheEntry(id);
142 if (!instance) {
143 instance = this.createInstanceImpl(id);
144 this.storeCacheEntry(id, instance);
145 }
146 return instance;
147 },
148
149 /** Непосредственно создает экземпляр сущнсти, т.е. является фабричным методом.
150 * @param {String} id идентификатор создаваемого экземпляра.
151 */
152 createInstanceImpl : function(id) {
153 var opts = {
154 dataContext : this.getDataContext(),
155 id : id
156 };
157
158 return new this.itemsType(opts);
159 },
160
161 populateInstance : function(instance, data) {
162 this.mapping.readData(instance, data,this.getDataContext());
163 if (instance.onPopulate)
164 instance.onPopulate();
165 },
166
167 serializeInstance : function(instance) {
168 var data = {};
169 this.mapping.writeData(instance, data, this.getDataContext());
170 return data;
171 }
172
173 });
174 }); No newline at end of file
@@ -0,0 +1,166
1 define([ "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array",
2 "../safe", "dojo/when", "dojo/Deferred", "dojo/store/util/QueryResults" ], function(declare,
3 lang, array, safe, when, Deferred, QueryResults) {
4 /**
5 * @module implab/data/RestStore
6 *
7 * Реализует шаблон репозитария dojo/store над уже имеющимся хранилищем. При получении и
8 * отправке данных в нижележащие хранилище используется implab/data/MapSchema для преобразования
9 * данных.
10 */
11 return declare(null, {
12
13 itemsType : null,
14
15 _dataContext : null,
16
17 _store : null, // backing store
18 _cache : null,
19
20 constructor : function(options) {
21 options = options || {};
22
23 this._cache = {};
24 if (options.store)
25 this._store = options.store;
26 if (options.dataContext)
27 this._dataContext = options.dataContext;
28 },
29
30 setDataContext : function(v) {
31 this._dataContext = v;
32 },
33
34 getDataContext : function() {
35 return this._dataContext;
36 },
37
38 // READ
39 get : function(id) {
40 var me = this;
41 var cache = me.getCacheEntry(id);
42 if (cache)
43 return cache;
44 else
45 return when(me._store.get(id), function(data) {
46 return me._mapToObject(id, data);
47 });
48 },
49
50 query : function(query, options) {
51 var me = this;
52 var d = me._store.query(query, options);
53 var result = QueryResults(when(d, function(data) {
54 return array.map(data, function(item) {
55 return me._mapToObject(me._store.getIdentity(item), item);
56 });
57 }));
58 result.total = d.total;
59 return result;
60 },
61
62 getIdentity : function(object) {
63 return object.getId();
64 },
65
66 // UPDATE
67 put : function(object, directives) {
68 return this._store.put(this._mapFromObject(object), directives);
69 },
70
71 // INSERT
72 add : function(object, directives) {
73 var me = this;
74 // добавляем в хранилище данные, сохраняем в кеше объект с
75 // полученным идентификатором
76 return when(
77 me._store.add(this._mapFromObject(object), directives),
78 function(id) {
79 object.attach(id, me);
80 me.storeCacheEntry(id, object);
81 return id;
82 });
83 },
84
85 // DELETE
86 remove : function(id) {
87 var me = this;
88 return when(me._store.remove(id), function() {
89 me.removeCacheEntry(id);
90 });
91 },
92
93 _mapToObject : function(id, data) {
94 var instance = this.createInstance(id);
95 this.populateInstance(instance, data);
96 return instance;
97 },
98
99 _mapFromObject : function(object) {
100 return this.serializeInstance(object);
101 },
102
103 getCacheEntry : function(id) {
104 safe.argumentNotNull(id, "id");
105 id = id.toString();
106
107 return this._cache[id];
108 },
109
110 storeCacheEntry : function(id, object) {
111 safe.argumentNotNull(id, "id");
112 id = id.toString();
113
114 this._cache[id] = object;
115 },
116
117 removeCacheEntry : function(id) {
118 safe.argumentNotNull(id, "id");
119 id = id.toString();
120 delete this._cache[id];
121 },
122
123 /** Создает экземпляр сущности с указанным идентификатором, либо извлекает из кеша, если таковая уже имеется.
124 * @remarks
125 * Технически сюда можно было бы дополнительно передать данные для ининциализации объекта,
126 * но концептуально это не верно, поскольку процесс чтения объекта состоит из двух этапов:
127 * 1. Создание пустого объекта (createInstance)
128 * 2. Заполнение объекта при помощи схемы отображения (populateInstance)
129 * при этом первый этап может быть выполнен за долго до второго, например,
130 * при создании заглушек в процессе установления ссылок между объектами.
131 */
132 createInstance : function(id) {
133 var instance = this.getCacheEntry(id);
134 if (!instance) {
135 instance = this.createInstanceImpl(id);
136 this.storeCacheEntry(id, instance);
137 }
138 return instance;
139 },
140
141 /** Непосредственно создает экземпляр сущнсти, т.е. является фабричным методом.
142 * @param {String} id идентификатор создаваемого экземпляра.
143 */
144 createInstanceImpl : function(id) {
145 var opts = {
146 dataContext : this.getDataContext(),
147 id : id
148 };
149
150 return new this.itemsType(opts);
151 },
152
153 populateInstance : function(instance, data) {
154 this.itemsType.readData(instance, data,this.getDataContext());
155 if (instance.onPopulate)
156 instance.onPopulate();
157 },
158
159 serializeInstance : function(instance) {
160 var data = {};
161 this.itemsType.writeData(instance, data, this.getDataContext());
162 return data;
163 }
164
165 });
166 }); No newline at end of file
@@ -0,0 +1,19
1 define(["dojo/_base/declare", "dojo/_base/array", "../safe", "./StoreAdapter"], function(declare, array, safe ,AdapterStore){
2 return declare([AdapterStore], {
3 _attrs : null,
4
5 constructor : function(opts) {
6 safe.argumentNotEmptyArray(opts.attrs, "opts.attrs");
7 this._attrs = opts.attrs;
8 },
9
10 mapItem : function(item) {
11 var result = {};
12 array.forEach(this._attrs, function(p) {
13 result[p] = item.get(p);
14 });
15 return result;
16 }
17 });
18
19 }); No newline at end of file
@@ -0,0 +1,110
1 define([
2 "dojo/_base/declare",
3 "../safe",
4 "dojo/when",
5 "dojo/store/util/QueryResults" ],
6
7 function(declare, safe, when, QueryResults) {
8
9 "use strict";
10
11 /**
12 * Обертка вокруг произвольного хранилища, только для чтения. Используется
13 * для преобразования данных, например, отображения в списках элементов
14 * пространственных данных.
15 */
16 return declare(null, {
17 /**
18 * @type{String} Свойство, хранящее идентификатор
19 */
20 idProperty : null,
21
22 _store : null,
23
24 /**
25 * @param{String} opts.idProperty Имя свойства, в которое будет записан
26 * идентификатор, если не указан, то идентификатор будет
27 * взят из родительского хранилища или использоваться
28 * строка <code>id</code>
29 * @param{dojo.store} opts.store Родительское хранилище
30 */
31 constructor : function(opts) {
32 safe.argumentNotNull(opts, "opts");
33 safe.argumentNotNull(opts.store, "opts.store");
34
35 this._store = opts.store;
36 delete opts.store;
37 declare.safeMixin(this, opts);
38 this.idProperty = opts.idProperty || this._store.idProperty || "id";
39 },
40
41 getParentStore : function() {
42 return this._store;
43 },
44
45 get : function(id) {
46 var me = this;
47 return when(me._store.get(id), function(x) {
48 var m = me.mapItem(x);
49 if (!(me.idProperty in m))
50 m[me.idProperty] = id;
51 return m;
52 });
53 },
54
55 /**
56 * Выполняет запрос в родительском хранилище, для этого используется
57 * <code>translateQuery</code> для подготовки запроса, затем,
58 * <code>mapItem</code> для преобразования результатов.
59 */
60 query : function(q, options) {
61 var me = this, store = this._store;
62 return when(store.query(me.translateQuery(q), me
63 .translateOptions(options)), function(res) {
64 var total = res.total;
65 var mapped = res.map(function(x) {
66 var m = me.mapItem(x);
67 if (!(me.idProperty in m))
68 m[me.idProperty] = store.getIdentity &&
69 store.getIdentity(x);
70 return m;
71 });
72 mapped.total = total;
73 var results = new QueryResults(mapped);
74 console.log(results);
75 return results;
76 });
77 },
78
79 getIdentity : function(obj) {
80 return obj && obj[this.idProperty];
81 },
82
83 /**
84 * Преобразование запроса в формат родительского хранилища.
85 *
86 * @param{Object} q Запрос в формате текущего хранилища
87 * @returns{Object} Запрос в формате родительского хранилища
88 */
89 translateQuery : function(q) {
90 return q;
91 },
92
93 translateOptions : function(options) {
94 return options;
95 },
96
97 /**
98 * Преобразование объекта из родительского хранилища. При преобразовании
99 * в объекте можно задать идентификатор, иначе идентификатор будет
100 * автоматически получен и присвоен из родительского хранилища
101 *
102 * @param{Object} item Объект из родительского хранилища
103 * @returns{Object} результат преобразования
104 */
105 mapItem : function(item) {
106 return item;
107 }
108 });
109
110 }); No newline at end of file
@@ -0,0 +1,37
1 define(["dojo/_base/declare"], function(declare) {
2
3 return declare(null, {
4 dataContext : null,
5 idField : "id",
6 loaded : false,
7
8 constructor : function(opts){
9 if (opts) {
10 if(opts.dataContext)
11 this.dataContext = opts.dataContext;
12 if(opts.id)
13 this[this.idField] = opts.id;
14 }
15 },
16
17 getId : function() {
18 return this[this.idField];
19 },
20
21 attach : function(id, dc) {
22 if (this.dataContext)
23 throw new Error("The object is already attached");
24 this[this.idField] = id;
25 this.dataContext = dc;
26 },
27
28 isAttached : function() {
29 return this.dataContext ? true : false;
30 },
31
32 onPopulate : function() {
33 this.loaded = true;
34 }
35
36 });
37 }); No newline at end of file
@@ -0,0 +1,5
1 define(["dojo/_base/declare", "dojo/Stateful"], function(declare, Stateful) {
2 return declare([Stateful], {
3
4 });
5 }); No newline at end of file
@@ -0,0 +1,72
1 define([ "dojo/_base/declare", "./_ModelBase", "./MapSchema" ], function(
2 declare, _ModelBase, MapSchema) {
3 /**
4 * Создает новый класс, унаследованный от ./ModelBase, с указанной схемой
5 * отображения данных.
6 *
7 * @details Модель представляет собой объект, живущий в рамках контекста
8 * данных, также имеющий две схемы отображения: из модели хранения
9 * в источнике данных (toObjectMap) и наооборот в модель хранения в
10 * источнике данных (fromObjectMap).
11 *
12 * Описание схемы выглядит следующим образом
13 * <pre>
14 * {
15 * name : null, // отображение в обе стороны без преобразования
16 *
17 * age : Number, // при преобразоваении к объекту поле будет преобразовано dst.age = Number(src.age)
18 * // обратное преобразование отсутстсвует
19 *
20 * age : [Number, null] // тоже самое что и age : Number
21 *
22 * date : [Date, function(v) { return v.toString() }] // указывается преобразование в одну и в другую сторону
23 * }
24 * <pre>
25 */
26 return function(schema, mixins, opts) {
27 var fromObjectSchema = {}, toObjectSchema = {};
28 if (schema !== null && schema !== undefined) {
29 for ( var p in schema) {
30 var mapper = schema[p];
31
32 if (mapper instanceof Array) {
33 toObjectSchema[p] = mapper[0];
34 fromObjectSchema[p] = mapper[1];
35 } else {
36 toObjectSchema[p] = mapper;
37 fromObjectSchema[p] = null;
38 }
39 }
40 }
41
42 if (arguments.length < 3) {
43 opts = mixins;
44 mixins = undefined;
45 }
46
47 var base = [ _ModelBase ];
48 if (mixins) {
49 if (mixins instanceof Array)
50 base = base.concat(mixins);
51 else
52 base.push(mixins);
53 }
54
55 var model = declare(base, opts);
56
57 model.toObjectMap = new MapSchema(toObjectSchema);
58
59 model.fromObjectMap = new MapSchema(fromObjectSchema);
60
61 model.readData = function(that, data, context) {
62 model.toObjectMap.map(data, that, context);
63 };
64
65 model.writeData = function(that, data, context) {
66 data = data || {};
67 model.fromObjectMap.map(that, data, context);
68 };
69
70 return model;
71 };
72 }); No newline at end of file
@@ -0,0 +1,6
1 define([
2 './declare/_load!'
3 ], function(declare) {
4 'use strict';
5 return declare;
6 }); No newline at end of file
@@ -0,0 +1,12
1 define([], function () {
2 'use strict';
3
4 return {
5 load: function (id, require, callback) {
6 require(['dojo/_base/declare'], function (declare) {
7 callback(declare);
8 });
9 }
10 };
11
12 }); No newline at end of file
@@ -0,0 +1,73
1 "use strict";
2 define([], function () {
3 var slice = Array.prototype.slice;
4 var override = function (method) {
5 var proxy;
6
7 /** @this target object */
8 proxy = function () {
9 var me = this;
10 var inherited = (this.getInherited && this.getInherited(proxy.nom, {
11 callee: proxy
12 })) || function () {};
13
14 return method.apply(me, [function () {
15 return inherited.apply(me, arguments);
16 }].concat(slice.apply(arguments)));
17 };
18
19 proxy.method = method;
20 proxy.overrides = true;
21
22 return proxy;
23 };
24
25 override.before = function (method) {
26 var proxy;
27
28 /** @this target object */
29 proxy = function () {
30 var me = this;
31 var inherited = (this.getInherited && this.getInherited(proxy.nom, {
32 callee: proxy
33 })) || function () {};
34
35
36 method.apply(me, arguments);
37 return inherited.apply(me, arguments);
38 };
39
40 proxy.method = method;
41 proxy.overrides = true;
42
43 return proxy;
44 };
45
46 override.after = function (method) {
47 var proxy;
48
49 /** @this target object */
50 proxy = function () {
51 var me = this;
52 var inherited = (this.getInherited && this.getInherited(proxy.nom, {
53 callee: proxy
54 })) || function () {};
55
56 inherited.apply(me, arguments);
57
58 return method.apply(me, arguments);
59 };
60
61 proxy.method = method;
62 proxy.overrides = true;
63
64 return proxy;
65 };
66
67 override.hide = function (method) {
68 method.overrides = false;
69 return method;
70 };
71
72 return override;
73 }); No newline at end of file
@@ -0,0 +1,138
1 define([
2 "../declare",
3 "../safe",
4 "./Descriptor",
5 "./ValueDescriptor",
6 "../log/trace!"
7 ], function (declare, safe, Descriptor, Value, trace) {
8 var Context = declare(null, {
9
10 _cache: null,
11
12 _services: null,
13
14 _stack: null,
15
16 _visited: null,
17
18 container: null,
19
20 _trace: true,
21
22 constructor: function (container, services, cache, visited) {
23 safe.argumentNotNull(container, "container");
24 safe.argumentNotNull(services, "services");
25
26 this._visited = visited || {};
27 this._stack = [];
28 this._cache = cache || {};
29 this._services = services;
30 this.container = container;
31 },
32
33 getService: function (name, def) {
34 var d = this._services[name];
35
36 if (!d)
37 if (arguments.length > 1)
38 return def;
39 else
40 throw new Error("Service '" + name + "' not found");
41
42 return d.activate(this, name);
43 },
44
45 /**
46 * registers services local to the the activation context
47 *
48 * @name{string} the name of the service
49 * @service{string} the service descriptor to register
50 */
51 register: function (name, service) {
52 safe.argumentNotEmptyString(name, "name");
53
54 if (!(service instanceof Descriptor))
55 service = new Value(service, true);
56 this._services[name] = service;
57 },
58
59 clone: function () {
60 return new Context(
61 this.container,
62 Object.create(this._services),
63 this._cache,
64 this._visited
65 );
66
67 },
68
69 has: function (id) {
70 return id in this._cache;
71 },
72
73 get: function (id) {
74 return this._cache[id];
75 },
76
77 store: function (id, value) {
78 return (this._cache[id] = value);
79 },
80
81 parse: function (data, name) {
82 var me = this;
83 if (safe.isPrimitive(data))
84 return data;
85
86 if (data instanceof Descriptor) {
87 return data.activate(this, name);
88 } else if (data instanceof Array) {
89 me.enter(name);
90 var v = data.map(function (x, i) {
91 return me.parse(x, "." + i);
92 });
93 me.leave();
94 return v;
95 } else {
96 me.enter(name);
97 var result = {};
98 for (var p in data)
99 result[p] = me.parse(data[p], "." + p);
100 me.leave();
101 return result;
102 }
103 },
104
105 visit: function (id) {
106 var count = this._visited[id] || 0;
107 this._visited[id] = count + 1;
108 return count;
109 },
110
111 getStack: function () {
112 return this._stack.slice().reverse();
113 },
114
115 enter: function (name, d, localize) {
116 if (this._trace)
117 trace.log("enter " + name + " " + (d || "") +
118 (localize ? " localize" : ""));
119 this._stack.push({
120 name: name,
121 service: d,
122 scope: this._services
123 });
124 if (localize)
125 this._services = Object.create(this._services);
126 },
127
128 leave: function () {
129 var ctx = this._stack.pop();
130 this._services = ctx.scope;
131
132 if (this._trace)
133 trace.log("leave " + ctx.name + " " + (ctx.service || ""));
134 }
135 });
136
137 return Context;
138 }); No newline at end of file
@@ -0,0 +1,39
1 define([
2 "../declare"
3 ], function (declare) {
4 return declare(null, {
5 activationStack: null,
6
7 service: null,
8
9 innerException: null,
10
11 message: null,
12
13 constructor: function (service, activationStack, innerException) {
14 this.message = "Failed to activate the service";
15 this.activationStack = activationStack;
16 this.service = service;
17 this.innerException = innerException;
18 },
19
20 toString: function () {
21 var parts = [this.message];
22 if (this.service)
23 parts.push("when activating: " + this.service.toString());
24
25 if (this.innerException)
26 parts.push("caused by: " + this.innerException.toString());
27
28 if (this.activationStack) {
29 parts.push("at");
30 this.activationStack.forEach(function (x) {
31 parts.push(" " + x.name + " " +
32 (x.service ? x.service.toString() : ""));
33 });
34 }
35
36 return parts.join("\n");
37 }
38 });
39 }); No newline at end of file
@@ -0,0 +1,299
1 define([
2 "../declare",
3 "../safe",
4 "../Uuid",
5 "../Deferred",
6 "./ActivationContext",
7 "./Descriptor",
8 "./ValueDescriptor",
9 "./ReferenceDescriptor",
10 "./ServiceDescriptor",
11 "./ActivationError"
12 ], function (
13 declare,
14 safe,
15 Uuid,
16 Deferred,
17 ActivationContext,
18 Descriptor,
19 Value,
20 Reference,
21 Service,
22 ActivationError) {
23 var Container = declare(null, {
24 _services: null,
25 _cache: null,
26 _cleanup: null,
27 _root: null,
28 _parent: null,
29
30 constructor: function (parent) {
31 this._parent = parent;
32 this._services = parent ? Object.create(parent._services) : {};
33 this._cache = {};
34 this._cleanup = [];
35 this._root = parent ? parent.getRootContainer() : this;
36 this._services.container = new Value(this, true);
37 },
38
39 getRootContainer: function () {
40 return this._root;
41 },
42
43 getParent: function () {
44 return this._parent;
45 },
46
47 /**
48 *
49 */
50 getService: function (name, def) {
51 var d = this._services[name];
52 if (!d)
53 if (arguments.length > 1)
54 return def;
55 else
56 throw new Error("Service '" + name + "' isn't found");
57 if (d.isInstanceCreated())
58 return d.getInstance();
59
60 var context = new ActivationContext(this, this._services);
61
62 try {
63 return d.activate(context, name);
64 } catch (error) {
65 throw new ActivationError(name, context.getStack(), error);
66 }
67 },
68
69 register: function (name, service) {
70 if (arguments.length == 1) {
71 var data = name;
72 for (name in data)
73 this.register(name, data[name]);
74 } else {
75 if (!(service instanceof Descriptor))
76 service = new Value(service, true);
77 this._services[name] = service;
78 }
79 return this;
80 },
81
82 onDispose: function (callback) {
83 if (!(callback instanceof Function))
84 throw new Error("The callback must be a function");
85 this._cleanup.push(callback);
86 },
87
88 dispose: function () {
89 if (this._cleanup) {
90 for (var i = 0; i < this._cleanup.length; i++)
91 this._cleanup[i].call(null);
92 this._cleanup = null;
93 }
94 },
95
96 /**
97 * @param{String|Object} config
98 * The configuration of the contaier. Can be either a string or an object,
99 * if the configuration is an object it's treated as a collection of
100 * services which will be registed in the contaier.
101 *
102 * @param{Function} opts.contextRequire
103 * The function which will be used to load a configuration or types for services.
104 *
105 */
106 configure: function (config, opts) {
107 var p, me = this,
108 contextRequire = (opts && opts.contextRequire);
109
110 if (typeof (config) === "string") {
111 p = new Deferred();
112 if (!contextRequire) {
113 var shim = [config, new Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
114 define(shim, ["require", config], function (ctx, data) {
115 p.resolve([data, {
116 contextRequire: ctx
117 }]);
118 });
119 require([shim]);
120 } else {
121 // TODO how to get correct contextRequire for the relative config module?
122 contextRequire([config], function (data) {
123 p.resolve([data, {
124 contextRequire: contextRequire
125 }]);
126 });
127 }
128
129 return p.then(function (args) {
130 return me._configure.apply(me, args);
131 });
132 } else {
133 return me._configure(config, opts);
134 }
135 },
136
137 createChildContainer: function () {
138 return new Container(this);
139 },
140
141 has: function (id) {
142 return id in this._cache;
143 },
144
145 get: function (id) {
146 return this._cache[id];
147 },
148
149 store: function (id, value) {
150 return (this._cache[id] = value);
151 },
152
153 _configure: function (data, opts) {
154 var typemap = {},
155 d = new Deferred(),
156 me = this,
157 p,
158 contextRequire = (opts && opts.contextRequire) || require;
159
160 var services = {};
161
162 for (p in data) {
163 var service = me._parse(data[p], typemap);
164 if (!(service instanceof Descriptor))
165 service = new Value(service, false);
166 services[p] = service;
167 }
168
169 me.register(services);
170
171 var names = [];
172
173 for (p in typemap)
174 names.push(p);
175
176 if (names.length) {
177 contextRequire(names, function () {
178 for (var i = 0; i < names.length; i++)
179 typemap[names[i]] = arguments[i];
180 d.resolve(me);
181 });
182 } else {
183 d.resolve(me);
184 }
185 return d.promise;
186 },
187
188 _parse: function (data, typemap) {
189 if (safe.isPrimitive(data) || data instanceof Descriptor)
190 return data;
191 if (data.$dependency)
192 return new Reference(
193 data.$dependency,
194 data.lazy,
195 data.optional,
196 data["default"],
197 data.services && this._parseObject(data.services, typemap));
198 if (data.$value) {
199 var raw = !data.parse;
200 return new Value(raw ? data.$value : this._parse(
201 data.$value,
202 typemap), raw);
203 }
204 if (data.$type || data.$factory)
205 return this._parseService(data, typemap);
206 if (data instanceof Array)
207 return this._parseArray(data, typemap);
208
209 return this._parseObject(data, typemap);
210 },
211
212 _parseService: function (data, typemap) {
213 var me = this,
214 opts = {
215 owner: this
216 };
217 if (data.$type) {
218
219 opts.type = data.$type;
220
221 if (typeof (data.$type) === "string") {
222 typemap[data.$type] = null;
223 opts.typeMap = typemap;
224 }
225 }
226
227 if (data.$factory)
228 opts.factory = data.$factory;
229
230 if (data.services)
231 opts.services = me._parseObject(data.services, typemap);
232 if (data.inject)
233 opts.inject = data.inject instanceof Array ? data.inject.map(function (x) {
234 return me._parseObject(x, typemap);
235 }) : me._parseObject(data.inject, typemap);
236 if (data.params)
237 opts.params = me._parse(data.params, typemap);
238
239 if (data.activation) {
240 if (typeof (data.activation) === "string") {
241 switch (data.activation.toLowerCase()) {
242 case "singleton":
243 opts.activation = Service.SINGLETON;
244 break;
245 case "container":
246 opts.activation = Service.CONTAINER;
247 break;
248 case "hierarchy":
249 opts.activation = Service.HIERARCHY;
250 break;
251 case "context":
252 opts.activation = Service.CONTEXT;
253 break;
254 case "call":
255 opts.activation = Service.CALL;
256 break;
257 default:
258 throw new Error("Unknown activation type: " +
259 data.activation);
260 }
261 } else {
262 opts.activation = Number(data.activation);
263 }
264 }
265
266 if (data.cleanup)
267 opts.cleanup = data.cleanup;
268
269 return new Service(opts);
270 },
271
272 _parseObject: function (data, typemap) {
273 if (data.constructor &&
274 data.constructor.prototype !== Object.prototype)
275 return new Value(data, true);
276
277 var o = {};
278
279 for (var p in data)
280 o[p] = this._parse(data[p], typemap);
281
282 return o;
283 },
284
285 _parseArray: function (data, typemap) {
286 if (data.constructor &&
287 data.constructor.prototype !== Array.prototype)
288 return new Value(data, true);
289
290 var me = this;
291 return data.map(function (x) {
292 return me._parse(x, typemap);
293 });
294 }
295
296 });
297
298 return Container;
299 }); No newline at end of file
@@ -0,0 +1,4
1 define([], function() {
2 // abstract base type for descriptros
3 return function() {};
4 }); No newline at end of file
@@ -0,0 +1,90
1 define([
2 "../declare", "../safe", "./Descriptor", "./ActivationError", "./ValueDescriptor"
3 ],
4
5 function(declare, safe, Descriptor, ActivationError, Value) {
6 return declare(Descriptor, {
7 _name : null,
8 _lazy : false,
9 _optional : false,
10 _default : undefined,
11
12 constructor : function(name, lazy, optional, def, services) {
13 safe.argumentNotEmptyString(name, "name");
14 this._name = name;
15 this._lazy = Boolean(lazy);
16 this._optional = Boolean(optional);
17 this._default = def;
18 this._services = services;
19 },
20
21 activate : function(context, name) {
22 var me = this;
23
24 context.enter(name, this, true);
25
26 // добавляем сервисы
27 if (me._services) {
28 for ( var p in me._services) {
29 var sv = me._services[p];
30 context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false));
31 }
32 }
33
34 if (me._lazy) {
35 // сохраняем контекст активации
36 context = context.clone();
37 return function(cfg) {
38 // защищаем контекст на случай исключения в процессе
39 // активации
40 var ct = context.clone();
41 try {
42 if (cfg)
43 safe.each(cfg, function(v, k) {
44 ct.register(k, v instanceof Descriptor ? v : new Value(v, false));
45 });
46 return me._optional ? ct.getService(me._name, me._default) : ct
47 .getService(me._name);
48 } catch (error) {
49 throw new ActivationError(me._name, ct.getStack(), error);
50 }
51 };
52 }
53
54 var v = me._optional ? context.getService(me._name, me._default) : context
55 .getService(me._name);
56 context.leave(me);
57 return v;
58 },
59
60 isInstanceCreated : function() {
61 return false;
62 },
63
64 toString : function() {
65 var opts = [];
66 if (this._optional)
67 opts.push("optional");
68 if (this._lazy)
69 opts.push("lazy");
70
71 var parts = [
72 "@ref "
73 ];
74 if (opts.length) {
75 parts.push("{");
76 parts.push(opts.join());
77 parts.push("} ");
78 }
79
80 parts.push(this._name);
81
82 if (!safe.isNull(this._default)) {
83 parts.push(" = ");
84 parts.push(this._default);
85 }
86
87 return parts.join("");
88 }
89 });
90 }); No newline at end of file
@@ -0,0 +1,289
1 define(
2 [
3 "../declare",
4 "../safe",
5 "./Descriptor",
6 "./ValueDescriptor"
7 ],
8
9 function (declare, safe, Descriptor, Value) {
10 var SINGLETON_ACTIVATION = 1,
11 CONTAINER_ACTIVATION = 2,
12 CONTEXT_ACTIVATION = 3,
13 CALL_ACTIVATION = 4,
14 HIERARCHY_ACTIVATION = 5;
15
16 var injectMethod = function (target, method, context, args) {
17 var m = target[method];
18 if (!m)
19 throw new Error("Method '" + method + "' not found");
20
21 if (args instanceof Array)
22 m.apply(target, context.parse(args, "." + method));
23 else
24 m.call(target, context.parse(args, "." + method));
25 };
26
27 var makeClenupCallback = function (target, method) {
28 if (typeof (method) === "string") {
29 return function () {
30 target[method]();
31 };
32 } else {
33 return function () {
34 method(target);
35 };
36 }
37 };
38
39 var cacheId = 0;
40
41 var cls = declare(
42 Descriptor, {
43 _instance: null,
44 _hasInstance: false,
45 _activationType: CALL_ACTIVATION,
46 _services: null,
47 _type: null,
48 _typeMap: null,
49 _factory: null,
50 _params: undefined,
51 _inject: null,
52 _cleanup: null,
53 _cacheId: null,
54 _owner: null,
55
56 constructor: function (opts) {
57 safe.argumentNotNull(opts, "opts");
58 safe.argumentNotNull(opts.owner, "opts.owner");
59
60 this._owner = opts.owner;
61
62 if (!(opts.type || opts.factory))
63 throw new Error(
64 "Either a type or a factory must be specified");
65
66 if (typeof (opts.type) === "string" && !opts.typeMap)
67 throw new Error(
68 "The typeMap is required when the type is specified by its name");
69
70 if (opts.activation)
71 this._activationType = opts.activation;
72 if (opts.type)
73 this._type = opts.type;
74 if (opts.params)
75 this._params = opts.params;
76 if (opts.inject)
77 this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject];
78 if (opts.services)
79 this._services = opts.services;
80 if (opts.factory)
81 this._factory = opts.factory;
82 if (opts.typeMap)
83 this._typeMap = opts.typeMap;
84 if (opts.cleanup) {
85 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
86 throw new Error(
87 "The cleanup parameter must be either a function or a function name");
88
89 this._cleanup = opts.cleanup;
90 }
91
92 this._cacheId = ++cacheId;
93 },
94
95 activate: function (context, name) {
96
97 // if we have a local service records, register them first
98
99 var instance;
100
101 switch (this._activationType) {
102 case 1: // SINGLETON
103 // if the value is cached return it
104 if (this._hasInstance)
105 return this._instance;
106
107 var tof = this._type || this._factory;
108
109 // create the persistent cache identifier for the type
110 if (safe.isPrimitive(tof))
111 this._cacheId = this._type;
112 else
113 this._cacheId = safe.oid(tof);
114
115 // singletons are bound to the root container
116 var container = context.container.getRootContainer();
117
118 if (container.has(this._cacheId)) {
119 instance = container.get(this._cacheId);
120 } else {
121 instance = this._create(context, name);
122 container.store(this._cacheId, instance);
123 if (this._cleanup)
124 container.onDispose(
125 makeClenupCallback(instance, this._cleanup));
126 }
127
128 this._hasInstance = true;
129 return (this._instance = instance);
130
131 case 2: // CONTAINER
132 //return a cached value
133 if (this._hasInstance)
134 return this._instance;
135
136 // create an instance
137 instance = this._create(context, name);
138
139 // the instance is bound to the container
140 if (this._cleanup)
141 this._owner.onDispose(
142 makeClenupCallback(instance, this._cleanup));
143
144 // cache and return the instance
145 this._hasInstance = true;
146 return (this._instance = instance);
147 case 3: // CONTEXT
148 //return a cached value if one exists
149 if (context.has(this._cacheId))
150 return context.get(this._cacheId);
151 // context context activated instances are controlled by callers
152 return context.store(this._cacheId, this._create(
153 context,
154 name));
155 case 4: // CALL
156 // per-call created instances are controlled by callers
157 return this._create(context, name);
158 case 5: // HIERARCHY
159 // hierarchy activated instances are behave much like container activated
160 // except they are created and bound to the child container
161
162 // return a cached value
163 if (context.container.has(this._cacheId))
164 return context.container.get(this._cacheId);
165
166 instance = this._create(context, name);
167
168 if (this._cleanup)
169 context.container.onDispose(makeClenupCallback(
170 instance,
171 this._cleanup));
172
173 return context.container.store(this._cacheId, instance);
174 default:
175 throw "Invalid activation type: " + this._activationType;
176 }
177 },
178
179 isInstanceCreated: function () {
180 return this._hasInstance;
181 },
182
183 getInstance: function () {
184 return this._instance;
185 },
186
187 _create: function (context, name) {
188 context.enter(name, this, Boolean(this._services));
189
190 if (this._activationType != CALL_ACTIVATION &&
191 context.visit(this._cacheId) > 0)
192 throw new Error("Recursion detected");
193
194 if (this._services) {
195 for (var p in this._services) {
196 var sv = this._services[p];
197 context.register(p, sv instanceof Descriptor ? sv : new Value(sv, false));
198 }
199 }
200
201 var instance;
202
203 if (!this._factory) {
204 var ctor, type = this._type;
205
206 if (typeof (type) === "string") {
207 ctor = this._typeMap[type];
208 if (!ctor)
209 throw new Error("Failed to resolve the type '" +
210 type + "'");
211 } else {
212 ctor = type;
213 }
214
215 if (this._params === undefined) {
216 this._factory = function () {
217 return new ctor();
218 };
219 } else if (this._params instanceof Array) {
220 this._factory = function () {
221 var inst = Object.create(ctor.prototype);
222 var ret = ctor.apply(inst, arguments);
223 return typeof (ret) === "object" ? ret : inst;
224 };
225 } else {
226 this._factory = function (param) {
227 return new ctor(param);
228 };
229 }
230 }
231
232 if (this._params === undefined) {
233 instance = this._factory();
234 } else if (this._params instanceof Array) {
235 instance = this._factory.apply(this, context.parse(
236 this._params,
237 ".params"));
238 } else {
239 instance = this._factory(context.parse(
240 this._params,
241 ".params"));
242 }
243
244 if (this._inject) {
245 this._inject.forEach(function (spec) {
246 for (var m in spec)
247 injectMethod(instance, m, context, spec[m]);
248 });
249 }
250
251 context.leave();
252
253 return instance;
254 },
255
256 // @constructor {singleton} foo/bar/Baz
257 // @factory {singleton}
258 toString: function () {
259 var parts = [];
260
261 parts.push(this._type ? "@constructor" : "@factory");
262
263 parts.push(activationNames[this._activationType]);
264
265 if (typeof (this._type) === "string")
266 parts.push(this._type);
267
268 return parts.join(" ");
269 }
270
271 });
272
273 cls.SINGLETON = SINGLETON_ACTIVATION;
274 cls.CONTAINER = CONTAINER_ACTIVATION;
275 cls.CONTEXT = CONTEXT_ACTIVATION;
276 cls.CALL = CALL_ACTIVATION;
277 cls.HIERARCHY = HIERARCHY_ACTIVATION;
278
279 var activationNames = [
280 "",
281 "{singleton}",
282 "{container}",
283 "{context}",
284 "{call}",
285 "{hierarchy}"
286 ];
287
288 return cls;
289 }); No newline at end of file
@@ -0,0 +1,38
1 define([ "../declare", "./Descriptor", "../safe" ],
2
3 function(declare, Descriptor, safe) {
4 return declare(Descriptor, {
5 _value : undefined,
6 _raw : false,
7 constructor : function(value, raw) {
8 this._value = value;
9 this._raw = Boolean(raw);
10 },
11
12 activate : function(context, name) {
13 context.enter(name, this);
14 var v = this._raw ? this._value : context.parse(
15 this._value,
16 ".params");
17 context.leave(this);
18 return v;
19 },
20
21 isInstanceCreated : function() {
22 return this._raw;
23 },
24
25 getInstance : function() {
26 if (!this._raw)
27 throw new Error("The instance isn't constructed");
28 return this._value;
29 },
30
31 toString : function() {
32 if (this._raw)
33 return "@value {raw}";
34 else
35 return safe.isNull(this._value) ? "@value <null>" : "@value";
36 }
37 });
38 }); No newline at end of file
@@ -0,0 +1,30
1 define(
2 [ "dojo/_base/declare", "../text/format" ],
3 function(declare, format) {
4 return declare(
5 null,
6 {
7 name : null,
8
9 constructor : function(name) {
10 this.name = name;
11 },
12
13 log : function() {
14 console.log(this._makeMsg(arguments));
15 },
16
17 warn : function() {
18 console.warn(this._makeMsg(arguments));
19 },
20
21 error : function() {
22 console.error(this._makeMsg(arguments));
23 },
24
25 _makeMsg : function(args) {
26 return this.name ? this.name + " " +
27 format.apply(null, args) : format.apply(null, args);
28 }
29 });
30 }); No newline at end of file
@@ -0,0 +1,67
1 define([ "dojo/_base/declare" ],
2
3 function(declare) {
4 var cls = declare(null, {
5 _logChannel : null,
6
7 _logLevel : 1,
8
9 constructor : function(opts) {
10 if (typeof opts == "object") {
11 if ("logChannel" in opts)
12 this._logChannel = opts.logChannel;
13 if ("logLevel" in opts)
14 this._logLevel = opts.logLevel;
15 }
16 },
17
18 getLogChannel : function() {
19 return this._logChannel;
20 },
21
22 setLogChannel : function(v) {
23 this._logChannel = v;
24 },
25
26 getLogLevel : function() {
27 return this._logLevel;
28 },
29
30 setLogLevel : function(v) {
31 this._logLevel = v;
32 },
33
34 log : function(format) {
35 if (this._logChannel && this._logLevel > 2)
36 this._logChannel.log.apply(this._logChannel, arguments);
37 },
38 warn : function(format) {
39 if (this._logChannel && this._logLevel > 1)
40 this._logChannel.warn.apply(this._logChannel, arguments);
41 },
42 error : function(format) {
43 if (this._logChannel && this._logLevel > 0)
44 this._logChannel.error.apply(this._logChannel, arguments);
45 },
46
47 /**
48 * Used to by widgets
49 */
50 startup : function() {
51 var me = this, parent;
52 if (!me.getLogChannel()) {
53 parent = me;
54 while (parent = parent.getParent()) {
55 if (parent.getLogChannel) {
56 me.setLogChannel(parent.getLogChannel());
57 if(parent.getLogLevel)
58 me.setLogLevel(parent.getLogLevel());
59 break;
60 }
61 }
62 }
63 this.inherited(arguments);
64 }
65 });
66 return cls;
67 }); No newline at end of file
@@ -0,0 +1,25
1 define([], function () {
2 if (console && console.log)
3 return function (ch, name, msg) {
4
5 var args = [ch + ":"];
6
7 switch (name) {
8 case "warn":
9 case "error":
10 case "log":
11 break;
12 default:
13 args.push(name + ":");
14 name = "log";
15 }
16
17
18 if (msg instanceof Array)
19 args.push.apply(args, msg);
20 else
21 args.push(msg);
22
23 console[name].apply(console, args);
24 };
25 }); No newline at end of file
@@ -0,0 +1,116
1 define(["../text/format"], function (format) {
2 'use strict';
3
4 var listeners = [];
5 var channels = {};
6
7 var Trace = function (name) {
8 this.name = name;
9 this._subscribers = [];
10 };
11
12 Trace.prototype.debug = function () {
13 if (Trace.level >= 4)
14 this.notify("debug", format.apply(null, arguments));
15 };
16
17 Trace.prototype.log = function () {
18 if (Trace.level >= 3)
19 this.notify("log", format.apply(null, arguments));
20 };
21
22 Trace.prototype.warn = function () {
23 if (Trace.level >= 2)
24 this.notify("warn", format.apply(null, arguments));
25
26 };
27
28 Trace.prototype.error = function () {
29 if (Trace.level >= 1)
30 this.notify("error", format.apply(null, arguments));
31 };
32
33 Trace.prototype.notify = function (name, msg) {
34 var me = this;
35 me._subscribers.forEach(function (cb) {
36 cb(me, name, msg);
37 });
38 };
39
40 Trace.prototype.subscribe = function (cb) {
41 this._subscribers.push(cb);
42 };
43
44 Trace.prototype.toString = function () {
45 return this.name;
46 };
47
48 Trace.createChannel = function (type, name, cb) {
49 var chId = name;
50 if (channels[chId])
51 return channels[chId];
52
53 var channel = new type(chId);
54 channels[chId] = channel;
55
56 Trace._onNewChannel(chId, channel);
57 cb(channel);
58 };
59
60 Trace._onNewChannel = function (chId, ch) {
61 listeners.forEach(function (listener) {
62 listener(chId, ch);
63 });
64 };
65
66 Trace.on = function (filter, cb) {
67 if (arguments.length == 1) {
68 cb = filter;
69 filter = undefined;
70 }
71 var d, test;
72 if (filter instanceof RegExp) {
73 test = function (chId) {
74 return filter.test(chId);
75 };
76 } else if (filter instanceof Function) {
77 test = filter;
78 } else if (filter) {
79 test = function (chId) {
80 return chId == filter;
81 };
82 }
83
84 if (test) {
85 d = function(chId, ch) {
86 if(test(chId))
87 ch.subscribe(cb);
88 };
89 } else {
90 d = function(chId, ch) {
91 ch.subscribe(cb);
92 };
93 }
94 listeners.push(d);
95
96 for(var chId in channels)
97 d(chId,channels[chId]);
98 };
99
100 Trace.load = function (id, require, cb) {
101 if (id)
102 Trace.createChannel(Trace, id, cb);
103 else if (require.module && require.module.mid)
104 Trace.createChannel(Trace, require.module.mid, cb);
105 else
106 require(['module'], function (module) {
107 Trace.createChannel(Trace, module && module.id, cb);
108 });
109 };
110
111 Trace.dynamic = true;
112
113 Trace.level = 4;
114
115 return Trace;
116 }); No newline at end of file
@@ -0,0 +1,3
1 declare([], function(){
2 // does nothing yet...
3 }); No newline at end of file
@@ -0,0 +1,61
1 define(
2 [ "dojo/_base/declare", "dojo/_base/lang", "dojo/Evented", "../log/_LogMixin" ],
3
4 function(declare, lang, Evented, _LogMixin) {
5 return declare([ Evented, _LogMixin ], {
6 _session : null,
7 _destination : null,
8 _id : null,
9
10 constructor : function(session, destination, options) {
11 this._destination = destination;
12 this._session = session;
13 },
14
15 getDestination : function() {
16 return this._destination;
17 },
18
19 start : function() {
20 var me = this;
21 return me._session.createClient(me.prepareOptions({})).then(
22 function(id) {
23 me._id = id;
24 return me;
25 });
26 },
27
28 prepareOptions : function(options) {
29 var me = this;
30 options.mode = me.getMode();
31 options.destination = me.getDestination();
32 options.client = function(msg) {
33 me.process(msg);
34 };
35 return options;
36 },
37
38 process : function(msg) {
39 this.warn("Messages are not acceped by this client");
40 },
41
42 stop : function() {
43 var me = this;
44 if (me._id) {
45 me.log("stop");
46 return me._session.deleteClient({'clientId': me._id}).then(function() {
47 me._id = null;
48 return me;
49 });
50 }
51 },
52
53 toString : function() {
54 return "["
55 + [
56 this.getMode().toUpperCase(),
57 this.getDestination(),
58 this._id ].join(',') + "]";
59 }
60 });
61 }); No newline at end of file
@@ -0,0 +1,34
1 define([ "dojo/_base/declare", "./Listener" ],
2
3 function(declare, Listener) {
4 return declare(null, {
5 _session : null,
6 _destination : null,
7 _listenerClass : null,
8
9 constructor : function(session, destination, options) {
10 if (!session)
11 throw new Error("A session is required");
12 if (!destination)
13 throw new Error("A destination is required");
14
15 this._session = session;
16 this._destination = destination;
17 if (options) {
18 if (options.listenerClass)
19 this._listenerClass = options.listenerClass;
20 }
21 },
22
23 listen : function(callback) {
24 var factory = this._listenerClass || Listener;
25 var listener = new factory(this._session, this._destination, {
26 listener : callback
27 });
28 listener.start();
29
30 return listener;
31 }
32
33 });
34 });
@@ -0,0 +1,64
1 define([ "dojo/_base/declare", "dojo/_base/lang", "./Client" ],
2
3 function(declare, lang, Client) {
4 return declare([ Client ], {
5 _listener : null,
6
7 constructor : function(session, destination, options) {
8 if (!options || !options.listener)
9 throw new Error("A listener is required");
10 this._listener = options.listener;
11 if (options.transform)
12 this._transform = options.transform;
13 },
14
15 getMode : function() {
16 return "listener";
17 },
18
19 process : function(result) {
20 switch (result.type) {
21 case "message":
22 try {
23 this._handleMessage(result.message);
24 } catch (ex) {
25 var err = new Error("Failed to handle message");
26 err.envelope = result.message;
27 err.innerException = ex;
28 this._handleError(err);
29 }
30 break;
31 case "error":
32 this._handleError(result.error);
33 break;
34 }
35
36 },
37
38 _transform : function(envelope) {
39 return envelope;
40 },
41
42 _handleMessage : function(envelope) {
43 this.log(
44 "MESSAGE type = ${0}, headers = ${2}: ${1}",
45 envelope.bodyType,
46 envelope.body,
47 JSON.stringify(envelope.headers));
48 var data = this._transform(envelope);
49 this._listener(data);
50 this.emit("message", data);
51 },
52
53 _handleError : function(ex) {
54 if (ex.innerException)
55 this.error(
56 "ERROR: ${0} -> ${1}",
57 ex.message,
58 ex.innerException.message);
59 else
60 this.error("ERROR: ${0}", ex.message);
61 this.emit("error", ex);
62 }
63 });
64 }); No newline at end of file
@@ -0,0 +1,217
1 define(
2 [
3 "dojo/_base/declare",
4 "dojo/_base/lang",
5 "dojo/request",
6 "./Destination",
7 "dojo/Evented",
8 "dojo/Deferred",
9 "../log/_LogMixin" ],
10
11 function(declare, lang, request, Destination, Evented, Deferred, _LogMixin) {
12
13 var cls = declare(
14 [ Evented, _LogMixin ],
15 {
16 _id : null,
17 _baseUrl : null,
18 _destinations : null,
19 _timeout : 100000,
20 _clients : null,
21 _started : null,
22 _starting : false,
23
24 constructor : function(baseUrl, options) {
25 if (!baseUrl)
26 throw new Error("baseUrl is required");
27 options = options || {};
28
29 this._baseUrl = baseUrl.replace(/\/*$/, "");
30 this._destinations = {};
31 this._pending = [];
32 this._clients = {};
33 if (options.timeout)
34 this._timeout = options.timeout;
35
36 this._started = new Deferred();
37 },
38
39 start : function() {
40 if (this._starting)
41 return this._started;
42 this._starting = true;
43
44 var me = this;
45 me.log("START");
46 request(this._baseUrl, {
47 method : "POST",
48 handleAs : "json"
49 }).then(function(result) {
50 me._id = result;
51 me._emitConnected();
52 me._poll();
53 me._started.resolve(me);
54 }, function(error) {
55 me._emitError(error);
56 me._started.reject(me);
57 });
58 return me._started.promise;
59 },
60
61 createClient : function(options) {
62 if (!options || !options.destination || !options.mode)
63 throw new Error("Invalid argument");
64
65 var me = this;
66
67 return me._started
68 .then(function() {
69 var url = me._makeUrl(me._id);
70 me.log(
71 "CREATE mode=${0}, destination=${1}",
72 options.mode,
73 options.destination);
74
75 return request(url, {
76 method : "POST",
77 data : {
78 mode : options.mode,
79 destination : options.destination
80 },
81 handleAs : 'json'
82 })
83 .then(
84 function(id) {
85 me
86 .log(
87 "CLIENT id=${0}, mode=${1}, destination=${2}",
88 id,
89 options.mode,
90 options.destination);
91 me._clients[id] = options.client
92 ? options.client
93 : function(msg) {
94 me
95 .warn(
96 "The client id=${0}, mode=${1}, destination=${2} isn't accepting mesages",
97 id,
98 options.mode,
99 options.destination);
100 };
101 return id;
102 });
103 });
104
105 },
106
107 deleteClient : function(options) {
108 if (!options || !options.clientId)
109 throw new Error("Invalid argument");
110
111 var me = this, id = options.clientId;
112
113 return me._started.then(function() {
114 var url = me._makeUrl(me._id, options.clientId);
115
116 me.log("DELETE CLIENT ${0}", options.clientId);
117
118 return request(url, {
119 method : "DELETE",
120 handleAs : 'json'
121 }).then(function() {
122 me.log("CLIENT DELETED ${0}", options.clientId);
123 me._clients[id] = undefined;
124 });
125 });
126 },
127
128 _poll : function() {
129 var me = this, url = this._makeUrl(this._id);
130 me.log("POLL timeout=${0}", me._timeout);
131 request(url, {
132 method : "GET",
133 handleAs : "json",
134 query : {
135 timeout : me._timeout
136 }
137 }).then(function(response) {
138 me._handlePoll(response);
139 me._poll();
140 }, function(err) {
141 me.error("POLL faield with ${0}", err);
142 me._emitError(err);
143 });
144 },
145
146 _handlePoll : function(response) {
147 if (!response) {
148 this.log("POLL response undefined, looks like a bug");
149 return;
150 }
151 if (!response.results || !response.results.length) {
152 this.log("POLL response is empty");
153 return;
154 }
155
156 var results = response.results;
157 this.log("POLL got ${0} results", results.length);
158
159 for (var i = 0; i < results.length; i++) {
160 var result = results[i];
161 var client = this._clients[result.clientId];
162 if (!client) {
163 // TODO this could happen due to client isn't
164 // registered yet
165 this.error("Unknown client ${0}", result.clientId);
166 continue;
167 }
168 client.call(this, result);
169 }
170 },
171
172 _emitError : function(err) {
173 this.emit("error", err);
174 },
175
176 _emitConnected : function() {
177 var me = this;
178 me.log("CONNECTED");
179 me.emit("connected");
180 },
181
182 _makeUrl : function() {
183 var parts = [ this._baseUrl ];
184 for (var i = 0; i < arguments.length; i++)
185 parts.push(arguments[i].replace(/\/*$/, ""));
186 return parts.join('/');
187 },
188
189 queue : function(name) {
190 return this._getDestination("queue://" + name);
191 },
192
193 topic : function(name) {
194 return this._getDestination("topic://" + name);
195 },
196
197 _getDestination : function(uri) {
198 if (uri in this._destinations)
199 return this._destinations[uri];
200
201 var dest = new Destination(this, uri);
202 this._destinations[uri] = dest;
203 return dest;
204 },
205
206 toString : function() {
207 return [ "[", "SESSION ", this._id, "]" ].join(" ");
208 }
209 });
210
211 cls.connect = function(url, options) {
212 var session = new cls(url, options);
213 return session.start();
214 };
215
216 return cls;
217 });
@@ -0,0 +1,323
1 define([],
2
3 function () {
4 var _create = Object.create,
5 _keys = Object.keys;
6
7 var safe = null;
8 safe = {
9 argumentNotNull: function (arg, name) {
10 if (arg === null || arg === undefined)
11 throw new Error("The argument " + name + " can't be null or undefined");
12 },
13
14 argumentNotEmptyString: function (arg, name) {
15 if (typeof (arg) !== "string" || !arg.length)
16 throw new Error("The argument '" + name + "' must be a not empty string");
17 },
18
19 argumentNotEmptyArray: function (arg, name) {
20 if (!(arg instanceof Array) || !arg.length)
21 throw new Error("The argument '" + name + "' must be a not empty array");
22 },
23
24 argumentOfType: function (arg, type, name) {
25 if (!(arg instanceof type))
26 throw new Error("The argument '" + name + "' type doesn't match");
27 },
28
29 isNull: function (arg) {
30 return (arg === null || arg === undefined);
31 },
32
33 isPrimitive: function (arg) {
34 return (arg === null || arg === undefined || typeof (arg) === "string" ||
35 typeof (arg) === "number" || typeof (arg) === "boolean");
36 },
37
38 isInteger: function (arg) {
39 return parseInt(arg) == arg;
40 },
41
42 isNumber: function (arg) {
43 return parseFloat(arg) == arg;
44 },
45
46 isString: function (val) {
47 return typeof (val) == "string" || val instanceof String;
48 },
49
50 isNullOrEmptyString: function (str) {
51 if (str === null || str === undefined ||
52 ((typeof (str) == "string" || str instanceof String) && str.length === 0))
53 return true;
54 },
55
56 isNotEmptyArray: function (arg) {
57 return (arg instanceof Array && arg.length > 0);
58 },
59
60 /**
61 * Выполняет метод для каждого элемента массива, останавливается, когда
62 * либо достигнут конец массива, либо функция <c>cb</c> вернула
63 * значение.
64 *
65 * @param{Array | Object} obj массив элементов для просмотра
66 * @param{Function} cb функция, вызываемая для каждого элемента
67 * @param{Object} thisArg значение, которое будет передано в качестве
68 * <c>this</c> в <c>cb</c>.
69 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
70 * если достигнут конец массива.
71 */
72 each: function (obj, cb, thisArg) {
73 safe.argumentNotNull(cb, "cb");
74 var i, x;
75 if (obj instanceof Array) {
76 for (i = 0; i < obj.length; i++) {
77 x = cb.call(thisArg, obj[i], i);
78 if (x !== undefined)
79 return x;
80 }
81 } else {
82 var keys = _keys(obj);
83 for (i = 0; i < keys.length; i++) {
84 var k = keys[i];
85 x = cb.call(thisArg, obj[k], k);
86 if (x !== undefined)
87 return x;
88 }
89 }
90 },
91
92 /**
93 * Копирует свойства одного объекта в другой.
94 *
95 * @param{Any} dest объект в который нужно скопировать значения
96 * @param{Any} src источник из которого будут копироваться значения
97 * @tmpl{Object|Array} tmpl шаблон по которому будет происходить
98 * копирование. Если шаблон является массивом
99 * (список свойств), тогда значения этого массива
100 * являются именами свойсвт которые будут
101 * скопированы. Если шаблон является объектом (карта
102 * преобразования имен свойств src->dst), тогда
103 * копирование будет осуществляться только
104 * собственных свойств источника, присутсвующих в
105 * шаблоне, при этом значение свойства шаблона
106 * является именем свойства в которое будет
107 * произведено коприрование
108 */
109 mixin: function (dest, src, tmpl) {
110 safe.argumentNotNull(dest, "dest");
111 if (!src)
112 return dest;
113
114 var keys, i, p;
115 if (arguments.length < 3) {
116 keys = _keys(src);
117 for (i = 0; i < keys.length; i++) {
118 p = keys[i];
119 dest[p] = src[p];
120 }
121 } else {
122 if (tmpl instanceof Array) {
123 for (i = 0; i < tmpl.length; i++) {
124 p = tmpl[i];
125 if (p in src)
126 dest[p] = src[p];
127 }
128
129 } else {
130 keys = _keys(src);
131 for (i = 0; i < keys.length; i++) {
132 p = keys[i];
133 if (p in tmpl)
134 dest[tmpl[p]] = src[p];
135 }
136 }
137 }
138 return dest;
139 },
140
141 /** Wraps the specified function to emulate an asynchronous execution.
142 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
143 * @param{Function|String} fn [Required] Function wich will be wrapped.
144 */
145 async: function (fn, thisArg) {
146 if (arguments.length == 2 && !(fn instanceof Function))
147 fn = thisArg[fn];
148
149 if (fn == null)
150 throw new Error("The function must be specified");
151
152 function wrapresult(x, e) {
153 if (e) {
154 return {
155 then: function (cb, eb) {
156 try {
157 return eb ? wrapresult(eb(e)) : this;
158 } catch (e2) {
159 return wrapresult(null, e2);
160 }
161 }
162 };
163 } else {
164 if (x && x.then)
165 return x;
166 return {
167 then : function(cb) {
168 try {
169 return cb ? wrapresult(cb(x)) : this;
170 } catch(e2) {
171 return wrapresult(e2);
172 }
173 }
174 };
175 }
176 }
177
178 try {
179 return wrapresult(fn.apply(thisArg, arguments));
180 } catch (e) {
181 return wrapresult(null, e);
182 };
183 },
184
185 create: function () {
186 if (console && console.warn)
187 console.warn("implab/safe::create is deprecated use Object.create instead");
188 _create.apply(this, arguments);
189 },
190
191 delegate: function (target, method) {
192 if (!(method instanceof Function)) {
193 this.argumentNotNull(target, "target");
194 method = target[method];
195 }
196
197 if (!(method instanceof Function))
198 throw new Error("'method' argument must be a Function or a method name");
199
200 return function () {
201 return method.apply(target, arguments);
202 };
203 },
204
205 /**
206 * Для каждого элемента массива вызывает указанную функцию и сохраняет
207 * возвращенное значение в массиве результатов.
208 *
209 * @remarks cb может выполняться асинхронно, при этом одновременно будет
210 * только одна операция.
211 *
212 * @async
213 */
214 pmap: function (items, cb) {
215 safe.argumentNotNull(cb, "cb");
216
217 if (items && items.then instanceof Function)
218 return items.then(function (data) {
219 return safe.pmap(data, cb);
220 });
221
222 if (safe.isNull(items) || !items.length)
223 return items;
224
225 var i = 0,
226 result = [];
227
228 function next() {
229 var r, ri;
230
231 function chain(x) {
232 result[ri] = x;
233 return next();
234 }
235
236 while (i < items.length) {
237 r = cb(items[i], i);
238 ri = i;
239 i++;
240 if (r && r.then) {
241 return r.then(chain);
242 } else {
243 result[ri] = r;
244 }
245 }
246 return result;
247 }
248
249 return next();
250 },
251
252 /**
253 * Для каждого элемента массива вызывает указанную функцию, результаты
254 * не сохраняются
255 *
256 * @remarks cb может выполняться асинхронно, при этом одновременно будет
257 * только одна операция.
258 * @async
259 */
260 pfor: function (items, cb) {
261 safe.argumentNotNull(cb, "cb");
262
263 if (items && items.then instanceof Function)
264 return items.then(function (data) {
265 return safe.pmap(data, cb);
266 });
267
268 if (safe.isNull(items) || !items.length)
269 return items;
270
271 var i = 0;
272
273 function next() {
274 while (i < items.length) {
275 var r = cb(items[i], i);
276 i++;
277 if (r && r.then)
278 return r.then(next);
279 }
280 }
281
282 return next();
283 },
284
285 /**
286 * Выбирает первый элемент из последовательности, или обещания, если в
287 * качестве параметра используется обещание, оно должно вернуть массив.
288 *
289 * @param{Function} cb обработчик результата, ему будет передан первый
290 * элемент последовательности в случае успеха
291 * @param{Fucntion} err обработчик исключения, если массив пустой, либо
292 * не массив
293 *
294 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
295 * обещание, либо первый элемент.
296 * @async
297 */
298 first: function (sequence, cb, err) {
299 if (sequence) {
300 if (sequence.then instanceof Function) {
301 return sequence.then(function (res) {
302 return safe.first(res, cb, err);
303 }, err);
304 } else if (sequence && "length" in sequence) {
305 if (sequence.length === 0) {
306 if (err)
307 return err(new Error("The sequence is empty"));
308 else
309 throw new Error("The sequence is empty");
310 }
311 return cb ? cb(sequence[0]) : sequence[0];
312 }
313 }
314
315 if (err)
316 return err(new Error("The sequence is required"));
317 else
318 throw new Error("The sequence is required");
319 }
320 };
321
322 return safe;
323 }); No newline at end of file
@@ -0,0 +1,101
1 define(
2 [],
3 function() {
4 var map = {
5 "\\{" : "&curlopen;",
6 "\\}" : "&curlclose;",
7 "&" : "&amp;",
8 "\\:" : "&colon;"
9 };
10
11 var rev = {
12 curlopen : "{",
13 curlclose : "}",
14 amp : "&",
15 colon : ":"
16 };
17
18 var espaceString = function(s) {
19 if (!s)
20 return s;
21 return "'" + s.replace(/('|\\)/g, "\\$1") + "'";
22 };
23
24 var encode = function(s) {
25 if (!s)
26 return s;
27 return s.replace(/\\{|\\}|&|\\:/g, function(m) {
28 return map[m] || m;
29 });
30 };
31
32 var decode = function(s) {
33 if (!s)
34 return s;
35 return s.replace(/&(\w+);/g, function(m, $1) {
36 return rev[$1] || m;
37 });
38 };
39
40 var subst = function(s) {
41 var i = s.indexOf(":"), name, pattern;
42 if (i >= 0) {
43 name = s.substr(0, i);
44 pattern = s.substr(i + 1);
45 } else {
46 name = s;
47 }
48
49 if (pattern)
50 return [
51 espaceString(decode(name)),
52 espaceString(decode(pattern)) ];
53 else
54 return [ espaceString(decode(name)) ];
55 };
56
57 var compile = function(str) {
58 if (!str)
59 return function() {};
60
61 var chunks = encode(str).split("{"), chunk;
62
63 var code = [ "var result=[];" ];
64
65 for (var i = 0; i < chunks.length; i++) {
66 chunk = chunks[i];
67
68 if (i === 0) {
69 if (chunk)
70 code.push("result.push(" + espaceString(decode(chunk)) +
71 ");");
72 } else {
73 var len = chunk.indexOf("}");
74 if (len < 0)
75 throw new Error("Unbalanced substitution #" + i);
76
77 code.push("result.push(subst(" +
78 subst(chunk.substr(0, len)).join(",") + "));");
79 if (chunk.length > len + 1)
80 code.push("result.push(" +
81 espaceString(decode(chunk.substr(len + 1))) + ");");
82 }
83 }
84
85 code.push("return result.join('');");
86
87 /* jshint -W054 */
88 return new Function("subst", code.join("\n"));
89 };
90
91 var cache = {};
92
93 return function(template) {
94 var compiled = cache[template];
95 if (!compiled) {
96 compiled = compile(template);
97 cache[template] = compiled;
98 }
99 return compiled;
100 };
101 }); No newline at end of file
@@ -0,0 +1,87
1 define([
2 "../safe",
3 "./format-compile",
4 "dojo/number",
5 "dojo/date/locale",
6 "dojo/_base/array" ], function(safe, compile, number, date, array) {
7
8 // {short,medium,full,long}-{date,time}
9 var convert = function(value, pattern) {
10 if (!pattern)
11 return value.toString();
12
13 if (pattern.toLocaleLowerCase() == "json") {
14 var cache = [];
15 return JSON.stringify(value, function(k, v) {
16 if (!safe.isPrimitive(v)) {
17 var id = array.indexOf(cache, v);
18 if (id >= 0)
19 return "@ref-" + id;
20 else
21 return v;
22 } else {
23 return v;
24 }
25 },2);
26 }
27
28 if (safe.isNumber(value)) {
29 var nopt = {};
30 if (pattern.indexOf("!") === 0) {
31 nopt.round = -1;
32 pattern = pattern.substr(1);
33 }
34 nopt.pattern = pattern;
35 return number.format(value, nopt);
36 } else if (value instanceof Date) {
37 var m = pattern.match(/^(\w+)-(\w+)$/);
38 if (m)
39 return date.format(value, {
40 selector : m[2],
41 formatLength : m[1]
42 });
43 else if (pattern == "iso")
44 return value.toISOString();
45 else
46 return date.format(value, {
47 selector : "date",
48 datePattern : pattern
49 });
50 } else {
51 return value.toString(pattern);
52 }
53 };
54
55 function formatter(format) {
56 var data;
57
58 if (arguments.length <= 1)
59 return format;
60
61 data = Array.prototype.slice.call(arguments, 1);
62
63 var template = compile(format);
64
65 return template(function(name, pattern) {
66 var value = data[name];
67 return !safe.isNull(value) ? convert(value, pattern) : "";
68 });
69 }
70
71 formatter.compile = function(format) {
72 var template = compile(format);
73
74 return function() {
75 var data = arguments;
76
77 return template(function(name, pattern) {
78 var value = data[name];
79 return !safe.isNull(value) ? convert(value, pattern) : "";
80 });
81 };
82 };
83
84 formatter.convert = convert;
85
86 return formatter;
87 }); No newline at end of file
@@ -0,0 +1,134
1 define(
2 ["dojo/request", "./format", "../log/trace!"],
3 function (request, format, trace) {
4
5 // разбивает строку шаблона на токены, возвращает контекст для
6 // дальнейшей обработки в visitTemplate
7 var parseTemplate = function (str) {
8 var tokens = str.split(/(<%=|\[%=|<%|\[%|%\]|%>)/);
9 var pos = -1;
10 var data = [],
11 code = [];
12
13 return {
14 next: function () {
15 pos++;
16 return pos < tokens.length;
17 },
18 token: function () {
19 return tokens[pos];
20 },
21 pushData: function () {
22 var i = data.length;
23 data.push.apply(data, arguments);
24 return i;
25 },
26 pushCode : function() {
27 var i = code.length;
28 code.push.apply(code, arguments);
29 return i;
30 },
31 compile: function () {
32 var text = "var $p = [];\n" +
33 "var print = function(){\n" +
34 " $p.push(format.apply(null,arguments));\n" +
35 "};\n" +
36 // Introduce the data as local variables using with(){}
37 "with(obj){\n" +
38 code.join("\n") +
39 "}\n" +
40 "return $p.join('');";
41
42 try {
43 var compiled = new Function("obj, format, $data", text);
44 /**
45 * Функция форматирования по шаблону
46 *
47 * @type{Function}
48 * @param{Object} obj объект с параметрами для подстановки
49 */
50 return function (obj) {
51 return compiled(obj || {}, format, data);
52 };
53 } catch (e) {
54 trace.error([e]);
55 trace.log([text, data]);
56 throw e;
57 }
58 }
59 }
60 };
61
62 function visitTemplate(context) {
63 while (context.next()) {
64 switch (context.token()) {
65 case "<%":
66 case "[%":
67 visitCode(context);
68 break;
69 case "<%=":
70 case "[%=":
71 visitInline(context);
72 break;
73 default:
74 visitTextFragment(context);
75 break;
76 }
77 }
78 }
79
80 function visitInline(context) {
81 var code = ["$p.push("];
82 while (context.next()) {
83 if (context.token() == "%>" || context.token() == "%]")
84 break;
85 code.push(context.token());
86 }
87 code.push(");");
88 context.pushCode(code.join(''));
89 }
90
91 function visitCode(context) {
92 var code = [];
93 while (context.next()) {
94 if (context.token() == "%>" || context.token() == "%]")
95 break;
96 code.push(context.token());
97 }
98 context.pushCode(code.join(''));
99 }
100
101 function visitTextFragment(context) {
102 var i = context.pushData(context.token());
103 context.pushCode("$p.push($data["+i+"]);");
104 }
105
106 var compile = function (str) {
107 if (!str)
108 return function() { return "";};
109
110 var ctx = parseTemplate(str);
111 visitTemplate(ctx);
112 return ctx.compile();
113 };
114
115 var cache = {};
116
117 compile.load = function (id, require, callback) {
118 var url = require.toUrl(id);
119 if (url in cache) {
120 callback(cache[url]);
121 } else {
122 request(url).then(compile).then(function (tc) {
123 callback(cache[url] = tc);
124 }, function (err) {
125 require.signal("error", [{
126 error: err,
127 src: 'implab/text/template-compile'
128 }]);
129 });
130 }
131 };
132
133 return compile;
134 }); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now