// *************************************   BONE OBJECT ****************************************************
// The is the base class for all client-side bones.  It implements the bone interface, but is has very
// little implemented behavior, because it is intended as a base class for further bone subclasses.
// ************************************************************************************************************
//<SCRIPT>
mstrBoneImpl.prototype = {};
mstrBoneImpl.prototype.features = {};
mstrBoneImpl.prototype.isDormant = false;
mstrBoneImpl.prototype.winListeners = {};

// events
mstrBoneImpl.prototype.onload = function() {
    //@class=mstrBoneImpl;@method=onload;
    try {
        this.initBone();
        return true;
    }
    catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}

mstrBoneImpl.prototype.onreload = function() {
    //@class=mstrBoneImpl;@method=onreload;
    try {
        // TQMS 320622: set inReload to true to indicate inside onreload call.
        this.inReload = true;
        var retValue = true;
        
        //If the corresponding object doesn't exist in the page anymore, unload:
        if (!this.isBoneInPage()) {
            microstrategy.unRegisterBone(this.id);
        }
        else if (!this.isLoaded) {  //If not loaded, load:
	        retValue = this.onload();
        }
        else if (getElementById(this.id) != this.elem && !this.disabled) {
		    //If pointing to different HTML elements:
		    //TQMS 347869 only "refresh" the bone if it's not disabled
            this.onunload();
	        retValue = this.onload();
        }
    } catch(err) {
        microstrategy.errors.log(err);
        retValue = false;
    }
    // set inReload to false to indicate out of onreload call.
    this.inReload = false;
    return retValue;
}

mstrBoneImpl.prototype.isBoneInPage = function() {
    //@class=mstrBoneImpl;@method=isBoneInPage;
    try {
        var src = getElementById(this.id);
        return (src && (src.getAttribute("dialog") != "true" || src.childNodes.length > 0));
    } catch(err) {
        microstrategy.errors.log(err);
        return true;
    }
}

mstrBoneImpl.prototype.onrepostload = function() {
    //@class=mstrBoneImpl;@method=onrepostload;
    try {
        //If not post loaded, post load:
        if (!this.isPostLoaded) {
            return this.onpostload();
        }

        //Nothing to do...
        return true;
    } catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}


mstrBoneImpl.prototype.onpostload = function(e) {
    //@class=mstrBoneImpl;@method=onpostload;
    try {
        this.isPostLoaded = true;
        return true;
    }
    catch(err) {
        microstrategy.errors.log(err);
    }
    return true;
}

mstrBoneImpl.prototype.onmove = function(e) {
}

mstrBoneImpl.prototype.onunload = function(e) {
    //@class=mstrBoneImpl;@method=onunload;
    try {
    	for(var listenerId in this.winListeners){
    	    this.detachWinListener(this.winListeners[listenerId]);     
            delete mstr.controllers.Factory._objs[listenerId];
    	}
    	delete this.elem;
        this.isLoaded = false;
    }
    catch(err) {
        microstrategy.errors.log(err);
    }
    return true;
}

// methods
mstrBoneImpl.prototype.initBone = function() {
    //@class=mstrBoneImpl;@method=initBone;
    try {
        this.elem = getElementById(this.id);
        if(!this.elem){
            this.onunload();
            return false;
        }
        this.winListeners = {};
        this.isLoaded = true;
        this.isPostLoaded = false;
        return true;
    }
    catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}

mstrBoneImpl.prototype.absorbObject = function(obj, append) {
    //@class=mstrBoneImpl;@method=absorbObj;
    try {
         for (var method in obj) {
            if (append && typeof(obj[method]) == 'object') {
             //TQMS 393305: we need to check if this[method] is defined and is type of 'object' before doing any operation against it.
	            if(typeof this[method] == 'undefined')
	            {
	                		this[method] = {};   
	            }
	            if(typeof this[method] == 'object')	 
	             {
	                for (var value in obj[method]) {                	                                                                                            
	                    	this[method][value] = obj[method][value];
	                }  
	            }              
            } else {
                this[method] = obj[method];
            }
        }
    }
    catch(err) {
        microstrategy.errors.log(err);
    }
}

mstrBoneImpl.prototype.updateFeatures = function(obj) {
    //@class=mstrBoneImpl;@method=updateObject;
    try {
        var featuresObj = obj && obj["features"];
        
        if(featuresObj){
	        for (var featureName in featuresObj) {
	            this.setFeature(featureName, featuresObj[featureName]);
	        }
        }
    }
    catch(err) {
        microstrategy.errors.log(err);
    }
}

mstrBoneImpl.prototype.setTimeout = function(condition, methodToExecute, timeOut) {
    //@class=mstrBoneImpl;@method=setTimeout;
    try {
        if (eval(condition)){
            eval(methodToExecute);
        }
        else {
            methodToExecute = methodToExecute.replace(/"/g, "\\\"");
            condition = condition.replace(/"/g, "\\\"");
            window.setTimeout("microstrategy.bone(\"" + this.id + "\").setTimeout(\"" + condition + "\", \"" + methodToExecute + "\"," + timeOut + ");", timeOut);
        }
    }
    catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}

//mstrBoneImpl.prototype.toString = function() {
//    //@class=mstrBoneImpl;@method=toString;
//    try {
//        var className = "";
//        var s = this.id + "\n\n";
//        for (var prop in this) {
//            if (typeof(this[prop]) != "function") {
//                s += prop + ": " + this[prop] + "\n";
//            }
//        }
//        return s;
//    }
//    catch(err) {
//        microstrategy.errors.log(err);
//        return this.id;
//    }
//}

