// USE viewport

window.$Event = { };

$Event.CodeBSP = - 0x08; // backspace
$Event.CodeTAB =   0x09; // tab
$Event.CodeRTN =   0x0D; // return
$Event.CodeESC = - 0x1B; // escape
$Event.CodeSPC =   0x20; // space
$Event.CodePUP = - 0x21; // page up
$Event.CodePDN = - 0x22; // page down
$Event.CodeEND = - 0x23; // end
$Event.CodeHOM = - 0x24; // home
$Event.CodeLAR = - 0x25; // left arrow
$Event.CodeUAR = - 0x26; // up arrow
$Event.CodeRAR = - 0x27; // right arrow
$Event.CodeDAR = - 0x28; // down arrow
$Event.CodeDEL = - 0x2E; // delete

$Event.KEYDOWN = 0;
$Event.SHIELDS = [ ];
$Event.TYPES = 'keydown keyup keypress mousedown mouseup click dblclick mousemove mouseover mouseleave'.split(/\W+/);
$Event.Shield = null;


$Event.Cancel = function(EVT) {
//-----======----------------

/*
	Empêche le déclenchement par le navigateur des mécanismes par défaut qui assurent la propagation
	de l'évènement et son traitement standard. (Pour autant que ceux-ci n'aient pas été déclenchés
	avant l'émission de l'évènement.)
*/

	if (EVT.stopPropagation != undefined) {
		EVT.stopPropagation();
		EVT.preventDefault();
	}
	else {
		EVT.cancelBubble = true;
		EVT.returnValue = false;
	}
};


$Event.Capture = function(ELEM) {
//-----=======-----------------

/*
	Crée et affiche une division couvrant complètement le document, visuellement rendue par un
	background éventuel défini pour la classe cEVENT_SHIELD.
	Insère l'élément donné dans cette division.
	Redirige tous les évènements clavier et souris vers la fonction EVT_PROCESSOR.
	
	Pour annuler l'effet de $Event.Capture, appler $Event.Release.
*/

	var
		SHIELD = $Ce('div');

	SHIELD.className = 'cEVENT_SHIELD';
	SHIELD.style.position = 'absolute';
	SHIELD.style.margin = 0;
	SHIELD.style.borderWidth = 0;
	SHIELD.style.padding = 0;
	SHIELD.style.width = '100%';
	SHIELD.style.height = '100%';
	SHIELD.style.left = 0;
	SHIELD.style.top = 0;
	$Event.REPLACE_HANDLERS([ document, ELEM, SHIELD ], function(EVT) { ELEM.ProcessEvent($Event.NrmEvt(EVT)); });
	SHIELD.ELEM_PARENT = ELEM.parentNode;
	SHIELD.appendChild(ELEM);
	document.body.appendChild(SHIELD);
	$Event.SHIELDS.push($Event.Shield = SHIELD);
};


$Event.Modifiers = function(EVT) {
//-----=========----------------

/*
	Renvoie une chaîne qui indique quelles touches auxiliaires étaient enfoncées lors de
	l'occurrence de l'évènement donné.
*/

	return '' +
		(EVT.metaKey  ? 'M' : '') +
		(EVT.altKey   ? 'A' : '') +
		(EVT.ctrlKey  ? 'C' : '') +
		(EVT.shiftKey ? 'S' : '');
};

			
$Event.NrmEvt = function(EVT) {
//-----======----------------

/*
	Normalisation de tous les propriétés de l'évènement.
	
	Adjonction des propriétés:
	
		Target: Cible normalisée
		Left, Top: les coordonnées dans le document (et non dans la fenêtre)
		Type: Type normalisé
		KeyCode: Code-caractère normalisé
		MoCKey: (Meta Or Ctrl) metaKey || ctrlKey
*/

	var
		KEY_CODE;

	if (EVT && EVT.target) {
	// NOT MSIE
		EVT.Target = EVT.target;
		EVT.RelatedTarget = EVT.relatedTarget;
	}
	else {
	// MSIE
		EVT = window.event;
		EVT.Target = EVT.srcElement;
		EVT.RelatedTarget = EVT.fromElement ? EVT.fromElement : EVT.toElement;
	}
	if (EVT.clientX != undefined) {
		// FF Mac ne renvoie pas les coordonnées d'un évènement-clavier survenu dans une textarea.
		EVT.Left = EVT.clientX + $Viewport.ScrollLeft();
		EVT.Top  = EVT.clientY + $Viewport.ScrollTop();
	}
	EVT.KeyCode = 0;
	if (EVT.type.indexOf('key') == 0 && (KEY_CODE = (EVT.charCode ? EVT.charCode : EVT.keyCode))) {
		switch (EVT.type) {
			case 'keydown':
				if (
					(KEY_CODE >= 0x20 && KEY_CODE < 0x30) || // Flèches
					(KEY_CODE ==  8) || // BSP
					(KEY_CODE == 27) || // ESC
					(KEY_CODE == 47)    // DEL
				)
					$Event.KEYDOWN = EVT.KeyCode = - KEY_CODE;
				else
					$Event.KEYDOWN = 0;
				break;
			case 'keypress':
				if ($Event.KEYDOWN)
					$Event.KEYDOWN = 0;
				else
					EVT.KeyCode = KEY_CODE;
				break;
		}
		if (EVT.KeyCode)
			EVT.MoCKey = EVT.ctrlKey || EVT.metaKey;
	}
	EVT.Type = (
		EVT.type == 'click' && EVT.button == 2 ?
			'contextmenu' :
			(EVT.KeyCode ?
				'Key' :
				EVT.type
			)
	);
	return EVT;
};


