| @@ -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,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,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 | "&" : "&", | |
|
|
8 | "\\:" : ":" | |
|
|
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
