/**
@fileOverview
Part of the QlikView WorkBench Javascript Library (http://www.qlikwebworkbench.com)<br /><br />
Copyright (c) 2009 QlikTech International AB (http://www.qliktech.com)<br /><br />
All Rights Reserved. Email support@qliktech.com for licensing and support information.<br /><br />
@author <a href="mailto:email@industrialcodebox.com">Industrial CodeBox</a>
*/

/**    
Parent namespace which holds all QlikView WorkBench related functionality.<br />
@namespace
@ignore
*/
Qww = {};
Qww.Ctls = {};

Qww.Graph = {};

/**
Constructs a new Qww.Graph.Mgr button instance.
@class JavaScript class to manage communication with an underlying QlikView chart object. Note that 
currently this chart will be read only (it will not support drag selection).<br />
@param {Object} cfg JSON object to configure ButtonMgr.
@param {String} cfg.ObjectID Id of the chart object in the QlikView document.
@param {String} [cfg.ApplicationID=null] Id/Name of the QlikView document. Note this is <strong>only</strong> necessary when 
multiple QlikView documents are being used on the same web page and should otherwise be set to null.
@param {Function} [cfg.OnUpdate] Function to call whenever an update is received for the chart. Should have the signature 
cfg.OnUpdate(graphMgr, caption, queryStringToChartImage). Note that the queryStringToChartImage needs to be appended to the url to the 
datapump currently being used - e.g. "QvsViewClientEx.ashx" + queryStringToChartImage.
*/
Qww.Graph.Mgr = function(cfg) {

    var _initialised = false;

    var me = this;

    /** 
    Whether the chart is enabled (visible) within the QlikView document. This can be dependent, 
    for example, upon show and hide conditions set for the object in QlikView.
    @deprecated Use Mode.
    @type Bool
    */
    this.Enabled = true;

    /** 
    The state of the object. This can be dependent, for example, upon show and hide conditions set 
    for the object in QlikView.
    @type Qww.QlikViewObjectState
    */
    this.Mode = null;

    /**
    @ignore
    Returns an array of Qww.QvsConnectorSet objects which represents the initiasation which 
    needs to be sent to QlikView server in order to initialise this object.
    @returns {Qww.QvsConnectorSet[]} Array of Qww.QvsConnectorSet objects which represents 
    the initiasation for this object.
    */
    this.GetInitialisationSets = function() {

        var sets = [];

        var objId = cfg.ObjectID;
        var appId = cfg.ApplicationID;

        sets.push(new Qww.QvsConnectorSet(appId, objId, "add", "mode;text;fixedrows;choice;pageoffset;pagesize;totalsize", false));
        sets.push(new Qww.QvsConnectorSet(appId, objId + ".Caption", "add", "mode;text", false));
        sets.push(new Qww.QvsConnectorSet(appId, objId + ".Head", "add", "mode;text", false));
        sets.push(new Qww.QvsConnectorSet(appId, objId + ".Body", "add", "mode;text", false));
        sets.push(new Qww.QvsConnectorSet(appId, objId + ".Graph", "add", "mode;text;ie6false", true));

        return sets;
    };

    function initialise() {

        //var graph = qwwHub.DoAvqSelect(cfg.ApplicationID, cfg.ObjectID);

        if (_initialised == false) {

            qwwHub.DoAllSets(me.GetInitialisationSets());

            _initialised = true;
        }
    };

    this.OnAvqUpdateComplete = function() {

        initialise();

        debugger;
        var node = qwwHub.DoAvqSelect(cfg.ApplicationID, cfg.ObjectID);

        if (node == null) return;

        var captionNode = jQuery("value[name=Caption]", node);

        if (captionNode.length > 0)
            var caption = captionNode[0].getAttribute("label");

        var stampNode = jQuery("value[name=Graph]", node);

        if (stampNode.length > 0)
            var stamp = stampNode[0].getAttribute("stamp");

        me.Enabled = (node.getAttribute("mode") == "enabled");
        me.Mode = node.getAttribute("mode");

        //http://cblaptop/QvAJAXZfc/QvsViewClient.asp?mark=41164352886FEB59&datamode=binary&ident=null&stamp=c0d2f64e58483dbcb7ffd788cbbac956&view=kiva&autoview=SH03&name=Document.CH08.Graph&width=978&height=217
        //http://cblaptop/QvAJAXZfc/QvsViewClient.asp?datamode=binary&stamp=c0d2f64e58483dbcb7ffd788cbbac956&view=kiva&name=Document.CH08.Graph&width=978&height=617
        //                       QvsViewClientEx.ashx?datamode=binary&stamp=4a08985cb8b08528fac835b8b48f055f&view=Entertainment&name=Document.CHRELEASESPERYEAR.Graph&width=400&height=300

        if (cfg.OnUpdate)
            cfg.OnUpdate(me, caption, "?qwwjsonwc=true&datamode=binary&stamp=" + stamp + "&view=" + cfg.View + "&name=Document." + cfg.ObjectID + ".Graph");
    }

    qwwHub.Register(this);
};

