jasmine-stories 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,499 @@
1
+ /*
2
+ Script: Namespace.js
3
+ Namespace utility
4
+
5
+ Copyright:
6
+ Copyright (c) 2009 Maxime Bouroumeau-Fuseau
7
+
8
+ License:
9
+ MIT-style license.
10
+
11
+ Version:
12
+ 1.1
13
+ */
14
+
15
+ /*jslint evil : true */
16
+ /*global Namespace, XMLHttpRequest, ActiveXObject, window, document */
17
+
18
+ var Namespace = (function() {
19
+
20
+ var _listeners = {};
21
+ var _includedIdentifiers = [];
22
+
23
+ /**
24
+ * Returns an object in an array unless the object is an array
25
+ *
26
+ * @param mixed obj
27
+ * @return Array
28
+ */
29
+ var _toArray = function(obj) {
30
+ // checks if it's an array
31
+ if (typeof(obj) == 'object' && obj.sort) {
32
+ return obj;
33
+ }
34
+ return Array(obj);
35
+ };
36
+
37
+ /**
38
+ * Creates an XMLHttpRequest object
39
+ *
40
+ * @return XMLHttpRequest
41
+ */
42
+ var _createXmlHttpRequest = function() {
43
+ var xhr;
44
+ try { xhr = new XMLHttpRequest(); } catch(e) {
45
+ try { xhr = new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch(e2) {
46
+ try { xhr = new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch(e3) {
47
+ try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e4) {
48
+ try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e5) {
49
+ throw new Error( "This browser does not support XMLHttpRequest." );
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ return xhr;
56
+ };
57
+
58
+ /**
59
+ * Checks if an http request is successful based on its status code.
60
+ * Borrowed from dojo (http://www.dojotoolkit.org).
61
+ *
62
+ * @param Integer status Http status code
63
+ * @return Boolean
64
+ */
65
+ var _isHttpRequestSuccessful = function(status) {
66
+ return (status >= 200 && status < 300) || // Boolean
67
+ status == 304 || // allow any 2XX response code
68
+ status == 1223 || // get it out of the cache
69
+ (!status && (window.location.protocol == "file:" || window.location.protocol == "chrome:") ); // Internet Explorer mangled the status code
70
+ };
71
+
72
+ /**
73
+ * Creates a script tag with the specified data as content
74
+ *
75
+ * @param String data The content of the script
76
+ */
77
+ var _createScript = function(data) {
78
+ var script = document.createElement('script');
79
+ script.type = 'text/javascript';
80
+ script.text = data;
81
+
82
+ if (typeof window.execScript === "object") { // According to IE
83
+ window.execScript(data);
84
+ } else {
85
+ try { // Attempt body insertion
86
+ document.body.appendChild(script);
87
+ } catch (e) { // Fall back on eval
88
+ window['eval'](data);
89
+ }
90
+ }
91
+ };
92
+
93
+ /**
94
+ * Dispatches an event
95
+ *
96
+ * @param String eventName
97
+ * @param Object properties
98
+ */
99
+ var _dispatchEvent = function(eventName, properties) {
100
+ if (!_listeners[eventName]) { return; }
101
+ properties.event = eventName;
102
+ for (var i = 0; i < _listeners[eventName].length; i++) {
103
+ _listeners[eventName][i](properties);
104
+ }
105
+ };
106
+
107
+ /**
108
+ * Creates an Object following the specified namespace identifier.
109
+ *
110
+ * @public
111
+ * @param String identifier The namespace string
112
+ * @param Object klasses (OPTIONAL) An object which properties will be added to the namespace
113
+ * @return Object The most inner object
114
+ */
115
+ var _namespace = function(identifier) {
116
+ var klasses = arguments[1] || false;
117
+ var ns = window;
118
+
119
+ if (identifier !== '') {
120
+ var parts = identifier.split(Namespace.separator);
121
+ for (var i = 0; i < parts.length; i++) {
122
+ if (!ns[parts[i]]) {
123
+ ns[parts[i]] = {};
124
+ }
125
+ ns = ns[parts[i]];
126
+ }
127
+ }
128
+
129
+ if (klasses) {
130
+ for (var klass in klasses) {
131
+ if (klasses.hasOwnProperty(klass)) {
132
+ ns[klass] = klasses[klass];
133
+ }
134
+ }
135
+ }
136
+
137
+ _dispatchEvent('create', { 'identifier': identifier });
138
+ return ns;
139
+ };
140
+
141
+ /**
142
+ * Checks if the specified identifier is defined
143
+ *
144
+ * @public
145
+ * @param String identifier The namespace string
146
+ * @return Boolean
147
+ */
148
+ _namespace.exist = function(identifier) {
149
+ if (identifier === '') { return true; }
150
+
151
+ var parts = identifier.split(Namespace.separator);
152
+ var ns = window;
153
+ for (var i = 0; i < parts.length; i++) {
154
+ if (!ns[parts[i]]) {
155
+ return false;
156
+ }
157
+ ns = ns[parts[i]];
158
+ }
159
+
160
+ return true;
161
+ };
162
+
163
+ /**
164
+ * Maps an identifier to a uri. Is public so it can be overriden by custom scripts.
165
+ *
166
+ * @public
167
+ * @param String identifier The namespace identifier
168
+ * @return String The uri
169
+ */
170
+ _namespace.mapIdentifierToUri = function(identifier) {
171
+ var regexp = new RegExp('\\' + Namespace.separator, 'g');
172
+ return Namespace.baseUri + identifier.replace(regexp, '/') + '.js';
173
+ };
174
+
175
+ /**
176
+ * Loads a remote script atfer mapping the identifier to an uri
177
+ *
178
+ * @param String identifier The namespace identifier
179
+ * @param Function successCallback When set, the file will be loaded asynchronously. Will be called when the file is loaded
180
+ * @param Function errorCallback Callback to be called when an error occurs
181
+ * @return Boolean Success of failure when loading synchronously
182
+ */
183
+ var _loadScript = function(identifier) {
184
+ var successCallback = arguments[1];
185
+ var errorCallback = arguments[2];
186
+ var async = typeof successCallback === "function";
187
+ var uri = _namespace.mapIdentifierToUri(identifier);
188
+ var event = { 'identifier': identifier, 'uri': uri, 'async': async, 'callback': successCallback };
189
+
190
+ var xhr = _createXmlHttpRequest();
191
+ xhr.open("GET", uri, async);
192
+
193
+ if (async) {
194
+ xhr.onreadystatechange = function() {
195
+ if (xhr.readyState == 4) {
196
+ if (_isHttpRequestSuccessful(xhr.status || 0)) {
197
+ _createScript(xhr.responseText);
198
+ _dispatchEvent('include', event);
199
+ successCallback();
200
+ return;
201
+ }
202
+ event.status = xhr.status;
203
+ _dispatchEvent('includeError', event);
204
+ if (typeof errorCallback === "function") {
205
+ errorCallback();
206
+ }
207
+ }
208
+ };
209
+ }
210
+
211
+ xhr.send(null);
212
+
213
+ if (!async) {
214
+ if (_isHttpRequestSuccessful(xhr.status || 0)) {
215
+ _createScript(xhr.responseText);
216
+ _dispatchEvent('include', event);
217
+ return true;
218
+ }
219
+ event.status = xhr.status;
220
+ _dispatchEvent('includeError', event);
221
+ return false;
222
+ }
223
+ };
224
+
225
+ /**
226
+ * Includes a remote javascript file identified by the specified namespace string. The identifier
227
+ * must point to a class. Separators in the string will be converted to slashes and the .js extension will be appended.
228
+ *
229
+ * @public
230
+ * @param String identifier The namespace string
231
+ * @param Function callback (OPTIONAL) A function to call when the remote script has been included
232
+ */
233
+ _namespace.include = function(identifier) {
234
+ var successCallback = arguments[1] || false;
235
+ var errorCallback = arguments[2] || false;
236
+
237
+ // checks if the identifier is not already included
238
+ if (_includedIdentifiers[identifier]) {
239
+ if (typeof successCallback === "function") { successCallback(); }
240
+ return true;
241
+ }
242
+
243
+ if (successCallback) {
244
+ _loadScript(identifier, function() {
245
+ _includedIdentifiers[identifier] = true;
246
+ successCallback();
247
+ }, errorCallback);
248
+ } else {
249
+ if (_loadScript(identifier)) {
250
+ _includedIdentifiers[identifier] = true;
251
+ return true;
252
+ }
253
+ return false;
254
+ }
255
+ };
256
+
257
+ /**
258
+ * Imports properties from the specified namespace to the global space (ie. under window)
259
+ *
260
+ * The identifier string can contain the * wildcard character as its last segment (eg: com.test.*)
261
+ * which will import all properties from the namespace.
262
+ *
263
+ * If not, the targeted namespace will be imported (ie. if com.test is imported, the test object
264
+ * will now be global). If the targeted object is not found, it will be included using include().
265
+ *
266
+ * @public
267
+ * @param String identifier The namespace string
268
+ * @param Function callback (OPTIONAL) A function to call when the process is completed (including the include() if used)
269
+ * @param Boolean autoInclude (OPTIONAL) Whether to automatically auto include the targeted object is not found. Default is Namespace.autoInclude
270
+ */
271
+ _namespace.use = function(identifier) {
272
+ var identifiers = _toArray(identifier);
273
+ var callback = arguments[1] || false;
274
+ var autoInclude = arguments.length > 2 ? arguments[2] : Namespace.autoInclude;
275
+ var event = { 'identifier': identifier };
276
+ var parts, target, ns;
277
+
278
+ for (var i = 0; i < identifiers.length; i++) {
279
+ identifier = identifiers[i];
280
+
281
+ parts = identifier.split(Namespace.separator);
282
+ target = parts.pop();
283
+ ns = _namespace(parts.join(Namespace.separator));
284
+
285
+ if (target == '*') {
286
+ // imports all objects from the identifier, can't use include() in that case
287
+ for (var objectName in ns) {
288
+ if (ns.hasOwnProperty(objectName)) {
289
+ window[objectName] = ns[objectName];
290
+ }
291
+ }
292
+ } else {
293
+ // imports only one object
294
+ if (ns[target]) {
295
+ // the object exists, import it
296
+ window[target] = ns[target];
297
+ } else {
298
+ // the object does not exist
299
+ if (autoInclude) {
300
+ // try to auto include it
301
+ if (callback) {
302
+ _namespace.include(identifier, function() {
303
+ window[target] = ns[target];
304
+
305
+ if (i + 1 < identifiers.length) {
306
+ // we continue to unpack the rest from here
307
+ _namespace.unpack(identifiers.slice(i + 1), callback, autoInclude);
308
+ } else {
309
+ // no more identifiers to unpack
310
+ _dispatchEvent('use', event);
311
+ if (typeof callback === "function") {
312
+ callback();
313
+ }
314
+ }
315
+ });
316
+ return;
317
+ } else {
318
+ _namespace.include(identifier);
319
+ window[target] = ns[target];
320
+ }
321
+ }
322
+ }
323
+ }
324
+
325
+ }
326
+
327
+ // all identifiers have been unpacked
328
+ _dispatchEvent('use', event);
329
+ if (typeof callback === "function") { callback(); }
330
+ };
331
+
332
+ /**
333
+ * Binds the include() and unpack() method to a specified identifier
334
+ *
335
+ * @public
336
+ * @param String identifier The namespace identifier
337
+ * @return Object
338
+ */
339
+ _namespace.from = function(identifier) {
340
+ return {
341
+ /**
342
+ * Includes the identifier specified in from()
343
+ *
344
+ * @see Namespace.include()
345
+ */
346
+ include: function() {
347
+ var callback = arguments[0] || false;
348
+ _namespace.include(identifier, callback);
349
+ },
350
+ /**
351
+ * Includes the identifier specified in from() and unpack
352
+ * the specified indentifier in _identifier
353
+ *
354
+ * @see Namespace.use()
355
+ */
356
+ use: function(_identifier) {
357
+ var callback = arguments[1] || false;
358
+ if (_identifier.charAt(0) == '.') {
359
+ _identifier = identifier + _identifier;
360
+ }
361
+
362
+ if (callback) {
363
+ _namespace.include(identifier, function() {
364
+ _namespace.use(_identifier, callback, false);
365
+ });
366
+ } else {
367
+ _namespace.include(identifier);
368
+ _namespace.use(_identifier, callback, false);
369
+ }
370
+ }
371
+ };
372
+ };
373
+
374
+ /**
375
+ * Registers a namespace so it won't be included
376
+ *
377
+ * Idea and code submitted by Nathan Smith (http://github.com/smith)
378
+ *
379
+ * @param String|Array identifier
380
+ */
381
+ _namespace.provide = function(identifier) {
382
+ var identifiers = _toArray(identifier);
383
+
384
+ for (var i = 0; i < identifiers.length; i++) {
385
+ if (!(identifier in _includedIdentifiers)) {
386
+ _dispatchEvent('provide', { 'identifier': identifier });
387
+ _includedIdentifiers[identifier] = true;
388
+ }
389
+ }
390
+ };
391
+
392
+ /**
393
+ * Registers a function to be called when the specified event is dispatched
394
+ *
395
+ * @param String eventName
396
+ * @param Function callback
397
+ */
398
+ _namespace.addEventListener = function(eventName, callback) {
399
+ if (!_listeners[eventName]) { _listeners[eventName] = []; }
400
+ _listeners[eventName].push(callback);
401
+ };
402
+
403
+ /**
404
+ * Unregisters an event listener
405
+ *
406
+ * @param String eventName
407
+ * @param Function callback
408
+ */
409
+ _namespace.removeEventListener = function(eventName, callback) {
410
+ if (!_listeners[eventName]) { return; }
411
+ for (var i = 0; i < _listeners[eventName].length; i++) {
412
+ if (_listeners[eventName][i] == callback) {
413
+ delete _listeners[eventName][i];
414
+ return;
415
+ }
416
+ }
417
+ };
418
+
419
+ /**
420
+ * Adds methods to javascript native's object
421
+ * Inspired by http://thinkweb2.com/projects/prototype/namespacing-made-easy/
422
+ *
423
+ * @public
424
+ */
425
+ _namespace.registerNativeExtensions = function() {
426
+ /**
427
+ * @see Namespace()
428
+ */
429
+ String.prototype.namespace = function() {
430
+ var klasses = arguments[0] || {};
431
+ return _namespace(this.valueOf(), klasses);
432
+ };
433
+ /**
434
+ * @see Namespace.include()
435
+ */
436
+ String.prototype.include = function() {
437
+ var callback = arguments[0] || false;
438
+ return _namespace.include(this.valueOf(), callback);
439
+ };
440
+ /**
441
+ * @see Namespace.use()
442
+ */
443
+ String.prototype.use = function() {
444
+ var callback = arguments[0] || false;
445
+ return _namespace.use(this.valueOf(), callback);
446
+ };
447
+ /**
448
+ * @see Namespace.from()
449
+ */
450
+ String.prototype.from = function() {
451
+ return _namespace.from(this.valueOf());
452
+ };
453
+ /**
454
+ * @see Namespace.provide()
455
+ * Idea and code submitted by Nathan Smith (http://github.com/smith)
456
+ */
457
+ String.prototype.provide = function() {
458
+ return _namespace.provide(this.valueOf());
459
+ };
460
+ /**
461
+ * @see Namespace.use()
462
+ */
463
+ Array.prototype.use = function() {
464
+ var callback = arguments[0] || false;
465
+ return _namespace.use(this, callback);
466
+ };
467
+ /**
468
+ * @see Namespace.provide()
469
+ */
470
+ Array.prototype.provide = function() {
471
+ return _namespace.provide(this);
472
+ };
473
+ };
474
+
475
+ return _namespace;
476
+ })();
477
+
478
+ /**
479
+ * Namespace segment separator
480
+ *
481
+ * @var String
482
+ */
483
+ Namespace.separator = '.';
484
+
485
+ /**
486
+ * Base uri when using Namespace.include()
487
+ * Must end with a slash
488
+ *
489
+ * @var String
490
+ */
491
+ Namespace.baseUri = './';
492
+
493
+ /**
494
+ * Whether to automatically call Namespace.include() when Namespace.import()
495
+ * does not find the targeted object.
496
+ *
497
+ * @var Boolean
498
+ */
499
+ Namespace.autoInclude = true;