//vPanel 4/3/2010

(function($) {


    $.fn.vPanel = function(options) {


        if (this.length > 1) {
            alert("vPanel can only be applied to a single DOM element");
            return false;
        }


        //initialisation

        if (options.name == '') {
            alert("vPanel has to have a name");
            return false;
        }


        return this.each(function() {
            var element = $(this);

            var lookfor = "vPanel-" + options.name;

            // Return early if this element already has a plugin instance
            if (element.data(lookfor)) {
                vLog('vPanel', 'already instantiated!' + lookfor);
                return;
            }
            // pass options to plugin constructor
            var vrs = new $.vPanel(this, options);

            // Store plugin object in this element's data
            element.data(lookfor, vrs);

            //register us with the page
            $(document).trigger('vnx-reg-vpanel', vrs);



        });
    }




    $.vPanel = function(element, options) {
        var $element = element;
        var Config = {
            name: '',                       //have to have a unique name for each panel
            formula: null,                  //a {} of formula name and functions that return any value

            targetID: '', 				    //the target element id where the rendering will happen
            templateID: '', 				//ID of the template to use for rendering

            renderStyle: 'replace',         //do we replace what's in the targetID [replace|before|after]
            renderStyleClearClass: '',      //if we clear by removing elements of a class

            vDatasource: '',                //name of the datasource and renderer
            vRenderer: '',                  //renderer to use
            ajaxUrl: '',                    //if neither of the above are supplied then the panel is in HTML mode

            displayfrom: 0, 				//default data to display from
            display: 999, 				    //number of items to show


            refreshonload: true, 		    //do a render on load
            refreshondelete: false,
            setselectiononload: '',         //perform a default selection on load [start|end|pagestart|pageend|current], i.e. selected index within panel

            autoupdate: false, 			    //do we update automatically when a given fragment has changed and is valid?
            updatemode: 'fragment', 		//when updating do we send the 'fragment','nexus' or the entire nexus, or 'page' for the page

            autoinsert: false,
            insertmode: 'fragment',

            onafterrefresh: null,
            onafterinsert: null,
            onafterupdate: null,                   // function to call after an update
            onafterselect: null,
            onafterdelete: null,
            onaftererror: null,
            onsetselectionatload: null,

            fragmentupdateclass: '',               // the class to use to indicate an update
            fragmentinsertclass: '',               // the class to use to indicate an update
            fragmentdeleteclass: '',               // the class to use to indicate an update

            placeholderid: '',                      // placeholderid : this element will be cloned into position if the actiontype is insert
            placeholdertargetid: '',                // placeholdertargetid : this is the position where a new placeholder will be inserted
            enableWait: true,
            selectWaitClass: '',
            selectWaitTemplateID: '',
            errortemplateID: '',

            clearoninsert: true,

            hideonnodata: false,
            showthisIDonnodata: '',
            validclass: 'valid', 		    //????? for validation
            invalidclass: 'invalid', 	    //????? for validation

            globalparamclass: '.vnx-global'         //as well as the fragments collection of form elements we can include global ones too
        };
        if (options) {
            jQuery.extend(Config, options);
        };

        var Us = this;
        var OurNexus = null;

        var Internal = {
            HTMLmode: false,
            CurrentSelectionKey: null,
            CurrentPosition: 0,
            CurrentPageSize: 10
        };


        var dc = function(a) { return document.createElement(a); };

        //functions
        //{

        this.applyto = function() {
            return Config.name;
        }

        //////////////////////////////////////////////////////////////
        // INITIALISE

        this.initialisepanel = function() {

            //keep a local instance of the pages nexus for quick access
            OurNexus = $(document).data('vnexus');
            //test that we have a datasource and renderer
            if (Config.vDatasource == '') {
                //do we have an ajaxURL
                if (Config.ajaxUrl != '') {
                    Internal.HTMLmode = true;
                }
                else {
                    //we can't operate as a panel with out something at least
                    alert('No datasource name found for this vPanel');
                }
            } else {
                if (OurNexus.getdatasource(Config.vDatasource) == null) alert('No datasource registered for this vPanel:' + Config.vDatasource);
            }
            if (Config.vRenderer == '' && !Internal.HTMLmode) {
                alert('No renderer found for this vNexus');
            } else {
                if (!Internal.HTMLmode && OurNexus.getrenderer(Config.vRenderer) == null) alert('No renderer registered for this vPanel:' + Config.vRenderer);
            }

            //register ourselves with our datasource if we have one
            if (!Internal.HTMLmode) OurNexus.getdatasource(Config.vDatasource).registerpanel(Us);

            //set up internals
            Internal.CurrentPosition = Config.displayfrom;
            Internal.CurrentPageSize = Config.display;




            //do we have a selection on load
            if (Config.setselectiononload != '') {
                //either we use the setting direct (if HTMLmode) or we ask the datasource for the Key
                Internal.CurrentSelectionKey = Internal.HTMLmode ? Config.setselectiononload : OurNexus.getdatasource(Config.vDatasource).getkeybyposition(Config.setselectiononload);
            }

            if (Config.onsetselectionatload != null) {
                //either we use the setting direct (if HTMLmode) or we ask the datasource for the Key
                Internal.CurrentSelectionKey = Config.onsetselectionatload();
            }

            //do an initial refresh if required (usually it is)
            if (Config.refreshonload) Us.doRefresh(this, this, null);


            //recalculate any calculated fields now
            Us.recalculatepanel(this, null);

            vLog('vPanel', 'Initialised ' + Us.applyto() + " isHTMLPanel: " + Internal.HTMLmode);

        }


        function getfragment(fromwhere) {
            return $(fromwhere).closest('[data-frag-id]');
        }

        function getpanel() {
            return $('[data-vpanel=' + Config.name + ']');
        }

        //this just returns whether the given fragment is valid or not
        //TODO : remove validatenow
        function isfragmentvalid(validatenow, $frag) {

            var allvalid = true;
            $($frag.find('[data-vvl]')).each(function() {
                //do we validate all of them now too?
                if (validatenow) {
                    $(document).trigger('vvl-validateone', $(this));
                }
                //are all the elements flagged as being valid?
                vLog('vPanel', 'IsFragValid:' + $(this).attr('data-vvlstate'));
                if ($(this).attr('data-vvlstate') != 'valid') {
                    allvalid = false;
                }
            });
            return allvalid;
        }

        //this creates the data to be sent to the server
        function createpostdatafromfragment(fragmentlist) {

            var a = {};
            $(fragmentlist).find('[name]').each(function() {
                var n = $(this).attr('name');
                var v = $(this).val() == '' ? $(this).html() : $(this).val();
                a[n] = v;
            });

            //add anything from the global class
            if (Config.globalparamclass != '') {
                $(Config.globalparamclass).each(function() {
                    var n = $(this).attr('name');
                    var v = $(this).val() == '' ? $(this).html() : $(this).val();
                    a[n] = v;
                });
            }

            return a;
        }

        function clearelements(fragmentlist) {
            $(fragmentlist).find(':text').each(function() {
                $(this).val('');
            });
        }

        function requestremotehtml(postparameters, sucessfunction) {
            //uses the ajaxURL to request new data
            $.ajax({
                async: true,
                url: Config.ajaxUrl,
                cache: false,
                dataType: 'html',
                success: function(html) {
                    vLog('vPanel', 'requestremotehtml Got ASYNC Data : ' + Us.applyto());
                    //with it done then call the function passed
                    sucessfunction(true, html, postparameters);
                },
                failure: function() { alert("failed to get data"); },
                type: "POST",
                data: postparameters,
                contentType: "application/x-www-form-urlencoded"
            });

        }

        function replacepanelhtml($target, newhtml) {
            //it could be that the returned html is split into different chunks for different panels and their slaves
            var $returned = $(dc("div"));
            $returned.html(newhtml);

            if ($returned.find('[data-vpanel]').length > 0) {
                //we have some vpanels to update
                $returned.find('[data-vpanel]').each(function() {
                    var pn = $(this).attr('data-vpanel');
                    //is the given vpanel the same name as our panel, i.e. is it for us?
                    if (pn == Config.name) {
                        $target.html($(this).html());
                        //now we should also update the fragment id
                        $target.attr('data-frag-id', $(this).attr('data-frag-id'));
                        vLog('vPanel', 'replacepanelhtml Replaced main panel');
                    }
                    else {
                        //interesting whether we allow for this?
                        $('[data-vpanel=' + pn + ']').html($(this).html());
                        $('[data-vpanel=' + pn + ']').attr('data-frag-id', $(this).attr('data-frag-id'));
                        vLog('vPanel', 'replacepanelhtml Replaced other panel:' + pn);
                    }
                });
                //we have some vpanels slaves to update
                $returned.find('[data-vpanel-slave]').each(function() {
                    var pn = $(this).attr('data-vpanel-slave');
                    $('[data-vpanel-slave=' + pn + ']').html($(this).html());
                    vLog('vPanel', 'replacepanelhtml Replaced slave panel:' + pn);
                });

            } else {
                //no vpanel found so assume the whole html is used to replace what is inside
                $target.html($returned.html());
            }

        }



        //thepanel refers to this object
        this.doRefresh = function(thepanel, elementfromgroup, extradata) {

            //this renders the whole nexus based on a datasource and a template
            var callback = function finishRefresh(result, anydata) {

                //target is either a dedicated targetID or the whole panel
                $target = Config.targetID != "" ? $('#' + Config.targetID) : $(thepanel);

                if (Internal.HTMLmode) {
                    //update results with html
                    replacepanelhtml($target, anydata);
                    OurNexus.panelrefresh(this);
                } else {
                    //render the content into a container
                    var $rendered = OurNexus.getrenderer(Config.vRenderer).render(OurNexus.getdatasource(Config.vDatasource), Internal.CurrentSelectionKey, Internal.CurrentPosition, Internal.CurrentPageSize);

                    //we could potentially animate the panel out and in

                    //does the container have anything inside?
                    if ($rendered == null) {
                        //if we return false from the above it means there was no data or content to render
                        if (Config.hideonnodata) {
                            //if we're set to hide on no data then we should and perhaps show something else
                            getpanel().hide();
                            //hide the slave panels too
                            $('[data-vpanel-slave=' + Us.applyto() + ']').each(function() { $(this).hide() });
                            if (Config.showthisIDonnodata != '') $('#' + Config.showthisIDonnodata).show();
                        }
                    } else {
                        //this means there was data rendered so we must add and show this panel if it was previously hidden

                        //add the content inside us
                        if (Config.renderStyle == 'replace') {
                            //a simple replace swaps the content
                            $target.html($rendered.html());
                            OurNexus.panelrefresh(this);
                        }
                        else {
                            //this means it's either before or after, so we need to clear out an old render by removing a class
                            getpanel().find('.' + Config.renderStyleClearClass).remove();
                            //then we append before or after
                            if (Config.renderStyle == 'after') {
                                $target.after($rendered.html());
                            }
                            else if (Config.renderStyle == 'before') {
                                $target.before($rendered.html());
                            }
                            else {
                                alert("unknown render style in " + Us.applyto());
                            }
                            //initialise any validators or tooltips
                            OurNexus.panelrefresh(getpanel());
                        }

                        //render any additional areas

                        //look for slave panels
                        $('[data-vpanel-slave=' + Us.applyto() + ']').each(function() {
                            vLog('vPanel', 'Found Slave Panel ' + $(this).attr('id'));
                            //do we have a templateid?
                            var templateID = $(this).attr('data-templateID');
                            if (templateID == null) {
                                //create the template from the slaves content
                                templateID = $(this).attr('id');
                            }
                            //data position
                            var dataposition = $(this).attr('data-position');
                            if (dataposition == null) {
                                dataposition = 'currentselection';
                            }
                            //get the data and dataset
                            var dataitem = OurNexus.getdatasource(Config.vDatasource).getdatabyposition(dataposition, Internal.CurrentPosition, Internal.CurrentPageSize, Internal.CurrentSelectionKey);
                            var dataset = dataitem;
                            //only render if it's not null
                            var rendered = OurNexus.getrenderer(Config.vRenderer).rendertemplate(templateID, dataset, dataitem, false);
                            if (rendered != null) {
                                $(this).html(rendered).show();
                                //this is freshly added, so we may need to initialise elements within
                                OurNexus.panelrefresh(this);
                            } else {
                                $(this).hide();
                            }
                        });


                        if (Config.hideonnodata && getpanel().css('display') == 'none') {
                            //we also need to turn off the panel that is being shown if there is no data
                            if (Config.showthisIDonnodata != '') $('#' + Config.showthisIDonnodata).hide();
                            $('[data-vpanel-slave=' + Us.applyto() + ']').each(function() { $(this).show() });
                            getpanel().show();
                        }
                    };
                }

                //remove any wait class on the target ID if needed
                $target.removeClass(Config.selectWaitClass);

                //recalculate nexus if needed
                Us.recalculatepanel(thepanel, null);

                //if we have on onafterrefresh then call this. This is probably for event rebinding
                if (Config.onafterrefresh != null) {
                    Config.onafterrefresh(Internal);
                }

            }

            //are we an HTML Panel
            if (Internal.HTMLmode) {
                //in this case we use the URL to load more info
                var datatopass = { "vnxmode": "REFRESH" };
                $.extend(datatopass, extradata);
                Us.doWait(thepanel, datatopass);
                requestremotehtml(datatopass, callback);
            }
            else {
                //if we have the data then finish the render now or pass it on in a callback
                if (OurNexus.getdatasource(Config.vDatasource).getasyncdata(callback)) {
                    //if we return true from the above it means we have the data already so we can just render it
                    callback();
                }
            }

        }

        //this shows the waiting display for this nexus which can either be a class swap
        //or a more complicated rendering of a template
        this.doWait = function(thepanel, selectdata) {

            if (!Config.enableWait) return;
            $tid = Config.targetID != "" ? $('#' + Config.targetID) : $(thepanel);
            //do we render a specific wait template
            if (Config.selectWaitTemplateID != '') {
                //render it and replace the html of our target (either a panel or a sub element within the panel)
                var rendered = OurNexus.getrenderer(Config.vRenderer).rendertemplate(Config.selectWaitTemplateID, selectdata, selectdata, false);
                $tid.html(rendered);
            }
            else {
                $tid.html('');
            }
            //do we add a class to the placeholder?
            if (Config.selectWaitClass != '') {
                //add the class, this will be removed on a refresh
                $tid.addClass(Config.selectWaitClass);
            }
        }


        //this navigates to a page +/- pages or by an actual amount, so it moves the CurrentPosition of the panel which
        //in turn will set the renderer accordingly
        function doPage(thepanel, elementfromgroup, amount, pagenumber) {

            if (Internal.HTMLmode) { alert("doPage not active for HTMLpanels"); return false; }
            alert("Needs updating");
            var result = false;
            //page number could also be a date, not always an int
            if (pagenumber != null) {
                result = Internal.vRenderer.setpage(pagenumber);
            }
            else {
                var am = parseInt(amount);
                if (am != NaN) result = Internal.vRenderer.page(am);
            }

            //do we update?
            if (result) Us.doRefresh(thepanel, elementfromgroup, null);
        }


        //sets the selected key for this panel and performs a refresh. However it doesn't mean
        //that the currentposition changes, just the element that is 'selected', so in a list
        //the element that is selected can be highlighted
        this.doSelect = function($thepanel, $triggerelement, directkey, setcurrentposition) {

            if (Internal.HTMLmode) { alert("doSelect not active for HTMLpanels"); return false; }

            //we set the selected based on either the trigger element or the fragment
            var newkey = directkey;
            if (newkey == null) {
                //if there is no direct key to set then we either take on from the data-selectkey attribute on the trigger
                if ($triggerelement.attr('data-selectkey') != null) {
                    newkey = $triggerelement.attr('data-selectkey');
                } else {
                    //or get the key from the nearest fragment
                    $fragment = getfragment($triggerelement);
                    newkey = $fragment.attr('data-frag-id')
                }
            }
            //now we have a key (could be null though)
            if (Internal.CurrentSelectionKey != newkey) {
                vLog('vnexus', "select for " + newkey);
                Internal.CurrentSelectionKey = newkey;
                //we also need to update out current position
                if (setcurrentposition) {
                    //find the index position of the key
                    var index = OurNexus.getdatasource(Config.vDatasource).getindexbykey(Internal.CurrentSelectionKey);
                    if (index >= 0) Internal.CurrentPosition = index;
                    else alert("Can't find index for this key: " + Internal.CurrentSelectionKey);
                }
                //force a refresh of the entire panel with the new selection
                Us.doRefresh($thepanel, $triggerelement, null);
                //run a call back if needed
                if (Config.onafterselect != null) Config.onafterselect(newkey);
            }
        }

        /////////////////////////////////////////
        ///
        ///  INSERT
        ///
        ///
        //do an insert operations
        this.doInsert = function(thepanel, elementfromgroup, placeholdertemplateid, placeholdertargetid, placeholderposition, extrainsertdata, errortemplateid) {



            ///////////////////////////////
            /// CALL BACK
            function finishinsert(result, returneddata, originaldata, $placeholder) {
                //was it a sucess?
                if (result) {

                    $target = Config.targetID != "" ? $('#' + Config.targetID) : $(thepanel);
                    if (Internal.HTMLmode) {
                        //update results with html
                        replacepanelhtml($target, returneddata);
                        OurNexus.panelrefresh(thepanel);
                    } else {
                        if (placeholdertemplateid != '') {
                            //if we're using a placeholder then we should render just that

                            //alert($renderedplaceholder);
                            Us.doRefresh(thepanel, elementfromgroup, null);
                        } else {
                            //doing a refresh will update everything
                            Us.doRefresh(thepanel, elementfromgroup, null);
                        }
                    }

                    $target.removeClass(Config.selectWaitClass);

                    //we can fire off a function if we have one
                    if (Config.onafterinsert != null) Config.onafterinsert(returneddata, originaldata);
                }
                else {
                    //failed to insert
                    if ($placeholder != null && errortemplateid != "") {
                        //lets render an error template instead
                        var newdata = OurNexus.getrenderer(Config.vRenderer).rendertemplate(errortemplateid, returneddata, returneddata, false);
                        $placeholder.html(newdata).removeClass(Config.fragmentinsertclass).addClass(Config.fragmenterrorclass);
                    }
                    if (Config.onaftererror != null) Config.onaftererror("insert", returneddata, originaldata);
                }

                if (Config.clearoninsert) {
                    //we also need to clear the form fields that created this too
                    var focused = false;
                    $frag.find('[name]').each(function() {
                        if ($(this).val() == '') $(this).html('');
                        else {
                            $(this).val('');
                            if (!focused) {
                                $(this).focus();
                                focused = true;
                            }
                        }
                        $(this).removeClass(Config.validclass); //remove any validation showing
                    });
                }

            }




            //if (Internal.HTMLmode) { alert("doInsert not active for HTMLpanels"); return false; }

            if (placeholdertemplateid == null) placeholdertemplateid = "";
            if (placeholdertargetid == null) placeholdertargetid = Config.targetID;
            if (placeholderposition == null) placeholderposition = "append";
            if (errortemplateid == null) errortemplateid = Config.errortemplateID;
            var $renderedplaceholder = null;

            //get the parent fragment (this is the fragment that sparked off the insert)
            var $frag = getfragment(elementfromgroup);

            //is it valid?
            if (!isfragmentvalid(true, $frag)) {
                return;
            }

            //get the data from the fragment
            var allelements = createpostdatafromfragment($frag);

            //add any extra data passed to the panel (perhaps it was on a DOM element)
            $.each(extrainsertdata, function(n, v) {
                allelements[n] = v;
            });

            //create a placeholder fragment
            if (placeholdertemplateid != "") {

                //lets render the allelemets data with the template supplied
                $renderedplaceholder = OurNexus.getrenderer(Config.vRenderer).rendertemplate(placeholdertemplateid, allelements, allelements, true);
                $placeholdertarget = $('#' + placeholdertargetid);
                //now lets add this to the DOM in the right place
                switch (placeholderposition) {
                    case 'prepend':
                        $placeholdertarget.prepend($renderedplaceholder);
                        break;
                    case 'append':
                        $placeholdertarget.append($renderedplaceholder);
                        break;
                    case 'after':
                        $placeholdertarget.after($renderedplaceholder);
                        break;
                    case 'before':
                        $placeholdertarget.before($renderedplaceholder);
                        break;
                }
                //add a class if needed
                if (Config.fragmentinsertclass != '') $renderedplaceholder.addClass(Config.fragmentinsertclass);
            }




            if (Internal.HTMLmode) {
                allelements["vnxmode"] = "INSERT";
                allelements["vnxkey"] = 0;
                Us.doWait(thepanel, allelements);
                //in this case we use the URL to load more info
                requestremotehtml(allelements, finishinsert);
            } else {

                if (!OurNexus.getdatasource(Config.vDatasource).insertitem(allelements, finishinsert, $renderedplaceholder)) {
                    //problem even doing the update (an error generated before we got to send it)
                    alert("problem doing an insert");
                }
            }
        }




        /////////////////////////////////////////
        ///
        ///  UPDATE
        ///
        ///
        this.doUpdate = function(thepanel, elementfromgroup, extraupdatedata) {

            //get the fragment (the single parent element)
            var $frag = getfragment(elementfromgroup);

            //by defining the function here we get access to thepanel and elementfromgroup above
            function finishupdate(result, returneddata, originaldata) {

                $target = Config.targetID != "" ? $('#' + Config.targetID) : $(thepanel);
                if (Internal.HTMLmode) {
                    //update results with html
                    replacepanelhtml($target, returneddata);
                    OurNexus.panelrefresh(thepanel);
                } else {
                    //this means that the data has been sucessfully updated, we are passed the json update object
                    //that was returned which has a headersection and a datasection containing the updated record
                    Us.doRefresh(thepanel, elementfromgroup, null);
                }
                if (Config.fragmentupdateclass != '') $frag.removeClass(Config.fragmentupdateclass);
                //remove any wait class on the target ID if needed
                $target.removeClass(Config.selectWaitClass);
                //we can fire off a function if we have one
                if (Config.onafterupdate != null) Config.onafterupdate(returneddata, originaldata);
            }

            //get the key from attribute data-id (which the renderer creates)
            var keyvalue = $frag.attr('data-frag-id');
            if (keyvalue == null) {
                alert("no key value, update not possible");
                return false;
            }

            //are we are valid for an update? this also performs a validation now across the entire fragment
            if (!isfragmentvalid(true, $frag)) {
                return false;
            }
            else {

                //we just send the data back, create data from all inputs with names
                var allelements = createpostdatafromfragment($frag);
                $.each(extraupdatedata, function(n, v) { allelements[n] = v; });

                //prior to doing a submission we should remove the valid call from the fragment
                //TODO this shouldn't really be in the panel because only the validator knows what class to use
                $frag.find('.valid').removeClass('valid');

                //and visually make a change to people know there's an update in progress
                if (Config.fragmentupdateclass != '') $frag.addClass(Config.fragmentupdateclass);

                //are we an HTML Panel
                if (Internal.HTMLmode) {
                    allelements["vnxmode"] = "UPDATE";
                    allelements["vnxkey"] = keyvalue;
                    Us.doWait(thepanel, allelements);
                    //in this case we use the URL to load more info
                    requestremotehtml(allelements, finishupdate);
                } else {
                    //do the updateitem and on success we call the embedded function above to clean up
                    if (!OurNexus.getdatasource(Config.vDatasource).updateitem(keyvalue, allelements, finishupdate)) {
                        //problem even doing the update (an error generated before we got to send it)
                        alert("problem doing an update")
                    }
                }

            }
        }



        /////////////////////////////////////////
        ///
        ///  DELETE
        ///
        ///
        this.doDelete = function(thepanel, elementfromgroup, extradeletedata) {

            if (Internal.HTMLmode) { alert("doDelete not active for HTMLpanels"); return false; }

            //get the fragment
            var $frag = getfragment(elementfromgroup);

            function finishdelete(results, returneddata, originaldata) {

                if (Config.refreshondelete) {
                    Us.doRefresh(thepanel, null, null);
                }
                else {
                    //physically remove the fragment now we know it's deleted
                    $frag.remove();
                    //this will affect the calculations
                    Us.recalculatepanel(thepanel, null);
                }

                //also we may need to alter the currentselection
                if (Internal.CurrentSelectionKey == keyvalue) {
                    Internal.CurrentSelectionKey = null;
                }
                if (Config.onafterdelete != null) Config.onafterdelete(originaldata);


                //what to do about calendars?

                //it may be best to refresh the entire nexus after some deletes, for example if we're
                //displaying 10 items and we delete one then we're only showing 9, eventually we
                //could delete this list - in this case and perhaps a calendar a nexus refresh is
                //the best option

            }


            //get the ID of the record
            var keyvalue = $frag.attr('data-frag-id');
            if (keyvalue == null) {
                alert("no key value, delete not possible");
                return false;
            }
            //update the css if needed
            if (Config.fragmentdeleteclass != '') $frag.addClass(Config.fragmentdeleteclass);

            //get all the elements that can be used to delete with
            var allelements = createpostdatafromfragment($frag);
            //add extra data
            $.each(extradeletedata, function(n, v) { allelements[n] = v; });

            if (!OurNexus.getdatasource(Config.vDatasource).deleteitem(keyvalue, allelements, finishdelete)) {
                //problem even doing the update (an error generated before we got to send it)
                alert("problem doing delete")
            }


        }

        //if an element within the panel has been changed then we get this event to process that, this
        //can take the form of an auto update or insert for example
        this.doValueChange = function($thepanelelement, $triggerelement, oldvalue, newvalue) {
            //the passed element has triggered a change, lets see if there is a fragment with auto anything
            var $frag = getfragment($triggerelement);
            if ($frag.hasClass('vnx-autoupdate')) {
                Us.doUpdate($thepanelelement, $triggerelement, {});
            }
            if ($frag.hasClass('vnx-autoinsert')) {
                //get the template options (from the fragment) (when auto inserting there is no real trigger element)
                var placeholdertemplateID = $frag.attr('data-placeholder-templateID');
                var placeholderposition = $frag.attr('data-placeholder-position');
                var placeholdertargetID = $frag.attr('data-placeholder-targetID');
                var errortemplateid = $frag.attr('data-placeholder-errortemplateID');
                Us.doInsert($thepanelelement, $triggerelement, placeholdertemplateID, placeholdertargetID, placeholderposition, {}, errortemplateid);
            }
        }



        // CALCULATION FUNCTIONS
        //
        // Update any live calculated fields within this nexus

        this.recalculatepanel = function(thepanel, jqueryelement) {

            //first do a cacluation for the current fragment. the jquery element was the one that was changed
            //we find the closest fragment and then find all the elements marked with the vnx-calc class then
            //look for a vtx-formula attribute which contains the name of the forumla to use. These formula
            //are setup in the setup for each nexus and refer to external javascript functions that get passed
            //the fragment

            if (Config.formula == null) return false;   //obviously if we don't have any forumla it isn't worth spending any time

            if (jqueryelement == null) {
                //update all fragments in the current nexus
                var $allfrag = getpanel().find('[data-frag-id]');  //find the fragments that are real
                //with each fragment found
                $allfrag.each(function() {
                    $frag = this; //so we can pass this fragment to the calculation
                    //look inside for calculations for the fragment
                    $(this).find('.vnx-calc-fragment').each(function(i) {
                        //now we have our row, do any of the trcalcs match our formula?
                        var formulatolookup = $(this).attr('data-formula-name');  //get the name from the custom attribute
                        var currentelement = this;
                        //does this match any of our formula?
                        $.each(Config.formula, function(name, formulavalue) {
                            if (name == formulatolookup) {
                                //add the result to the element
                                $(currentelement).html(formulavalue(currentelement, $frag));
                            };
                        });
                    });
                });
            }
            else {

                //first perform the calculation specified by the element (vnx-calc)

                var $frag = getfragment(jqueryelement);  //get fragment
                var $elm = $(jqueryelement);
                //find and run the calculation for this element
                var formulatolookup = $elm.attr('data-formula-name');
                //does this match any of our formula?
                $.each(Config.formula, function(name, formulavalue) {
                    if (name == formulatolookup) {
                        //run the function and add the result to the element
                        $(jqueryelement).html(formulavalue($elm, $frag));
                    };
                });

                //now peform the calculations for the fragment
                var $fragcalcs = $frag.find('.vnx-calc-fragment').each(function(i) {
                    //now we have our row, do any of the trcalcs match our formula?
                    var formulatolookup = $(this).attr('data-formula-name');  //get the name from the custom attribute
                    var currentelement = this;
                    //does this match any of our formula?
                    $.each(Config.formula, function(name, formulavalue) {
                        if (name == formulatolookup) {
                            //add the result to the element
                            $(currentelement).html(formulavalue(currentelement, $frag));
                        };
                    });
                });
            }

            //finally, run any global formula across the entire nexus
            var $glob = getpanel().find('.vnx-calc-global').each(function(i) {
                var formulatolookup = $(this).attr('data-formula-name');  //get the name from the custom attribute
                var currentelement = this;
                $.each(Config.formula, function(name, formulavalue) {
                    if (name == formulatolookup) {
                        $(currentelement).html(formulavalue(currentelement, getpanel()));
                    };
                });
            });

        }










        //}




        Us.initialisepanel();







    }
})(jQuery);