Qww.GetSummary = function(arrayOfKeys, object) {

    var s = "";

    for (var i = 0; i < arrayOfKeys.length; i++) {

        s += "<strong>" + arrayOfKeys[i] + "</strong>:" + object[arrayOfKeys[i]];

        if (i < arrayOfKeys.length - 1) s += ", ";
    }

    return s;
};

Qww.Helper = {};

Qww.Helper.GetQueryVariable = function(variable) {
    var query = window.location.search.substring(1);
    var vars = query.split("&");
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split("=");
        if (pair[0] == variable) {
            return pair[1];
        }
    }
    return null;
}

Qww.Helper.DoesArrayContainReference = function(arr, ref) {

    var arrLength = arr.length;

    for (var i = 0; i < arrLength; i++) {
        if (arr[i] == ref)
            return true;
    }

    return false;
};



/**
Represents the caption of a QlikView object (for example Chart, Table, TextBox etc.).
@class Represents the caption of a QlikView object (for example Chart, Table, TextBox etc.)<br />
*/
Qww.ObjectCaption = function() {

    /**
    Whether the caption is visible.
    @deprecated Use Mode.
    @type Bool
    */
    this.Enabled = true;

    /**
    Text for the caption.
    @type String
    */
    this.Text = "";

    /** 
    The state of the object. This can be dependent, for example, upon show and hide conditions set 
    for the object in QlikView.
    @type Qww.QlikViewObjectState
    */
    this.Mode = null;
};

Qww.ObjectCaption.prototype.ParseFromXml = function(elem) {

    var captionElem = jQuery("value[name=Caption]", elem);

    if (captionElem.length == 1) {
        elem = captionElem[0];
    }

    this.Enabled = (elem.getAttribute("mode") == "enabled");

    if (this.Mode != Qww.QlikViewObjectState.Hidden) {
        this.Text = elem.getAttribute("label");
    }

};

/** 
Represents the set of arguments which should be sent to QVS, for example as seen in the xml sent 
to the qvpx protocol: "<set name='object' " LHS='RHS' />"
@class
Represents the set of arguments which should be sent to QVS, for example as seen in the xml sent 
to the qvpx protocol: "<set name='object' " LHS='RHS' />"
@param {String} application Application name which the object belongs to. Can use 
null if there is only one document being connected to on the page.
@param {String} objectId Object ID of the object in the QlikView document.
@param {String} lhs Left hand side of command.
@param {String} rhs Right hand side of command.
@param {Boolean} [isFinal=false] Whether this is the final command in a set.
*/
Qww.QvsConnectorSet = function(application, objectId, lhs, rhs, isFinal) {

    this.Application = application;
    //this.Object = cfg.Application + "." + object;
    this.Object = objectId;
    this.LHS = lhs;
    this.RHS = rhs;
    this.IsFinal = (isFinal == null) ? false : isFinal;
}

/** 
Converts this logical Set command to an actual xml string to be sent to QVS.
@returns {String} XML string representing the command.
*/
Qww.QvsConnectorSet.prototype.ToXml = function() {
    return "<set name=\"" + this.Object + "\" " + this.LHS + "=\"" + this.RHS + "\" />";
}

/** 
Converts this logical Set command to a shorthand summary with : delimeters.
@param {String} lastObject The name of the last object which this method was called. When 
concatenating lots of short hand strings this allows the object ID to be omitted in all 
but the first shorthand representation allowing the string to be shorter.
@returns {String} Shorthand summary string representing the command.
*/
Qww.QvsConnectorSet.prototype.ToShortHand = function(lastObject) {

    if (lastObject == this.Object)
        return ":" + this.LHS + ":" + this.RHS;
    else
        return this.Object + ":" + this.LHS + ":" + this.RHS;
}

QwwJs = {}

//QwwJs.EnsureIdHasHash = function(id) {
//if (id.substring(0, 1) == "#")
//return id;
//else
//return "#" + id;
//};

//QwwJs.EnsureIdDoesntHaveHash = function(id) {
//if (id.substring(0, 1) == "#")
//return id.substring(1, id.length - 1);
//else
//return id;
//};

QwwJs.Alert = function(msg) {
    alert("QWW: " + msg);
};