mstrBoneImpl.prototype.isFeatureAvailable = function(name){
    //@class=mstrBoneImpl;@method=isFeatureAvailable;
    try {
        var __result = true;
        if ((this.features != null) && typeof(this.features[name]) != 'undefined')
            __result = this.features[name];

        return __result;
    }
    catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}

mstrBoneImpl.prototype.hasFeatures = function(){
    //@class=mstrBoneImpl;@method=hasFeatures;
    try {
        for (var id in this.features) {
            return true;
        }
        return false;
    }
    catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}

mstrBoneImpl.prototype.setFeature = function(name, value){
    //@class=mstrBoneImpl;@method=setFeature;
    try {
        if (this.features != null) {
            this.features[name] = value;
        }
    }
    catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}

mstrBoneImpl.prototype.attachWinListener = function(listener, eventName, callbackMethodName, replace){
	//@class=mstrBoneImpl;@method=attachWinListener;
	try{
		if((!listener.id ||!microstrategy.bone(listener.id))){
            if (!listener.id && listener.path){
                listener.id = listener.path;
            }
            mstr.controllers.Factory.add(listener);
        }
	    if(!this.winListeners[listener.id]){
	    	this.winListeners[listener.id] = {};
	    }
        
        if (typeof(replace) == 'undefined' || replace || (!this.winListeners[listener.id][eventName])) {
	        this.winListeners[listener.id][eventName] = callbackMethodName;
		    if (typeof(mstr) != 'undefined') {
	            //if (typeof(console) != 'undefined') console.log('ATTACHING :::: listener - ' + listener.id + ' :: event  -' + eventName + '  :: callbackMethodName - ' + callbackMethodName);
                mstr.controllers.Factory.registerWindow(window);
	            mstr.controllers.EventManager.attachWindowEventListener(listener, eventName, callbackMethodName);
	        }
        } 
        //else {
        //    if (typeof(console) != 'undefined') console.log('SKIPPING :::: listener - ' + listener.id + ' :: event  -' + eventName + '  :: callbackMethodName - ' + callbackMethodName);
        //}
	}
	catch(err) {
        microstrategy.errors.log(err);
        return false;
	}
}

/**
 * Binding events to listeners.
 * @param {Object} t The target object (mostly bones) that listens to the events.
 * @param {Array} e A map object with event name as its key and the associated actions as its value. 
 * For example, {'mouseup': {s: 'ondrag', a: 'return ;'}, 'mousemove': {s: 'ondrag', a: 'return ;'}}; 
 * @param {String} e.key Event name. For example, 'mousemove' or 'mouseup'.
 * @param {String} e[key].s JUIL event handler name on the target object. For example, 'ondrag'.
 * @param {String} e[key].a Bone event action name. For example, "return microstrategy.bone('" + this.id + "').ondrag(e);".
 */
mstrBoneImpl.prototype.bindingEvents = function(t, e) {
    if (typeof(mstr) != 'undefined') {
        for(var i in e) {
            this.attachWinListener(t, i, e[i].s);
        }
     } else {
         for(var i in e) {
             document['on'+i] = new Function("e", e[i].a);
         }
     }    
}

mstrBoneImpl.prototype.detachWinListener = function(listener, eventName) {
	//@class=mstrBoneImpl;@method=detachWinListener;
	try {
		var listenerId = (listener.id)? listener.id:listener;
		if (!this.winListeners[listenerId]) return;
        if(eventName) {			//if eventName is specified just detach the individual event
           //if (typeof(console) != 'undefined') console.log('DETACHING :::: listener - ' + listenerId + ' :: event  -' + eventName); 
           mstr.controllers.EventManager.detachWindowEventListenerById(listenerId, eventName);
	       delete this.winListeners[listenerId][eventName];
	    } else {                //if eventName is not specified detach all the events registered by the listener 
	       for(var event in this.winListeners[listenerId]){
               if (typeof(mstr) != 'undefined') {
                  // if (typeof(console) != 'undefined') console.log('DETACHING :::: listener - ' + listenerId + ' :: event  -' + event);
    	           mstr.controllers.EventManager.detachWindowEventListenerById(listenerId, event);
               }
	       }
	       delete this.winListeners[listenerId];
	    }
	}
	catch(err) {
        microstrategy.errors.log(err);
        return false;
	}
}

/**
 * Initializes the event. If the mouse flag parameter is provided, calls getMouse(e) function to set 
 * the global variables.
 * @param {HTMLEvent|Event} e An HTMLEvent or an mstr event object {@link mstr.lang.Event}.
 * @param {Boolean} [mouse=false] If the value is set to be true, getMouse(e) will be called to set global
 * variables lMouseX and lMouseY.
 */
mstrBoneImpl.prototype.initEvent = function(e, mouse) {
    e = e || window.event;
    if (e.e) e = e.e;
    
    if(mouse) {
        getMouse(e); 
    }
    
    return e;    
}

/**
 * @class The base class for all Bones.
 * @param {String} id The ID of the HTMLElement that visually represents this bone.
 */
function mstrBoneImpl(id) {
    //@class=mstrBoneImpl;@method=mstrBoneImpl;
    try {
        this.isLoaded = false;
        this.inReload = false;
        this.isPostLoaded = false;
        this.primaryBone = false;
        this.features = {};

        this.id = id;
        return this;
    }
    catch(err) {
        microstrategy.errors.log(err);
        return false;
    }
}



