1 /**
  2  * @author <a href="http://www.creynders.be">Camille Reynders</a>
  3  * @version 0.2.0
  4  * @namespace
  5  */
  6 var dijon = {
  7 	/**
  8 	 * framework version number
  9 	 * @constant
 10 	 * @type String
 11 	 */
 12 	VERSION : '0.3.2'
 13 };//dijon
 14 
 15   //======================================//
 16  // dijon.EventDispatcher
 17 //======================================//
 18 
 19 /**
 20  * @class dijon.EventDispatcher
 21  * @author Camille Reynders - www.creynders.be
 22  * @constructor
 23  */
 24 dijon.EventDispatcher = function(){
 25 
 26 	this.fqn = 'dijon.EventDispatcher';
 27 
 28 	/** @private */
 29 	this._listeners = {};
 30 
 31 	/** @private */
 32 	this._length = 0;
 33 
 34 };//dijon.EventDispatcher
 35 
 36 dijon.EventDispatcher.prototype = {
 37 
 38 	/**
 39 	 * @private
 40 	 * @param {String} eventType
 41 	 * @param {Number} index
 42 	 */
 43 	_removeListenerByIndex : function( eventType, index ){
 44 		this._listeners[ eventType ].splice( index, 1 );
 45 		if( this._listeners[ eventType ].length <= 0 )
 46 			delete this._listeners[ eventType ];
 47 		this._length--;
 48 	},
 49 
 50 	/**
 51 	 * adds a handler to be invoked when an event is dispatched
 52 	 * @param {String} eventType The name of the event to be listened to
 53 	 * @param {Function} listener The handler to be called when the event has been dispatched
 54 	 * @param {Boolean} [oneShot=false] Whether the listener must be called only once
 55 	 * @param {Boolean} [passEvent=false] Whether the event object needs to be passed to the listener
 56 	 * @return {dijon.EventDispatcher} The EventDispatcher instance
 57 	*/
 58 	addListener : function( eventType, listener, oneShot, passEvent ) {
 59 		this.addScopedListener( eventType, listener, undefined, oneShot, passEvent );
 60 		return this;
 61 	},
 62 
 63 	/**
 64 	 * adds a handler to be invoked in a specific <code>scope</code> when an event of <code>eventType</code> is dispatched
 65 	 * @param {String} eventType The name of the event to be listened to
 66 	 * @param {Function} listener The handler to be called when the event has been dispatched
 67 	 * @param {Object} scope The scope in which the listener will be called
 68 	 * @param {Boolean} [oneShot=false] Whether the listener must be called only once
 69 	 * @param {Boolean} [passEvent=false] Whether the event object needs to be passed to the listener
 70 	 * @return {dijon.EventDispatcher} The EventDispatcher instance
 71 	 */
 72 	addScopedListener : function( eventType, listener, scope, oneShot, passEvent ){
 73 		if( oneShot == undefined ) oneShot = false;
 74 		if( passEvent == undefined ) passEvent = false;
 75 		if( this._listeners[ eventType ] == undefined ){
 76 			this._listeners[ eventType ] = [];
 77 		}
 78 		this._listeners[ eventType ].push( {
 79 			scope : scope,
 80 			listener : listener,
 81 			oneShot : oneShot,
 82 			passEvent : passEvent
 83 		} );
 84 		++this._length;
 85 		return this;
 86 	},
 87 
 88 	/**
 89 	 * Checks whether the dispatcher object has registered <code>listener</code> for <code>eventType</code>
 90 	 * @param {String} eventType The name of the event to be listened to
 91 	 * @param {Function} listener The handler to be called when the event has been dispatched
 92 	 * @return {Boolean}
 93 	 */
 94 	hasListener : function( eventType, listener ){
 95 		return this.hasScopedListener( eventType, listener, undefined );
 96 	},
 97 
 98 	/**
 99 	 * Checks whether the dispatcher object has registered <code>listener</code> in <code>scope</code> for <code>eventType</code>
100 	 * @param {String} eventType The name of the event to be listened to
101 	 * @param {Function} listener The handler to be called when the event has been dispatched
102 	 * @param {Object} scope The scope in which the listener will be called
103 	 * @return {Boolean}
104 	 */
105 	hasScopedListener : function( eventType, listener, scope ){
106 		if( this._listeners[ eventType ] ){
107 			var listeners = this._listeners[ eventType ];
108 			for( var i = 0, n = listeners.length ; i < n ; i++  ){
109 				var obj = listeners[ i ];
110 				if( obj.listener === listener && obj.scope === scope ){
111 					return true;
112 				}
113 			}
114 		}
115 
116 		return false;
117 	},
118 
119 	/**
120 	 * Removes a <code>listener</code> registered for <code>eventType</code>
121 	 * @param {String} eventType The name of the event to be listened to
122 	 * @param {Function} listener The handler to be called when the event has been dispatched
123 	 * @return {dijon.EventDispatcher} The EventDispatcher instance
124 	 */
125 	removeListener : function( eventType, listener ){
126 		this.removeScopedListener( eventType, listener, undefined );
127 		return this;
128 	},
129 
130 	/**
131 	 * Removes a <code>listener</code> in <code>scope</code> registered for <code>eventType</code>
132 	 * @param {String} eventType The name of the event to be listened to
133 	 * @param {Function} listener The handler to be called when the event has been dispatched
134 	 * @param {Object} scope The scope in which the listener will be called
135 	 * @return {dijon.EventDispatcher} The EventDispatcher instance
136 	 */
137 	removeScopedListener : function( eventType, listener, scope ) {
138 		for ( var i = 0 ; this._listeners[ eventType ] && i < this._listeners[ eventType ].length ;  ){
139 			var obj = this._listeners[ eventType ][ i ];
140 			if( obj.listener == listener && obj.scope === scope ){
141 				this._removeListenerByIndex( eventType, i );
142 			}else{
143 				i++;
144 			}
145 		}
146 		return this;
147 	},
148 
149 	/**
150 	 * dispatches an event with any number of arguments
151 	 * @param {Object|String} event The event object or event type
152 	 * @param {String} event.type if <code>event</code> is of type <code>Object</code> it requires a <code>type</code> property set to the name of the event that will be listened to.
153 	 * @param {...} [...=undefined] Any number of parameters
154 	 * @return {dijon.EventDispatcher} The EventDispatcher instance
155 	 */
156 	dispatchEvent : function( event ){
157 		if (typeof event == "string"){
158 			event = { type: event };
159 		}
160 		if (!event.target){
161 			event.target = this;
162 		}
163 		var args = [];
164 		for( var i = 1, n = arguments.length; i < n ; i++ ){
165 			args.push( arguments[ i ] );
166 		}
167 
168 		if( this._listeners[ event.type ] ){
169 			for( var i = 0 ; this._listeners[ event.type ] && i < this._listeners[ event.type ].length ;  ){
170 				var obj = this._listeners[ event.type ][ i ];
171 				if( obj.oneShot ){
172 					this._removeListenerByIndex( event.type, i );
173 				}else{
174 					i++;
175 				}
176 				if( obj.passEvent ){
177 					args.unshift( event );
178 				}
179 				obj.listener.apply( obj.scope, args );
180 			}
181 		}
182 
183 		return this;
184 	},
185 
186 	/**
187 	 * removes all event listeners
188 	 * @return {dijon.EventDispatcher} The EventDispatcher instance
189 	 */
190 	removeAllListeners : function(){
191 	   this._listeners = {};
192 	   this._length = 0;
193 
194 	   return this;
195 	},
196 
197 	/**
198 	 * Returns the number of listeners
199 	 * @return {Number} The number of listeners
200 	 */
201 	length : function(){
202 		return this._length;
203 	}
204 
205 };//dijon.EventDispatcher.prototype
206 
207   //======================================//
208  // dijon.Dictionary
209 //======================================//
210 
211 /**
212  * The Dictionary class lets you create a dynamic collection of properties, which uses strict equality (===) for key comparison.
213  * @class dijon.Dictionary
214  * @constructor
215  */
216 dijon.Dictionary = function(){
217 
218 	this.fqn = 'dijon.Dictionary';
219 	
220 	/**
221 	 * @private
222 	 */
223 	this._map = [];
224 	
225 };//dijon.Dictionary
226 
227 dijon.Dictionary.prototype = {
228 	/**
229 	 * @private
230 	 * @param key
231 	 * @return index
232 	 */
233 	_getIndexByKey : function( key ){
234 		for( var i = 0, n = this._map.length ; i < n ; i++ ){
235 			if( this._map[ i ].key === key )
236                 return i;
237 		}
238 
239 		return -1;
240 	},
241 
242 	/**
243 	 * Maps <code>value</code> to <code>key</code>
244 	 * @param {Object} key
245 	 * @param {Object} value
246 	 * @return {dijon.Dictionary} the Dictionary instance
247 	 */
248 	add : function( key, value ){
249 		var index = this._getIndexByKey( key );
250 		if( index < 0 ){
251 			this._map.push( {
252 				key : key,
253 				value : value
254 			});
255 		}else{
256 			this._map[ index ] = {
257 				key : key,
258 				value : value
259 			}
260 		}
261 		return this;
262 	},
263 
264 	/**
265 	 * removes the mapping of the value of <code>key</code>
266 	 * @param {Object} key
267 	 * @return {dijon.Dictionary} the Dictionary instance
268 	 */
269 	remove : function( key ){
270 		var index = this._getIndexByKey( key );
271 		if( index >= 0 ) return this._map.splice( index, 1 ).value;
272 
273 		return this;
274 	},
275 
276 	/**
277 	 * retrieves the value mapped to <code>key</code>
278 	 * @param {Object} key
279 	 * @return {Object} the value mapped to <code>key</code>
280 	 */
281 	getValue : function( key ){
282 		var index = this._getIndexByKey( key );
283 
284 		if( index >= 0 ) return this._map[ index ].value;
285 
286 		return null;
287 	},
288 
289 	/**
290 	 * checks whether a value has been mapped to <code>key</code>
291 	 * @param {Object} key
292 	 * @return {Boolean}
293 	 */
294 	hasValue : function( key ){
295 		var index = this._getIndexByKey( key );
296 		return ( index >= 0 );
297 	}
298 
299 };//dijon.Dictionary.prototype
300 
301   //======================================//
302  // dijon.Injector
303 //======================================//
304 
305 /**
306  * @class dijon.Injector
307  * @constructor
308 */
309 dijon.Injector = function(){
310 	this.fqn = 'dijon.Injector';
311 
312 	/** @private */
313 	this._mappingsByClassOrObject = new dijon.Dictionary();
314 
315 	/** @private */
316 	this._outlets = [];
317 };//dijon.Injector
318 
319 dijon.Injector.prototype = {
320 
321 	/**
322 	 * @private
323 	 * @param {Class} clazz
324 	 */
325 	_createAndSetupInstance : function( clazz ){
326 		var instance = new clazz();
327 		this.injectInto( instance );
328 		if( "setup" in instance ) instance.setup.call( instance );
329 		return instance;
330 	},
331 
332 	/**
333 	 * @private
334 	 * @param {Class} key
335 	 * @param {Boolean} overrideRules
336 	 * @return {Object}
337 	 */
338 	_retrieveFromCacheOrCreate : function( key, overrideRules ){
339 		var config = this._mappingsByClassOrObject.getValue( key );
340 		var output = null;
341 		if( overrideRules ){
342             //key is a class
343             //TODO: check if it IS a class
344 			output = this._createAndSetupInstance( key );
345 		}else{
346 			if( config ){
347 				if( config.isSingleton ){
348 					if( config.object == null  ){
349 						config.object = this._createAndSetupInstance( config.clazz );
350 					}
351 					output = config.object;
352 				}else{
353 					output = this._createAndSetupInstance( config.clazz );
354 				}
355 			}else{
356 				throw new Error( this.fqn + " is missing a rule for " + key );
357 			}
358 		}
359 		return output
360 	},
361 
362 
363 	/**
364 	 * defines <code>propertyName</code> as an injection point for the class mapped to <code>targetKey</code> to be injected with an instance
365 	 * of the class mapped to <code>sourceKey</code>.
366 	 * @param {Class} targetKey the class the injection point is applied to.
367 	 * @param {String} propertyName the <strong>name</strong> of the property used as an injection point.<br/>
368 	 * [!] MUST BE STRING
369 	 * @param {Object} sourceKey the key to the value that will be injected
370      * @see dijon.Injector#removeOutlet
371 	 */
372 	addOutlet : function( targetKey, propertyName, sourceKey ){
373 		this._outlets.push( {
374 			target : targetKey,
375 			property : propertyName,
376 			source : sourceKey
377 		} );
378 	},
379 
380 	/**
381 	 * Create (if possible) or retrieve an instance of the class mapped to <code>key</code>
382 	 * @param {Object} key
383 	 * @return {Object}
384 	 */
385 	getInstance : function( key ){
386 		return this._retrieveFromCacheOrCreate( key, false );
387 	},
388 
389 	/**
390 	 * When asked for an instance of the class <code>whenAskedFor<code> or object <code>whenAskedFor</code> inject an instance of <code>whenAskedFor<code>.
391 	 * @param {Class|Object} whenAskedFor
392 	 */
393 	mapSingleton : function( whenAskedFor ){
394 		if( this._mappingsByClassOrObject.hasValue( whenAskedFor ) ) throw new Error( this.fqn + ' cannot remap ' + whenAskedFor + ' without unmapping first' );
395 		this.mapSingletonOf( whenAskedFor, whenAskedFor );
396 	},
397 
398 	/**
399 	 * When asked for an instance of the class <code>whenAskedFor</code> or object <code>whenAskedFor</code> inject the instance <code>useValue</code>.
400 	 * @param {Class|Object} whenAskedFor
401 	 * @param {Object} useValue
402 	 */
403 	mapValue : function( whenAskedFor, useValue ){
404 		if( whenAskedFor == null || whenAskedFor == undefined ) throw new Error( this.fqn + ' cannot map to an undefined object' );
405 		if( useValue == null || useValue == undefined ) throw new Error( this.fqn + ' cannot map an undefined object' );
406 		if( this._mappingsByClassOrObject.hasValue( whenAskedFor ) ) throw new Error( this.fqn + ' cannot remap ' + ' without unmapping first' );
407 		this._mappingsByClassOrObject.add(
408 			whenAskedFor,
409 			{
410 				clazz : whenAskedFor,
411 				object : useValue,
412 				isSingleton : true
413 			}
414 		);
415 	},
416 
417 	setValue : function( whenAskedFor, useValue ){
418 		var config = this._mappingsByClassOrObject.getValue( whenAskedFor );
419 		config.object = useValue;
420 	},
421 
422 	/**
423 	 * Does a rule exist to satsify such a request?
424 	 * @param {Class} whenAskedFor
425 	 * @return {Boolean}
426 	 */
427 	hasMapping : function( whenAskedFor ){
428 		return this._mappingsByClassOrObject.hasValue( whenAskedFor );
429 	},
430 
431 	/**
432 	 * When asked for an instance of the class <code>whenAskedFor</code> or for object <code>whenAskedFor</code> inject a <strong>new</strong> instance of <code>instantiateClass</code>.
433 	 * @param {Class|Object} whenAskedFor
434 	 * @param {Class} instantiateClass
435 	 */
436 	mapClass : function( whenAskedFor, instantiateClass ){
437 		if( this.hasMapping( whenAskedFor ) ) throw new Error( this.fqn + ' cannot remap ' + ' without unmapping first' );
438 		this._mappingsByClassOrObject.add(
439 			whenAskedFor,
440 			{
441 				clazz : instantiateClass,
442 				object : null,
443 				isSingleton : false
444 			}
445 		);
446 	},
447 
448 	/**
449 	 * When asked for an instance of the class <code>whenAskedFor</code> or object <code>whenAskedFor</code> inject an instance of <code>useSingletonOf</code>.
450 	 * @param {Class|Object} whenAskedFor
451 	 * @param {Class} useSingletonOf
452 	 */
453 	mapSingletonOf : function( whenAskedFor, useSingletonOf ){
454 		if( this._mappingsByClassOrObject.hasValue( whenAskedFor ) ) throw new Error( this.fqn + ' cannot remap ' + ' without unmapping first' );
455 		this._mappingsByClassOrObject.add(
456 			whenAskedFor,
457 			{
458 				clazz : useSingletonOf,
459 				object : null,
460 				isSingleton : true
461 			}
462 		);
463 	},
464 
465 	/**
466 	 * create an instance of the class mapped to <code>keyOrClass</code> and fulfill it's mapped dependencies<br/>
467 	 * <strong>WILL ALWAYS CREATE A NEW INSTANCE</strong>, even if <code>keyOrClass</code> was mapped otherwise or
468 	 * <strong>even when <code>keyOrClass</code> was not mapped</code>.
469 	 * @param {Class} keyOrClass
470 	 * @return {Object}
471 	 */
472 	instantiate : function( keyOrClass ){
473 		return this._retrieveFromCacheOrCreate( keyOrClass, true );
474 	},
475 
476 	/**
477 	 * Perform an injection into an object, satisfying all it's dependencies
478 	 * @param {Object} instance
479 	 */
480 	injectInto : function( instance ){
481 		for( var i = 0, n = this._outlets.length ; i < n ; i++ ){
482 			var mapping = this._outlets[ i ];
483 			if( instance && instance instanceof mapping.target && mapping.property in instance )
484 				instance[ mapping.property ] = this.getInstance( mapping.source );
485 		}
486 	},
487 
488 	/**
489 	 * Remove a rule from the injector
490 	 * @param {Class|Object} whenAskedFor
491 	 */
492 	unmap : function( whenAskedFor ){
493 		this._mappingsByClassOrObject.remove( whenAskedFor );
494 	},
495 
496 	/**
497 	 * removes an injection point mapping for a given class mapped to <code>key</code>
498 	 * @param {Object} key
499 	 * @param {String} propertyName MUST BE STRING
500 	 * @see dijon.Injector#addOutlet
501 	 */
502 	removeOutlet : function( key, propertyName ){
503 		for( var i = 0, n = this._outlets.length ; i < n ; i++ ){
504 			var point = this._outlets[ i ];
505 			if( point.target == key && point.property == propertyName ) {
506 				this._outlets.splice( i, 1 );
507 				return;
508 			}
509 		}
510 	}
511 
512 };//dijon.Injector.prototype
513 
514   //======================================//
515  // dijon.EventMap
516 //======================================//
517 
518 /**
519  * registers class members as listeners for specific events, before (or after) instantiation.<br/>
520  * Allows for mapping listeners to lazily instantiated objects.<br/>
521  * [!] This class differs substantially from the RobotLegs EventMap both in use and functionality
522  * @class dijon.EventMap
523  * @constructor
524  */
525 dijon.EventMap = function(){
526 	this.fqn = 'dijon.EventMap';
527 
528 	/**
529 	 * @private
530 	 * @type Object
531 	 */
532 	this._mappingsByEventType = {};
533 
534 	/**
535 	 * @private
536 	 * @type Object
537 	 */
538 	this._mappingsNumByKey = {};
539 
540 	/**
541 	 * @private
542 	 * @type dijon.EventDispatcher
543 	 */
544 	this.eventDispatcher = undefined; //inject
545 
546 	/**
547 	 * @private
548 	 * @type dijon.Injector
549 	 */
550 	this.injector = undefined; //inject
551 
552 };//dijon.EventMap
553 
554 dijon.EventMap.prototype = {
555 
556 	/**
557 	 * @private
558 	 * @param eventType
559 	 * @param key
560 	 * @param handler
561 	 */
562 	_getMappingIndex : function( mappingsListForEvent, key, handler ){
563 		if( mappingsListForEvent ){
564 			for( var i = 0, n = mappingsListForEvent.length; i < n ; i++ ){
565 				var mapping = mappingsListForEvent[ i ];
566 				if( mapping.key === key && mapping.handler === handler ){
567 					return i;
568 				}
569 			}
570 		}
571 
572 		return -1;
573 	},
574 				
575 	/**
576 	 * @private
577 	 * @param {Object} event
578 	 */
579 	_handleRuledMappedEvent : function( event ){
580 		var mappingsListForEvent = this._mappingsByEventType[ event.type ];
581 		var args = [];
582 
583 		for( var i = 1, n = arguments.length; i < n ; i++ ){
584 			args.push( arguments[ i ] );
585 		}
586 		for( var i = 0, n = mappingsListForEvent.length; i < n; i++ ){
587 			var obj = mappingsListForEvent[i];
588 			if( this.injector.hasMapping( obj.key ) ){
589 				var instance = this.injector.getInstance( obj.key );
590 				if( obj.oneShot )
591 					this.removeRuledMapping( event.type, obj.key, obj.handler );
592 				if( obj.passEvent )
593 					args.unshift( event );
594 				if( obj.handler != null )
595 					instance[ obj.handler ].apply( instance, args );
596 					//obj.handler.apply( instance, args );
597 			}else{
598 				//injector mapping has been deleted, but
599 				//eventMap mapping not
600 				//TODO: remove or throw error?
601 			}
602 		}
603 	},
604 
605 	/**
606 	 * @private
607 	 * @param {String} eventType
608 	 * @param {Class} key
609 	 * @param {Function} handler
610 	 */
611 	_removeRuledMappingAndUnmapFromInjectorIfNecessary : function( eventType, key, handler ){
612 		this.removeRuledMapping( eventType, key, handler );
613 		var mappingsNum = this._mappingsNumByKey[ key ] || 0;
614 		if( mappingsNum <= 0 )
615 			this.injector.unmap( key );
616 	},
617 
618 	/**
619 	 * maps <code>handler</code> as a listener for <code>eventType</code> to be called as an instance member of the class mapped to <code>key</code>
620 	 * instance. The instance will be created according to the rule defined for <code>key</code> in injector.
621 	 * <br/>[!] requires <code>key</code> is already ruled by the injector
622 	 * @see dijon.Injector
623 	 * @param {String} eventType The name of the event to be listened to
624 	 * @param {Object} key
625 	 * @param {Function} handler
626 	 * @param {Boolean} [oneShot=false] Whether the listener must be called only once
627 	 * @param {Boolean} [passEvent=false] Whether the event object should be passed as a parameter to <code>handler</code>
628 	 * upon invocation or not. If <code>true</code> any additional dispatched values will be passed as parameters after
629 	 * the event object
630 	 */
631 	addRuledMapping : function( eventType, key, handler, oneShot, passEvent ){
632 		if( ! this.injector.hasMapping( key ) ){
633 			throw new Error( '*** ERROR *** ' + this.fqn + ' addRuledMapping can only be used on a key already mapped by the injector')
634 		}
635 		if( ! this._mappingsByEventType[ eventType ] ){
636 			this._mappingsByEventType[ eventType ] = [];
637 			this.eventDispatcher.addScopedListener( eventType, this._handleRuledMappedEvent, this, false, true );
638 		}
639 
640 		var mappingsNum = this._mappingsNumByKey[ key ] || 0;
641 		this._mappingsNumByKey[ key ] = ++mappingsNum;
642 
643 		this._mappingsByEventType[ eventType ].push( { key : key, handler : handler, oneShot : oneShot, passEvent: passEvent } );
644 	},
645 
646 	/**
647 	 * Removes the mapping for <code>key</code>
648 	 * @see dijon.EventMap#addRuledMapping
649 	 * @param {String} eventType The name of the event to be listened to
650 	 * @param {Class} key
651 	 * @param {Function} handler
652 	 */
653 	removeRuledMapping : function( eventType, key, handler ){
654 		var mappingsListForEvent = this._mappingsByEventType[ eventType ];
655 		var index = this._getMappingIndex( mappingsListForEvent, key, handler );
656 		if( index >= 0 ){
657 			/* DO NOT CLEAN UP MAPPING DEPENDENCIES, mapping maybe still in use */
658 			/*
659 			var mapping = mappingsListForEvent[ index ];
660 			delete mapping.key;
661 			delete mapping.handler;
662 			mapping = null;
663 			*/
664 			mappingsListForEvent.splice( index, 1 );
665 			if( mappingsListForEvent.length <= 0 )
666 				delete mappingsListForEvent[ eventType ];
667 			var mappingsNum = this._mappingsNumByKey[ key ] || 0;
668 			if( mappingsNum <= 0 ){
669 				delete this._mappingsNumByKey[ key ];
670 			}else{
671 				this._mappingsNumByKey[ key ] = --mappingsNum;
672 			}
673 			return true;
674 
675 		}
676 		return false;
677 	},
678 
679 	/**
680 	 * maps <code>handler</code> as a listener for <code>eventType</code> to be called as a member of a <code>clazz</code>
681 	 * instance. The instance will ALWAYS be a new one, regardless of previous injector mappings for that <code>clazz</code>.<br/>
682 	 * If <code>handler</code> is <code>undefined</code> or <code>null</code> the instance will be created but no handler will be invoked.<br/>
683 	 * In that case the dispatched payload will not be passed. (no constructor injection at the moment)
684 	 * @param {String} eventType
685 	 * @param {Class} clazz
686 	 * @param {Function} [handler=null]
687 	 * @param {Boolean} [oneShot=false]
688 	 * @param {Boolean} [passEvent=false]
689 	 */
690 	addClassMapping : function( eventType, clazz, handler, oneShot, passEvent ){
691 		if( ! this.injector.hasMapping( clazz ) )
692 			this.injector.mapClass( clazz, clazz );
693 		if( handler == undefined )
694 			handler = null;
695 		this.addRuledMapping( eventType, clazz, handler, oneShot, passEvent );
696 	},
697 
698 	/**
699 	 * Removes the mapping for <code>clazz</code>
700 	 * @see dijon.EventMap#addClassMapping
701 	 * @param {String} eventType
702 	 * @param {Class} clazz
703 	 * @param {Function} [handler=null]
704 	 */
705 	removeClassMapping : function( eventType, clazz, handler ){
706 		if( handler == undefined )
707 			handler = null;
708 		this._removeRuledMappingAndUnmapFromInjectorIfNecessary( eventType, clazz, handler );
709 	},
710 
711 	/**
712 	 * Checks whether a mapping exists. The combination of <code>eventType, key, handler</code> must be identical
713 	 * to what was mapped for this to return true. If <code>key</code> was mapped for <code>eventType</code> <strong>with</strong>
714 	 * a <code>handler</code> then <code>hasMapping</code> will return <code>false</code> if only invoked with parameters
715 	 * <code>eventType</code> and <code>key</code>
716 	 * @param {String} eventType
717 	 * @param {Object} key
718 	 * @param {Function} [handler=null]
719 	 * @return {Boolean}
720 	 */
721 	hasMapping : function( eventType, key, handler ){
722 		if( handler == undefined )
723 			handler = null;
724 		return this._getMappingIndex( this._mappingsByEventType[ eventType ], key, handler ) >= 0 ;
725 	}
726 
727 
728 };//dijon.EventMap.prototype
729 
730 
731   //======================================//
732  // dijon.Actor
733 //======================================//
734 
735 /**
736  * Convenience class
737  * @class dijon.Actor
738  * @constructor
739  */
740 dijon.Actor = function(){
741 	/**
742 	 * @type dijon.Injector
743 	 */
744 	this.injector = undefined;//inject
745 	
746 	/**
747 	 * @type dijon.EventMap
748 	 */
749 	this.eventMap = undefined;//inject
750 
751 	/**
752 	 * @type dijon.EventDispatcher
753 	 */
754 	this.eventDispatcher = undefined; //inject
755 };//dijon.Actor
756 
757 dijon.Actor.prototype = {
758 	/**
759 	 * is automatically invoked after injection has occurred.
760 	 * <br/> cfr. RobotLegs' <code>[PostConstruct]</code> or <code>Mediator#onRegister</code>
761 	 */
762 	setup : function(){
763 	}
764 };//dijon.Actor.prototype
765 
766 
767   //======================================//
768  // dijon.Context
769 //======================================//
770 
771 /**
772  * @class dijon.Context
773  * @constructor
774  */
775 dijon.Context = function(){
776 	this.fqn = 'dijon.Context';
777 
778 };//dijon.Context
779 dijon.Context.prototype = new dijon.Actor();
780 dijon.Context.prototype.constructor = dijon.Context;
781 
782 /**
783  * @private
784  */
785 dijon.Context.prototype._createInjector = function(){
786 	this.injector = new dijon.Injector();
787 };
788 
789 /**
790  * @private
791  */
792 dijon.Context.prototype._setupWirings = function(){
793 	this.parseConfig( dijon.wirings );
794 	this.injector.setValue( dijon.wirings.injector, this.injector );
795 }
796 /**
797  * @param {Boolean} [autoStartup=true]
798  */
799 dijon.Context.prototype.init = function( autoStartup ){
800 	this._createInjector();
801 	this._setupWirings();
802 	this.injector.injectInto( this );
803 	if( autoStartup == true || autoStartup==undefined ) this.startup();
804 };
805 
806 dijon.Context.prototype.parseConfig = function( configList ){
807 	for( var configName in configList ){
808 		var configObj = configList[ configName ];
809 		if( configObj.singleton ){
810 			this.injector.mapSingletonOf( configObj, configObj.impl );
811 		}else{
812 			this.injector.mapClass( configObj, configObj.impl );
813 		}
814 		for( var outletName in configObj.outlets ){
815 			var outletWiring = configObj.outlets[ outletName ];
816 			this.injector.addOutlet( configObj.impl, outletName, outletWiring );
817 		}
818 		for( var i in configObj.handlers ){
819 			var handlerConfig = configObj.handlers[ i ];
820 			this.eventMap.addRuledMapping( handlerConfig.event, configObj, handlerConfig.handler, handlerConfig.oneShot, handlerConfig.passEvent )
821 		}
822 	}
823 };
824 
825 
826 
827 /**
828  * abstract, should be overridden
829  */
830 dijon.Context.prototype.startup = function(){
831 }
832 
833 dijon.wirings = {};
834 dijon.wirings.injector = {
835 	impl : dijon.Injector,
836 	singleton : true
837 };
838 dijon.wirings.eventDispatcher = {
839 	impl : dijon.EventDispatcher,
840 	singleton : true
841 };
842 dijon.wirings.eventMap = {
843 	impl : dijon.EventMap,
844 	singleton : true,
845 	outlets : {
846 		eventDispatcher : dijon.wirings.eventDispatcher,
847 		injector : dijon.wirings.injector
848 	}
849 };
850 dijon.wirings.actor = {
851 	impl : dijon.Actor,
852 	singleton : true,
853 	outlets : {
854 		eventDispatcher : dijon.wirings.eventDispatcher,
855 		injector : dijon.wirings.injector,
856 		eventMap : dijon.wirings.eventMap
857 	}
858 };
859