/**
Creates a new Hub object. <strong>You should NOT create this, a single 
instance is automatically created named qwwHub.</strong>
@constructor
@class Central hub class which other controls and functionality can register with.<br />
*/
Qww.Hub = function() {

    var objectsToCallOnAvqUpdateCompleteOn = new Array();
    var objectsToCallAllOnAvqUpdateCompletesCalledOn = new Array();
    var objectsToCallOnAvqUpdateBeginOn = new Array();
    var objectsToOnDocumentLoadedOn = new Array();
    var qwwLoggerObjects = new Array();

    var allRegisteredObjects = [];

    var me = this;

    var regsiteredControls = new Array();

    this.KeepSessionAliveTimeoutInSeconds = 120;
    var dataSourceIdDictionary = {};
    var defaultApplication;

    var qvDirectConnectors = {};

    this.CustomObjectsInitialised = false;

    /** 
    Whether the Hub should start the QVS communication.
    @type Bool
    @default true
    */
    this.HubShouldStartClient = true;

    /**
    Adds a logged object to the hub whose Trace, Warn and Error methods will be called as appropriate.
    @param {Logger} logger Logger to register with the hub
    @ignore
    @example
    qwwHub.AddLogger(new MyLogger());
    */
    this.AddLogger = function(obj) {
        var arr = qwwLoggerObjects;

        if (!Qww.Helper.DoesArrayContainReference(arr, obj))
            arr[arr.length] = obj;
    };

    this.AddObjectToCallOnAvqUpdateBegin = function(obj) {
        var arr = objectsToCallOnAvqUpdateBeginOn;

        if (!Qww.Helper.DoesArrayContainReference(arr, obj))
            arr[arr.length] = obj;
    };

    this.AddObjectToCallOnAvqUpdateComplete = function(obj) {
        var arr = objectsToCallOnAvqUpdateCompleteOn;

        if (!Qww.Helper.DoesArrayContainReference(arr, obj))
            arr[arr.length] = obj;
    };

    this.AddObjectToCallOnDocumentLoadedOn = function(obj) {
        var arr = objectsToOnDocumentLoadedOn;

        if (!Qww.Helper.DoesArrayContainReference(arr, obj))
            arr[arr.length] = obj;
    };

    this.AddObjectToCallOnAllAvqUpdateCompletesCalled = function(obj) {
        var arr = objectsToCallAllOnAvqUpdateCompletesCalledOn;

        if (!Qww.Helper.DoesArrayContainReference(arr, obj))
            arr[arr.length] = obj;
    };

    this.TrackAction = function(applicationId, actionName, actionDetail) {

        try {
            if (this.OnTrackAction) {
                this.OnTrackAction(applicationId, actionName, actionDetail);
            }
        }
        catch (e) { }
    };

    /**
    Registers a JavaScript object with the hub.
    @param {Object} obj Object which implements any of the following:
    .OnAvqUpdateComplete
    .OnAvqUpdateBegin
    .OnDocumentLoaded
    @example
    
    this.OnAvqComplete = function(){alert("QVS Updated");};
    this.OnAvqUpdateBegin = function(){alert("QVS About to update");};
    this.OnDocumentLoaded = function(){alert("Document loaded");}
    
    qwwHub.Register(this);
    */
    this.Register = function(obj) {
        if (Qww.Hub.StartMode != Qww.Hub.StartModes.NoQvsClient) {
            if (obj.Cfg && obj.Cfg.ObjectID) {
                if (obj.Cfg.ApplicationID) {
                    obj.Binder = this.GetQvaFromApplicationName(obj.Cfg.ApplicationID);
                }
                else {
                    obj.Binder = this.GetQvaFromApplicationName(defaultApplication);
                }

                if (obj.Binder) {
                    new QvaMgrExternal(obj);
                }
                else {
                    //debugger;
                }
            }

            if (obj.Binder == null) {
                if (obj.OnAllAvqUpdateCompletesCalled) {
                    this.AddObjectToCallOnAllAvqUpdateCompletesCalled(obj);
                }
            }
        }
        else {

            if (obj.OnAvqUpdateComplete) {
                this.AddObjectToCallOnAvqUpdateComplete(obj);
            }

            if (obj.OnAllAvqUpdateCompletesCalled) {
                this.AddObjectToCallOnAllAvqUpdateCompletesCalled(obj);
            }
        }


        if (obj.OnAvqUpdateBegin)
            this.AddObjectToCallOnAvqUpdateBegin(obj);

        if (obj.OnDocumentLoaded)
            this.AddObjectToCallOnDocumentLoadedOn(obj);


        allRegisteredObjects.push(obj);

    };

    function callLoggingMethod(method, msg) {
        var arr = qwwLoggerObjects;

        var arrLength = arr.length;

        for (var j = 0; j < arrLength; j++) {

            var o = arr[j];

            if (o[method])
                o[method](msg);
        }
    };

    this.Trace = function(msg) {
        callLoggingMethod("Trace", msg);
    };

    this.Error = function(msg) {
        callLoggingMethod("Error", msg);
    };

    this.CallAllOnDocumentLoadedHandlers = function() {

        var arr = objectsToOnDocumentLoadedOn;

        var arrLength = arr.length;

        for (var j = 0; j < arrLength; j++) {
            var obj = arr[j];

            if (obj.OnDocumentLoaded)
                obj.OnDocumentLoaded();
        }
    };

    //var _isWorkBenchOnlyConnectorInBatchMode = false;

    this.CallAllOnAvqUpdateCompleteHandlers = function() {

        //        if (me.CustomObjectsInitialised == false) {
        //            //
        //            // When used in conjunction with the QvAjax???.js framework, initialising
        //            // all objects in the initial request (i.e. custom with native) the very
        //            // first update from the server didnt seem to fire the OnUpdate event meaning,
        //            // for example, that the flexi grid wouldn't paint until the first selection
        //            // had been made.
        //            //
        //            this.InitialiseCustomObjects();
        //        }

        var arr = objectsToCallOnAvqUpdateCompleteOn;

        //_isWorkBenchOnlyConnectorInBatchMode = true;

        var arrLength = arr.length;

        for (var j = 0; j < arrLength; j++) {
            var o = arr[j];

            if (o.OnAvqUpdateComplete)
                o.OnAvqUpdateComplete();
        }

        var arr = objectsToCallAllOnAvqUpdateCompletesCalledOn;

        var arrLength = arr.length;

        for (var j = 0; j < arrLength; j++) {
            var o = arr[j];

            if (o.OnAllAvqUpdateCompletesCalled)
                o.OnAllAvqUpdateCompletesCalled();
        }

        //        //for (var w = 0; w < qvDirectConnectors.length; w++)
        //        if (qvDirectConnectors[defaultApplication] != null)
        //            qvDirectConnectors[defaultApplication].Send();

        //_isWorkBenchOnlyConnectorInBatchMode = false;
    };


    this.CallAllOnAvqUpdateBeginHandlers = function() {
        var arr = objectsToCallOnAvqUpdateBeginOn;
        var arrLength = arr.length;

        for (var j = 0; j < arrLength; j++) {
            var o = arr[j];

            if (o.OnAvqUpdateBegin)
                o.OnAvqUpdateBegin();
        }
    };

    this.OnError = function(msg) {
        alert("QWW Error: " + msg);
        callLoggingMethod("Error", msg);
    };

    this.InitialiseCustomObjects = function() {

        var objectsWithInitialisation = [];

        var arrLength = allRegisteredObjects.length;

        for (var j = 0; j < arrLength; j++) {
            var o = allRegisteredObjects[j];

            if (o.GetInitialisationSets) {
                objectsWithInitialisation.push(o);
            }
        }

        var usingJson = this.Mode == Qww.QvsConnectorMode.CrossDomainUsingJquerygetJSON;
        var usingNativeQvsObjects = (Qww.Hub.StartMode != Qww.Hub.StartModes.NoQvsClient);

        var arrLength = objectsWithInitialisation.length;

        for (var k = 0; k < arrLength; k++) {

            var ignoreIsFinal = this.Mode != Qww.QvsConnectorMode.CrossDomainUsingJquerygetJSON;

            if (k == objectsWithInitialisation.length - 1) {
                if (usingJson == false) {
                    if (usingNativeQvsObjects == true)
                        ignoreIsFinal = true; // because Qva.Start will kick things off
                    else
                        ignoreIsFinal = false; // we want the last isFinal =  true to get sent to the server.
                }
                else {
                    ignoreIsFinal = false; // if we are using json we want the final request for each individual object to be sent.
                }
            }

            this.DoAllSets(objectsWithInitialisation[k].GetInitialisationSets(), ignoreIsFinal);
        };

        this.CustomObjectsInitialised = true;
    };

    this.Start = function() {

        this.InitialiseCustomObjects();

        if (Qww.Hub.StartMode == Qww.Hub.StartModes.NoQvsClient) {

        }
        else if (Qww.Hub.StartMode == Qww.Hub.StartModes.Pre8_5) {

            Avq.Start();

            var timoutInSeconds = this.KeepSessionAliveTimeoutInSeconds * 1;

            if (timoutInSeconds > -1)
                setInterval(function() { avqSet("", "keepalive", "true", true); }, timoutInSeconds * 1000);
        }
        else {
            if (me.HubShouldStartClient == true)
                Qva.Start();
        }
    };

    /**
    Sends through a collection of logical sets to QlikView Server.
    @param {Qww.QvsConnectorSet[]} An array of logical sets to be sent to the server.
    @param {Boolean} Whether this is the final 'set of sets' to be sent (if it is not even 
    if the last set in this batch has isFinal=true, this will be ignored.
    */
    this.DoAllSets = function(arrayOfSets, ignoreIsFinal) {

        var arrLength = arrayOfSets.length;

        for (i = 0; i < arrLength; i++) {

            var s = arrayOfSets[i];

            if (ignoreIsFinal == null || ignoreIsFinal == false) {
                this.DoAvqSet(s.Application, s.Object, s.LHS, s.RHS, s.IsFinal);
            }
            else {
                this.DoAvqSet(s.Application, s.Object, s.LHS, s.RHS, false);
            }
        }
    };

    /** 
    Sends through a set command tto QlikView Server. e.g. 
    &lt;set name="Application.Object" add="text,mode" /&gt;
    @param {String} applicationID Application which object applied to. Set to null if you are not connecting 
    to multiple documents.
    @param {String} object Object identifier which set applies to.
    @param {String} lhs Left hand side argument
    @param {String} rhs Right hand side argument
    @param {Bool} isFinal Whether to send this and any other previously Set commands
    (where isFinal was set to false) now.
    */
    this.DoAvqSet = function(applicationID, objectId, lhs, rhs, isFinal) {
        if (isFinal == null)
            isFinal = true;

        if (applicationID == null || applicationID == "null" || applicationID == "undefined")
            applicationID = defaultApplication;

        if (Qww.Hub.StartMode == Qww.Hub.StartModes.NoQvsClient) {

            //if (_isWorkBenchOnlyConnectorInBatchMode == true) isFinal = false;

            //qvDirectConnectors[applicationID].Set(objectId, lhs, rhs, isFinal);

            //this.GetQvaFromApplicationName(applicationID).Set(objectId, lhs, rhs, isFinal);
            this.GetQvaFromApplicationName(applicationID).Set(objectId, lhs, rhs, isFinal);
        }
        else if (Qww.Hub.StartMode == Qww.Hub.StartModes.V8_5) {

            //            var dataSourceId = dataSourceIdDictionary[applicationID];

            //            var qva = Qva.GetBinder(dataSourceId);

            var qva = this.GetQvaFromApplicationName(applicationID);

            if(!qva) qva = this.GetQvaFromApplicationName(defaultApplication);

            if (qva) {

                var prefix = "";

                if (objectId != null && objectId.substring(0, 9) != "Document.")
                    prefix = "Document.";

                if (objectId == "bookmark-apply") prefix = "";

                //qva.Set("Document." + objectId, lhs, rhs, isFinal);
                qva.Set(prefix + objectId, lhs, rhs, isFinal);
            }
            else
                Qww.Hub.Break(); // shouldnt be null.
        }
        else {

            var id = AvqView + "." + objectId;

            avqSet(id, lhs, rhs, isFinal);
        }
    };

    this.CreateBookMark = function(applicationID, name, onUpdate) {

        if (applicationID == null || applicationID == "null")
            applicationID = defaultApplication;

        //var dataSourceId = dataSourceIdDictionary[applicationID];

        //var qva = this.GetQvaFromDataSourceID(dataSourceId);
        var qva = this.GetQvaFromApplicationName(applicationID);

        // TODO remove cookie and scope? scope is obsolete and need more info on cookie.
        //var xml = "<update mark=\"\" stamp=\"\" cookie=\"true\" scope=\"Document\" session=\"" + qva.Session + "\" view=\"" + qva.View + "\" ident=\"new:Document.ActiveSheet.StandardActions\" kind=\"bookmark_obj\"></update>";
        var xml = "<update mark=\"\" stamp=\"\" cookie=\"true\" scope=\"Document\" view=\"" + qva.View + "\" ident=\"new:Document.ActiveSheet.StandardActions\" kind=\"bookmark_obj\"></update>";

        var dataPumpUrl = qva.Url + "?mark=&view=" + qva.View;

        jQuery.ajax({
            type: "POST",
            url: dataPumpUrl,
            dataType: "xml",
            data: xml,
            success: function(res) {

                var id = jQuery("value[name='Id']", res).attr("value");

                var x = "<update mark=\"" + qva.Mark + "\" stamp=\"" + qva.Stamp + "\" cookie=\"true\" scope=\"Document\" session=\"" + qva.Session + "\" view=\"" + qva.View + "\" ident=\"" + id + "\" kind=\"bookmark_obj\">";

                x += "<set name=\"Bookmark.Name\" text=\"" + name + "\"/>";
                x += "<set name=\"Bookmark.InfoText\" text=\"" + name + "\"/>";
                x += "<set name=\"Bookmark.ApplyInputFieldValues\" value=\"1\"/>";
                x += "<set name=\"Bookmark.Share\" value=\"1\"/>";
                x += "<set name=\"Bookmark.CreateFromActiveObject\" action=\"\"/>";
                x += "<set name=\"Document.Nothing\" add=\"Nothing\"/></update>";

                jQuery.ajax({
                    type: "POST",
                    url: dataPumpUrl,
                    dataType: "xml",
                    data: x,
                    success: function(res) {

                        var bookMarkId = jQuery("value[name='BookmarkId']", res).attr("value");

                        onUpdate(bookMarkId);

                    }
                });
            }
        });
    };

    /**
    Returns the  specified XML element from the latest set of results.
    @param {String} applicationID Application which object applied to. Set to null if you are not connecting 
    to multiple application.
    @param {String} objectId Path to the object to return from the latest results.
    */
    this.DoAvqSelect = function(applicationID, objectId) {

        if (applicationID == null || applicationID == "null")
            applicationID = defaultApplication;

        if (Qww.Hub.StartMode == Qww.Hub.StartModes.NoQvsClient) {
            //return qvDirectConnectors[applicationID].DoAvqSelect(objectId);
            return this.GetQvaFromApplicationName(applicationID).DoAvqSelect(objectId);
        }
        else if (Qww.Hub.StartMode == Qww.Hub.StartModes.V8_5) {

            //var dataSourceId = dataSourceIdDictionary[applicationID];

            //var qva = Qva.GetBinder(dataSourceId);

            var qva = this.GetQvaFromApplicationName(applicationID);

            if (!qva) qva = this.GetQvaFromApplicationName(defaultApplication);

            if (qva)
                return qva.Select("Document." + objectId);
            else
                Qww.Hub.Break(); // shouldnt be null.
        }
        else
            return AvqSelect(AvqView + "." + objectId);
    };

    /** 
    This sets up a connection with a QlikView document using only the 
    QlikView WorkBench JavaScript files (in other words meaning you don't need 
    to reference any of the Qv????.js files which ship with QlikView Server. This however 
    means that you can only use the controls and features available in the QlikView WorkBench and, 
    for example, do not have access to the 'native' QlikView Server ZFC controls (listbox, table, etc.).    
    @param {Object} cfg JSON Configuration object.
    @param {String} cfg.QlikViewDocument Name of the QlikView document.
    @param {Qww.QvsConnectorMode} [cfg.Mode=Qww.QvsConnector.Mode.Direct] Connection method to use.
    @param {Functin} [cfg.OnUpdateComplete] Function to call when update is received. Accepts one 
    argument which is a reference to the new data.
    @example   
    &lt;script src="../../Javascript/QWW/jquery.js" type="text/javascript"&gt;&lt;/script&gt;
    &lt;script src="../../Javascript/QWW/QwwAllCompressed.js" type="text/javascript"&gt;&lt;/script&gt;
    
    qwwHub.RegisterWorkBenchOnlyDocument({QlikViewDocument: "Entertainment"});
    
    var myFlexiGridCtlMgr = new Qww.Ctls.FlexiGrid.Mgr(
    {   
    "ObjectID" : 'TBDEMO_RESULTS',
    "OnRenderRecord" : onRenderRecord,
    // .... Other config ...
    })
    */
    this.RegisterWorkBenchOnlyDocument = function(cfg) {

        Qww.Hub.StartMode = Qww.Hub.StartModes.NoQvsClient;

        this.Mode = cfg.Mode;

        if (defaultApplication == null)
            defaultApplication = cfg.QlikViewDocument;

        if (qvDirectConnectors[cfg.QlikViewDocument] != null) {
            Qww.Hub.Break();
        }
        else {

            var conn = new Qww.QvsConnector(cfg);

            qvDirectConnectors[cfg.QlikViewDocument] = conn;

            //            conn.Cfg.OnUpdateBegin = function(data) {
            //                me.CallAllOnAvqUpdateBeginHandlers();
            //            };

            //            conn.Cfg.OnUpdateComplete = function(data) {
            //                me.CallAllOnAvqUpdateCompleteHandlers();
            //            };

            conn.Cfg.OnUpdateBegin = me.CallAllOnAvqUpdateBeginHandlers;
            conn.Cfg.OnUpdateComplete = me.CallAllOnAvqUpdateCompleteHandlers;
        }

        return qvDirectConnectors[cfg.QlikViewDocument];
    };

    //    this.GetQvaFromDataSourceID = function(dataSourceId) {
    //        var binder = Qva.GetBinder(dataSourceId);
    //        return binder;
    //    };

    this.GetQvaFromApplicationName = function(applicationName) {

        if (applicationName == null || applicationName == "null") {
            applicationName = defaultApplication;
        }

        if (Qww.Hub.StartMode == Qww.Hub.StartModes.NoQvsClient) {
            return qvDirectConnectors[applicationName];
        }
        else if (Qww.Hub.StartMode == Qww.Hub.StartModes.V8_5) {

            var dataSourceId = dataSourceIdDictionary[applicationName];

            return Qva.GetBinder(dataSourceId);
        }
        else
            return null;
    };


    /**
    Registers a connection with a QlikView document on the server. NOTE that 
    the QvDataSource ASP.NET control calls this method with the approriate arguments. This connection 
    method depends on the  various Qv???.js files shipped with QlikView Server. See {@link Qww.Hub#RegisterWorkBenchOnlyDocument} 
    for a 'light weight' connection method    
    @param {Object} cfg JSON Configuration object.
    @param {String} cfg.QlikViewDocument Name of the QlikView document.
    @param {String} [cfg.AvqAutoview] This is an optional property which allows the 
    server to respond with only the data that is used on a single page to minimize the traffic to the client
    @param {Array} [cfg.CustomIcons] Array of custom icons expressed as an array of {IconCode:'?', ImageUrl:'?'} objects. For 
    example to override the search icon in listboxes specifiy [{IconCode:'CSE', ImageUrl:'images/search.png'}].
    @param {String} [cfg.DataPump=""QvsViewClientEx.ashx"]
    @param {Boolean} [cfg.ShowDefaultRightClickMenu=false] Whether to display the native right click menu.
    @param {Function} showMessageFunction Function to call to display messages (errors, session timeouts etc.) 
    from QlikView Server.
    */
    this.RegisterDocument = function(cfg, showMessageFunction) {

        Qww.Hub.StartMode = Qww.Hub.StartModes.V8_5;

        try {
            var qva = null;

            if (cfg.QvDataSourceID) {

                var qva = Qva.GetBinder(cfg.QvDataSourceID);

                if (qva != null)
                    return qva;

                qva = new Qva.PageBinding(cfg.QvDataSourceID);

                dataSourceIdDictionary[cfg.QlikViewDocument] = cfg.QvDataSourceID;
            }
            else
                qva = new Qva.PageBinding();

            qva.View = cfg.QlikViewDocument;

        
            if (!qva.View) qva.View = cfg.QvApplicationFile;
            qva.IsContained = true;

            if (defaultApplication == null)
                defaultApplication = cfg.QlikViewDocument;

            //if (cfg.AvqAutoview && cfg.AvqAutoview != '')
            qva.Autoview = cfg.AvqAutoview;

            qva.Modal = new Qva.Modal(qva);
            //qva.JSON = true;

            //
            //  Register customer icons.
            //
            if (cfg.CustomIcons != null) {
                for (var i = 0; i < cfg.CustomIcons.length; i++) {
                    var customIcon = cfg.CustomIcons[i];
                    qva.CustomIcons[customIcon.IconCode] = customIcon.ImageUrl;
                };
            };

            if (showMessageFunction)
                qva.ShowMessage = showMessageFunction;

            if (cfg.ShowDefaultRightClickMenu == null || cfg.ShowDefaultRightClickMenu == false) {
                qva.OnCreateContextMenu = function() { };
            }

            if (cfg.DebugSupport == true) {
                qva.Trace = new Qva.Trace(qva);
                qva.DeveloperMode = true;
            }

            //qva.ScriptPath = "./"
            //qva.OnUpdateComplete = function() { me.CallAllOnAvqUpdateCompleteHandlers(); }
            //            qva.OnNewResults = function() { me.CallAllOnAvqUpdateCompleteHandlers(); }

            qva.OnUpdateComplete = me.CallAllOnAvqUpdateCompleteHandlers;

            qva.OnUpdateBegin = function() { me.CallAllOnAvqUpdateBeginHandlers(); }

            window.onresize = function() { Qva.SetBackgroundSize(); }

            if (cfg.DataPump && cfg.DataPump != '')
                qva.Remote = cfg.DataPump;
            else
                qva.Remote = "QvsViewClientEx.ashx?QlikViewServerVersion=8_50"; // assume localhost

            new Qva.Scanner(qva);

            return qva;
        }
        catch (e) {
            alert(e.message);
        }
    };
};