$Event.Release = function(ELEM) {
//-----=======-----------------

/*
	Libère toutes les captures, de la plus récente à la plus ancienne, jusqu'à ELEM, qui sera la
	dernière capture a être libérée.

	Si ELEM vaut null, toutes les captures sont libérées.
*/

	var
		RELEASE,
		SHIELD,
		_ELEM;
		
	RELEASE = ($Event.SHIELDS.length > 0);
	while (RELEASE) {
		document.body.removeChild(SHIELD = $Event.SHIELDS.pop());
		_ELEM = SHIELD.firstChild;
		if (SHIELD.ELEM_PARENT && SHIELD.ELEM_PARENT.nodeType == 1)
			SHIELD.ELEM_PARENT.appendChild(SHIELD.firstChild);
		$Event.RESTORE_HANDLERS([ _ELEM, document ]);
		RELEASE = ($Event.SHIELDS.length > 0 && _ELEM != ELEM);
	}
	$Event.Shield = ($Event.SHIELDS.length > 0 ? $Event.SHIELDS[$Event.SHIELDS.length - 1] : null);
};


$Event.REPLACE_HANDLERS = function(ELEMS, HANDLER) {
//-----================---------------------------

/*
	Sauvegarde tous les gestionnaires d'évènements des éléments donnés, dont le type fait partie de
	$Event.TYPES et les remplace par le gestionnaire donné, qui peut être null.
	
	Voir la fonction réciproque $Event.RESTORE_HANDLERS.
*/

	var
		I,
		J,
		ELEM,
		EVT_TYPE,
		TRIGGER;
		
	for (I = 0; I < ELEMS.length; I++) {
		ELEM = ELEMS[I];
		for (J = 0; J < $Event.TYPES.length; J++) {
			EVT_TYPE = $Event.TYPES[J];
			if (ELEM[EVT_TYPE + 'SAVED'] == undefined)
				ELEM[EVT_TYPE + 'SAVED'] = [ ];
			ELEM[EVT_TYPE + 'SAVED'].push(ELEM[(TRIGGER = 'on' + EVT_TYPE)] == undefined ? null : ELEM[TRIGGER]);
			ELEM[TRIGGER] = HANDLER;
		}
	}
};


$Event.RESTORE_HANDLERS = function(ELEMS) {
//-----================------------------

/*
	Annulle l'effet du derier appel à $Event.REPLACE_HANDLERS(ELEMS[I]).
*/

	var
		I,
		J,
		ELEM,
		EVT_TYPE,
		TRIGGER;
		
	for (I = 0; I < ELEMS.length; I++) {
		ELEM = ELEMS[I];
		for (J = 0; J < $Event.TYPES.length; J++) {
			EVT_TYPE = $Event.TYPES[J];
			ELEM['on' + EVT_TYPE] = ELEM[(TRIGGER = EVT_TYPE + 'SAVED')].pop();
		}
	}
};