Qww.Hub.ObjectsWithEventSubscriptions = [];

Qww.Hub.Subscribe = function(obj, eventName, func) {

    var foundObj = null;

    var arrLength = Qww.Hub.ObjectsWithEventSubscriptions.length;

    for (var i = 0; i < arrLength; i++) {

        var o = Qww.Hub.ObjectsWithEventSubscriptions[i];

        if (o.Obj === obj) {
            foundObj = o;
            break;
        }
    };

    if (foundObj === null) {
        foundObj = {};
        foundObj.Subs = [];
        foundObj.Obj = obj;

        if (obj[eventName] != null) {
            foundObj.Subs.push(obj[eventName]); // the event already had a handler and we dont want to lost this..
        }

        obj[eventName] = function(a, b, c, d, e, f, g, h, j) {

            var arrLength = foundObj.Subs.length;

            for (var i = 0; i < arrLength; i++) {
                try {
                    foundObj.Subs[i](a, b, c, d, e, f, g, h, j);
                }
                catch (e) {

                }
            }
        };

        Qww.Hub.ObjectsWithEventSubscriptions.push(foundObj);
    }

    foundObj.Subs.push(func);
};

// Start Modes are used to decide what code to run in the Qww.Hub.Start method.
// The global QwwJs_Hub.StartMode defaults to Pre8_5 and will be set by the QvDataSource
// by inserting the relevant javascript if the server version is set to 8.5 or above.

/**
Enumeration containing start modes for the Qww.Hub.
@class Enumeration containing start modes for the Qww.Hub.
*/
Qww.Hub.StartModes = {

    /** 
    QlikView Server 8.2 or earlier.
    @deprecated Deprecated since QVS 8.5 released.
    @constant
    */
    Pre8_5: 'Pre8_5',

    /**
    QlikView Server 8.5 (recommended/default).
    @constant
    */
    V8_5: 'V8_5',

    /**
    This start mode means that only QlikView WorkBench client JavaScript 
    files are being used. This is only valid if QlikView Server 8.5 or later 
    is being used.
    @constant
    */
    NoQvsClient: 'NoQvsClient'
}

Qww.Hub.StartMode = Qww.Hub.StartModes.V8_5;

/**
Global singleton instance of the Qww.Hub. This is created automatically.<br />
@type Qww.Hub
*/
var qwwHub = new Qww.Hub();

Qww.Hub.Break = function() { }


//debugger;

Qww.Hub.prototype.StartUp = function() {

qwwHub.Start();
qwwHub.CallAllOnDocumentLoadedHandlers();

};

Qww.Hub.ManualStart = false;

if (this.widget) {
    //debugger;
    widget.onLoad = function() {
        //debugger;
        //alert('A');
        if (Qww.Hub.ManualStart == false) {
            qwwHub.Start();
            qwwHub.CallAllOnDocumentLoadedHandlers();
        }
    }
}
else 
{

    jQuery(document).ready(function() {
        //debugger;

        if (Qww.Hub.ManualStart == false) {
            qwwHub.Start();
            qwwHub.CallAllOnDocumentLoadedHandlers();
        }

        //    if (Qww.Hub.StartMode == Qww.Hub.StartModes.NoQvsClient)
        //        qwwHub.CallAllOnAvqUpdateCompleteHandlers(); // needed to start everything moving when avq js files are not used.

    });
}

// -------------------------------------------------------

Qww.BasicControlBase = function() {
    this.ObjectName = "NotSet";
};

Qww.BasicControlBase.prototype.Trace = function(msg) {
    qwwHub.Trace(this.ObjectName + ":" + msg);
};

Qww.BasicControlBase.prototype.Error = function(msg) {
    qwwHub.Trace(this.ObjectName + ":" + msg);
};

Qww.BasicControlBase.prototype.UserAlert = function(msg) {
    alert(this.ObjectName + ":" + msg);
};


// -------------------------------------------------------

Qww.ControlBase = function(classPrefix) {
    if (classPrefix == null)
        this.ClassPrefix = "QwwJs_ControlBase";
    else
        this.ClassPrefix = classPrefix;
};

Qww.ControlBase.prototype = new Qww.BasicControlBase();

Qww.ControlBase.prototype.GetImagePath = function(imageName) {

    var websiteRoot = "";

    if (this.Cfg.WebSiteRoot.length > 0)
        websiteRoot = this.Cfg.WebSiteRoot;

    return websiteRoot + "QwwStyles/" + this.Cfg.Theme + "/" + this.ClassPrefix + "/" + imageName;
};

Qww.ControlBase.prototype.GetClassNameWithThemePostfix = function(cls) {
    if (this.Cfg.Theme.length == 0)// || this.Cfg.Theme == "Default")
        return this.ClassPrefix + "-" + cls;
    else
        return this.ClassPrefix + "-" + cls + "-" + this.Cfg.Theme;
};

Qww.ControlBase.prototype.GetFullyQualifiedElementID = function(localElementId) {
    if (this.Cfg.ElementIDPrefix.length == 0)
        return "Element" + localElementId;
    else
        return this.Cfg.ElementIDPrefix + localElementId;
};

Qww.ControlBase.prototype.GetElementFromLocalId = function(localElementId) {
    if (this.Cfg.ElementIDPrefix.length == 0)
        return document.getElementById("Element" + localElementId);
    else
        return document.getElementById(this.Cfg.ElementIDPrefix + localElementId);
};

Qww.ControlBase.prototype.GE = function(localElementId) {
    return this.GetElementFromLocalId(localElementId);
};


/*
A shortcut to GetFullyQualifiedElementID adds id='' attribute as well.
*/
Qww.ControlBase.prototype.GID = function(localElementId) {
    return "id='" + this.GetFullyQualifiedElementID(localElementId) + "'";
};

/*
A shortcut to GetClassNameWithThemePostfix with class attribute also.
*/
Qww.ControlBase.prototype.GCN = function(cls) {
    return "class=\'" + this.GetClassNameWithThemePostfix(cls) + "\'";
};


Qww.ControlBase.prototype.GetElement = function(idWithOrWithoutHash) {
    if (idWithOrWithoutHash.substring(0, 1) == "#")
        return jQuery(idWithOrWithoutHash)[0];
    else
        return document.getElementById(idWithOrWithoutHash);
};


/**
Enumeration of QlikView Object types (currently limited to only those needed by the Flexi Grid control).
@class Enumeration of QlikView Object types (currently limited to only those needed by the Flexi Grid control).<br />
*/
Qww.QlikViewObjectType = {

    /** 
    @constant
    */
    ListBox: 'ListBox',

    /** 
    @constant
    */
    Chart_Table: 'Chart_Table',

    /** 
    @constant
    */
    TableBox: 'TableBox'
};


/**
Enumeration of states which QlikView objects (or sub-objects, for example object captions) can 
be in.
@class Enumeration of states which QlikView objects (or sub-objects, for example object captions) can 
be in.<br />
*/
Qww.QlikViewObjectState = {

    /**   
    The object is visible and enabled.
    @constant
    */
    Enabled: 'enabled',

    /** 
    The object is visible and disabled.
    @constant
    */
    Disabled: 'disabled',

    /** 
    The object is hidden.
    @constant
    */
    Hidden: 'hidden'
};

/**
Enumeration of modes which can be used by the {@link Qww.QvsConnector} to connect to the QlikView Server Data Pump.
@class Enumeration of modes which can be used by the {@link Qww.QvsConnector} to connect to the QlikView Server Data Pump.<br />
*/
Qww.QvsConnectorMode = {

    /** 
    Connect directly with QlikView Server (uses jquery's ajax methods).
    @constant
    */
    Direct: 'Direct',

    /** 
    @constant
    @ignore
    */
    CrossDomainUsingJquerygetJSON: 'CrossDomainUsingJquerygetJSON'
};

QvaMgrExternal = function(mgr) {
    var cfg = mgr.Cfg;
    var objName = cfg.ObjectID;
    if (objName) {
        var binder = mgr.Binder;
        this.Name = binder.DefaultScope + "." + objName;
        this.Element = mgr;
        binder.AddManager(this);
    }
}
QvaMgrExternal.prototype.Paint = function() {
    if (this.Element.OnAvqUpdateComplete)
        this.Element.OnAvqUpdateComplete();
}

