<commit_before>async function(args) {
/*
created_timestamp(-1)
base_component_id("app_editor_3")
is_app(true)
display_name("Editor App V3")
description("This will return the editor app V3")
logo_url("https://2.bp.blogspot.com/-6Hdixw3dFxk/WfSQOnB9lDI/AAAAAAAAFFc/84DRGgcwOpYBOgknkHQ-qmgxvFv1D-iHACLcBGAs/s1600/BracketsDarks.PNG")
load_once_from_file(true)
*/
    //
    // Hack city!!! Turn off the component cache so that we can enable hot reloading of components
    //
    isStaticHtmlPageApp = false
    //
    //
    //

    await loadV2("appEmbed")

    Vue.component("app_editor_3",
    {
      props: ['app_id', 'card_index'],
      template: `

{{app_component_name?"" + app_component_name.substring(0,30):""}}{{(app_component_name && ((app_component_name.length > 30))?"...":"")}}

                        <input  class='caption' style='display: inline-block;' v-if='edit_name' v-model='new_name'></input>
                        <button type=button class='btn btn-primary' style='margin-left: 10px' v-if='edit_name' v-on:click='(async function(){await rename(new_name)})()'>Save new name</button>

                            <button  type=button class='btn btn-primary'      v-on:click='chooseApp()'  >App</button>
                            <button  type=button class=' btn btn-secondary'   v-on:click='chooseCode()' >Code</button>
                            <button  type=button class=' btn btn-info'        v-on:click='chooseBoth()' >Both</button>
                            <button  type=button class=' btn btn-secondary'   v-on:click='copyAppMethod(base_component_id,null)' >Copy app</button>
                            <button  type=button class=' btn btn-info'        v-on:click='embedApp(base_component_id)' >Embed app</button>
                            <button  v-if='(editor_component != "editor_component") && (!read_only)' type=button class=' btn btn-secondary'   v-on:click='editAsText()' >Edit as text</button>
                            <button  v-if='(editor_component != "editor_component") && (!read_only)' type=button class=' btn btn-secondary'   v-on:click='setVisibility("PUBLIC")' >Publish!</button>

                        <appEmbed v-bind:base_component_id_arg='base_component_id'></appEmbed>

                          <component  v-bind:is="editor_component" v-if="editor_loaded" ref="editorComponentRef">
                                          <button v-if='!read_only'
                                                  v-bind:style="'visibility: ' + ((app_shown && code_shown)?'':'hidden')"
                                                  slot-scope="editor_component"
                                                  v-on:click='setTimeout(async function(){await save(base_component_id, code_id,null)},100)'
                                                  type="button" class="btn btn-primary btn-sm">

                                                      Save changes
                                         </button>
                          </component>

                                    {{show_new_tab_tooltip?"Open app in new browser tab (shareable :)":""}}
                                    {{show_open_app_tooltip?"Download app as .HTML file (emailable :)":""}}

                                ← → ↻
                                ❏

                                    <input  readonly size="40" style='font-size: 13px;'
                                        v-bind:value='"http://" + location.hostname + ":" + location.port + "/app/" + base_component_id + ".html"'></input>

                                ♨

                          <component  v-bind:is="app_component_name" v-if="app_loaded">
                            APP HERE
                          </component>

       `
       ,
       data: function() {
           return {
               editor_loaded:       false,
               selected_app:        '',
               editor_component:    null,
               app_loaded:          false,
               app_component_name:  null,
               base_component_id:   null,
               code_id:            "...",
               version: 0,
               app_width:           "33%",
               code_width:          "63%",
               app_shown:           true,
               code_shown:          true,
               read_only:           false,
               mode:                "edit",
               show_name:           true,
               edit_name:           false,
               new_name:            "",
               show_new_tab_tooltip:false,
               show_open_app_tooltip:false
           }
       }
       ,

       methods: {
            // ---------------------------------------------------------------
            //                         chooseApp
            //
            // This is called when the end user selects "app"
            // ---------------------------------------------------------------
            chooseApp: async function() {
                var mm = this
                this.code_width = "0%"
                this.code_shown = false

                this.app_width = "95%"
                this.app_shown = true

                this.mode      = "edit"

                var text = await this.$refs.editorComponentRef.getText()

                //
                // there may be a problem here - we have to make sure that we saved
                // the correct code_id which is supposed to be the parent code id, so we
                // have to make sure that we save it every time we save code
                //
                await this.save( this.base_component_id, this.code_id, text )
            },

            chooseCode: async function() {
                var mm = this
                this.code_width = "95%"
                this.code_shown = true

                this.app_width = "0%"
                this.app_shown = false

                this.mode      = "edit"

                await mm.load_app( this.base_component_id )
            },

            chooseBoth: async function() {
                var mm = this
                this.code_width = "63%"
                this.code_shown = true

                this.app_width = "33%"
                this.app_shown = true

                this.mode      = "edit"
            },

            rename: async function(nn) {
                this.edit_name = false
                this.show_name = true

                nn = nn.replace(/[\W_]+/g,"_");
                await this.copyAppMethod( this.base_component_id , nn)
                this.new_name = ""
            },

            editAsText: async function() {
                var mm = this

                var text = await this.$refs.editorComponentRef.getText()

                var eds = saveHelper.getValueOfCodeString(text, "editors")
                if (eds) {
                    text = saveHelper.deleteCodeString(text, "editors")
                    text = saveHelper.insertCodeString(text, "editors_old",eds)
                }

                await mm.save(   this.base_component_id,   this.code_id,   text   )

                await mm.load_new_app( this.base_component_id )
            },

            setVisibility: async function(value) {
                var mm = this

                var text = await this.$refs.editorComponentRef.getText()

                var eds = saveHelper.getValueOfCodeString(text, "visibility")
                if (eds) {
                    text = saveHelper.deleteCodeString(text, "visibility")
                    text = saveHelper.insertCodeString(text, "visibility",value)
                }

                await mm.save(   this.base_component_id,   this.code_id,   text   )

                await mm.load_new_app( this.base_component_id )
            },

            embedApp: function(x) {
                var mm = this
                this.mode = "embed"
            }
            ,

            copyAppMethod: async function( appId , newAppId) {
                var mm = this
                var result = await callFunction(
                                    {
                                        driver_name: "copyApp",
                                        method_name: "copyAppshareApp"  }
                                        ,{
                                              base_component_id:    appId,
                                              new_app_id:           newAppId   })
                await mm.load_new_app( result.value.base_component_id )
                mm.$root.$emit('message', {
                                                type:               "insert_app_at",
                                                base_component_id:   result.value.base_component_id,
                                                card_index:          mm.card_index
                                            })

            }
            ,

           // ---------------------------------------------------------------
           //                           save
           //
           // This is called to save the currently edited code
           // ---------------------------------------------------------------
           save: async function( base_component_id, code_id , textIn) {

               var text =  textIn
               if (text == null) {
                    text = await this.$refs.editorComponentRef.getText()
               }

               var mm = this
               if (mm.read_only) {
                    return
               }

               var results = await callFunction(
               {
                   driver_name:     "appEditorV2SaveCode",
                   method_name:     "saveCode"
               },
                   {
                        base_component_id:      base_component_id,
                        code_id:                code_id,
                        code:                   text,
                        options:                {
                                                    sub_components: Object.keys(dev_app_component_loaded),
                                                    save_html:              true
                                                }
                   })

               await mm.load_app( mm.base_component_id )
           },

           load_new_app: async function ( baseComponentId ) {
               var mm = this
               if ((!baseComponentId) || (baseComponentId == "") || (!mm)) {
                    return
               }
               dev_app_component_loaded = new Object()
               this.editor_loaded = false
               await this.load_app(baseComponentId)
           }
           ,

           // ---------------------------------------------------------------
           //                           load_app
           //
           // This loads the latest version of the code stream marked with
           // 'baseComponentId'
           // ---------------------------------------------------------------
           load_app: async function ( baseComponentId ) {

                //
                // make sure that we reference an app
                //
                var mm = this
                if ((!baseComponentId) || (baseComponentId == "") || (!mm)) {
                    return
                }

               //
               // set up vars
               //
               mm.selected_app          = ""
               //mm.app_loaded            = false
               mm.base_component_id     = baseComponentId
               mm.app_component_name    = null

               //
               // read the code for the component that we are editing
               //
               var sql =    `select
                                id, cast(code as text)  as  code, editors
                             from
                                system_code
                             where
                                    base_component_id = '${baseComponentId}'
                                       and
                                    code_tag = 'LATEST' `

               var results = await callApp(
                   {
                        driver_name:    "systemFunctions2",
                        method_name:    "sql"
                   }
                   ,
                   {
                       sql: sql
                   })

               if (results) {
                   if (results.length > 0) {

                        //
                        // find the editor
                        //
                        var editors2 = results[0].editors
                        var newEditor = null
                        if (editors2) {
                            var edd = eval("(" + editors2 + ")")
                            newEditor = edd[0]
                        }

                        //
                        // find the code
                        //
                        var code = results[0].code
                        var codeId = results[0].id
                        if (mm.editor_loaded && (mm.code != code)) {
                            mm.code = code
                            mm.code_id = codeId
                        }

                        //
                        // load the editor
                        //
                        if ( !mm.editor_loaded ) {
                            var editorName = "editor_component"
                            if (newEditor) {
                                 editorName = newEditor
                            }

                            await loadV2( editorName, {text: code} )
                            mm.editor_loaded    = true
                            mm.editor_component = editorName
                       }

                       //
                       // set readonly
                       //
                       this.read_only = saveHelper.getValueOfCodeString(code, "read_only")
                   }

                   var results = await callApp( {code_id:    codeId },{})
                   mm.app_loaded = true
                   mm.baseComponentId = baseComponentId

                   setTimeout(async function() {
                       mm.app_component_name = baseComponentId
                       mm.$refs.editorComponentRef.setText(code)
                   },500)
               }
           }
       },

            mounted: async function () {
                var mm = this

                //
                // make sure we load the component for this app
                //
                if (mm.app_id) {
                    component_loaded[this.app_id]           = false
                    dev_app_component_loaded[this.app_id]   = false
                    component_cache[this.app_id]            = null

                    await this.load_app(this.app_id)

                }
           }
       })
       return {name: "app_editor"}

}
<commit_msg>Can make an app private<commit_after>async function(args) {
/*
created_timestamp(-1)
base_component_id("app_editor_3")
is_app(true)
display_name("Editor App V3")
description("This will return the editor app V3")
logo_url("https://2.bp.blogspot.com/-6Hdixw3dFxk/WfSQOnB9lDI/AAAAAAAAFFc/84DRGgcwOpYBOgknkHQ-qmgxvFv1D-iHACLcBGAs/s1600/BracketsDarks.PNG")
load_once_from_file(true)
*/
    //
    // Hack city!!! Turn off the component cache so that we can enable hot reloading of components
    //
    isStaticHtmlPageApp = false
    //
    //
    //

    await loadV2("appEmbed")

    Vue.component("app_editor_3",
    {
      props: ['app_id', 'card_index'],
      template: `

{{app_component_name?"" + app_component_name.substring(0,30):""}}{{(app_component_name && ((app_component_name.length > 30))?"...":"")}}

                        <input  class='caption' style='display: inline-block;' v-if='edit_name' v-model='new_name'></input>
                        <button type=button class='btn btn-primary' style='margin-left: 10px' v-if='edit_name' v-on:click='(async function(){await rename(new_name)})()'>Save new name</button>

                            <button  type=button class='btn btn-primary'      v-on:click='chooseApp()'  >App</button>
                            <button  type=button class=' btn btn-secondary'   v-on:click='chooseCode()' >Code</button>
                            <button  type=button class=' btn btn-info'        v-on:click='chooseBoth()' >Both</button>
                            <button  type=button class=' btn btn-secondary'   v-on:click='copyAppMethod(base_component_id,null)' >Copy app</button>
                            <button  type=button class=' btn btn-info'        v-on:click='embedApp(base_component_id)' >Embed app</button>
                            <button  v-if='(editor_component != "editor_component") && (!read_only)' type=button class=' btn btn-secondary'   v-on:click='editAsText()' >Edit as text</button>
                            <button  v-if='(editor_component != "editor_component") && (!read_only)' type=button class=' btn btn-danger'   v-on:click='setVisibility("PRIVATE")' >Public: Switch to private</button>
                            <button  v-if='(editor_component != "editor_component") && (!read_only)' type=button class=' btn btn-success'   v-on:click='setVisibility("PUBLIC")' >Private: Switch to public</button>

                        <appEmbed v-bind:base_component_id_arg='base_component_id'></appEmbed>

                          <component  v-bind:is="editor_component" v-if="editor_loaded" ref="editorComponentRef">
                                          <button v-if='!read_only'
                                                  v-bind:style="'visibility: ' + ((app_shown && code_shown)?'':'hidden')"
                                                  slot-scope="editor_component"
                                                  v-on:click='setTimeout(async function(){await save(base_component_id, code_id,null)},100)'
                                                  type="button" class="btn btn-primary btn-sm">

                                                      Save changes
                                         </button>
                          </component>

                                    {{show_new_tab_tooltip?"Open app in new browser tab (shareable :)":""}}
                                    {{show_open_app_tooltip?"Download app as .HTML file (emailable :)":""}}

                                ← → ↻
                                ❏

                                    <input  readonly size="40" style='font-size: 13px;'
                                        v-bind:value='"http://" + location.hostname + ":" + location.port + "/app/" + base_component_id + ".html"'></input>

                                ♨

                          <component  v-bind:is="app_component_name" v-if="app_loaded">
                            APP HERE
                          </component>

       `
       ,
       data: function() {
           return {
               editor_loaded:       false,
               selected_app:        '',
               editor_component:    null,
               app_loaded:          false,
               app_component_name:  null,
               base_component_id:   null,
               code_id:            "...",
               version: 0,
               app_width:           "33%",
               code_width:          "63%",
               app_shown:           true,
               code_shown:          true,
               read_only:           false,
               mode:                "edit",
               show_name:           true,
               edit_name:           false,
               new_name:            "",
               show_new_tab_tooltip:false,
               show_open_app_tooltip:false
           }
       }
       ,

       methods: {
            // ---------------------------------------------------------------
            //                         chooseApp
            //
            // This is called when the end user selects "app"
            // ---------------------------------------------------------------
            chooseApp: async function() {
                var mm = this
                this.code_width = "0%"
                this.code_shown = false

                this.app_width = "95%"
                this.app_shown = true

                this.mode      = "edit"

                var text = await this.$refs.editorComponentRef.getText()

                //
                // there may be a problem here - we have to make sure that we saved
                // the correct code_id which is supposed to be the parent code id, so we
                // have to make sure that we save it every time we save code
                //
                await this.save( this.base_component_id, this.code_id, text )
            },

            chooseCode: async function() {
                var mm = this
                this.code_width = "95%"
                this.code_shown = true

                this.app_width = "0%"
                this.app_shown = false

                this.mode      = "edit"

                await mm.load_app( this.base_component_id )
            },

            chooseBoth: async function() {
                var mm = this
                this.code_width = "63%"
                this.code_shown = true

                this.app_width = "33%"
                this.app_shown = true

                this.mode      = "edit"
            },

            rename: async function(nn) {
                this.edit_name = false
                this.show_name = true

                nn = nn.replace(/[\W_]+/g,"_");
                await this.copyAppMethod( this.base_component_id , nn)
                this.new_name = ""
            },

            editAsText: async function() {
                var mm = this

                var text = await this.$refs.editorComponentRef.getText()

                var eds = saveHelper.getValueOfCodeString(text, "editors")
                if (eds) {
                    text = saveHelper.deleteCodeString(text, "editors")
                    text = saveHelper.insertCodeString(text, "editors_old",eds)
                }

                await mm.save(   this.base_component_id,   this.code_id,   text   )

                await mm.load_new_app( this.base_component_id )
            },

            setVisibility: async function(value) {
                var mm = this

                var text = await this.$refs.editorComponentRef.getText()

                var eds = saveHelper.getValueOfCodeString(text, "visibility")
                if (eds) {
                    text = saveHelper.deleteCodeString(text, "visibility")
                    text = saveHelper.insertCodeString(text, "visibility",value)
                }

                await mm.save(   this.base_component_id,   this.code_id,   text   )

                await mm.load_new_app( this.base_component_id )
            },

            embedApp: function(x) {
                var mm = this
                this.mode = "embed"
            }
            ,

            copyAppMethod: async function( appId , newAppId) {
                var mm = this
                var result = await callFunction(
                                    {
                                        driver_name: "copyApp",
                                        method_name: "copyAppshareApp"  }
                                        ,{
                                              base_component_id:    appId,
                                              new_app_id:           newAppId   })
                await mm.load_new_app( result.value.base_component_id )
                mm.$root.$emit('message', {
                                                type:               "insert_app_at",
                                                base_component_id:   result.value.base_component_id,
                                                card_index:          mm.card_index
                                            })

            }
            ,

           // ---------------------------------------------------------------
           //                           save
           //
           // This is called to save the currently edited code
           // ---------------------------------------------------------------
           save: async function( base_component_id, code_id , textIn) {

               var text =  textIn
               if (text == null) {
                    text = await this.$refs.editorComponentRef.getText()
               }

               var mm = this
               if (mm.read_only) {
                    return
               }

               var results = await callFunction(
               {
                   driver_name:     "appEditorV2SaveCode",
                   method_name:     "saveCode"
               },
                   {
                        base_component_id:      base_component_id,
                        code_id:                code_id,
                        code:                   text,
                        options:                {
                                                    sub_components: Object.keys(dev_app_component_loaded),
                                                    save_html:              true
                                                }
                   })

               await mm.load_app( mm.base_component_id )
           },

           load_new_app: async function ( baseComponentId ) {
               var mm = this
               if ((!baseComponentId) || (baseComponentId == "") || (!mm)) {
                    return
               }
               dev_app_component_loaded = new Object()
               this.editor_loaded = false
               await this.load_app(baseComponentId)
           }
           ,

           // ---------------------------------------------------------------
           //                           load_app
           //
           // This loads the latest version of the code stream marked with
           // 'baseComponentId'
           // ---------------------------------------------------------------
           load_app: async function ( baseComponentId ) {

                //
                // make sure that we reference an app
                //
                var mm = this
                if ((!baseComponentId) || (baseComponentId == "") || (!mm)) {
                    return
                }

               //
               // set up vars
               //
               mm.selected_app          = ""
               //mm.app_loaded            = false
               mm.base_component_id     = baseComponentId
               mm.app_component_name    = null

               //
               // read the code for the component that we are editing
               //
               var sql =    `select
                                id, cast(code as text)  as  code, editors
                             from
                                system_code
                             where
                                    base_component_id = '${baseComponentId}'
                                       and
                                    code_tag = 'LATEST' `

               var results = await callApp(
                   {
                        driver_name:    "systemFunctions2",
                        method_name:    "sql"
                   }
                   ,
                   {
                       sql: sql
                   })

               if (results) {
                   if (results.length > 0) {

                        //
                        // find the editor
                        //
                        var editors2 = results[0].editors
                        var newEditor = null
                        if (editors2) {
                            var edd = eval("(" + editors2 + ")")
                            newEditor = edd[0]
                        }

                        //
                        // find the code
                        //
                        var code = results[0].code
                        var codeId = results[0].id
                        if (mm.editor_loaded && (mm.code != code)) {
                            mm.code = code
                            mm.code_id = codeId
                        }

                        //
                        // load the editor
                        //
                        if ( !mm.editor_loaded ) {
                            var editorName = "editor_component"
                            if (newEditor) {
                                 editorName = newEditor
                            }

                            await loadV2( editorName, {text: code} )
                            mm.editor_loaded    = true
                            mm.editor_component = editorName
                       }

                       //
                       // set readonly
                       //
                       this.read_only = saveHelper.getValueOfCodeString(code, "read_only")
                   }

                   var results = await callApp( {code_id:    codeId },{})
                   mm.app_loaded = true
                   mm.baseComponentId = baseComponentId

                   setTimeout(async function() {
                       mm.app_component_name = baseComponentId
                       mm.$refs.editorComponentRef.setText(code)
                   },500)
               }
           }
       },

            mounted: async function () {
                var mm = this

                //
                // make sure we load the component for this app
                //
                if (mm.app_id) {
                    component_loaded[this.app_id]           = false
                    dev_app_component_loaded[this.app_id]   = false
                    component_cache[this.app_id]            = null

                    await this.load_app(this.app_id)

                }
           }
       })
       return {name: "app_editor"}

}
<|endoftext|>


<commit_before>/*
 * Event raised after a create/update form is displayed and ready for further
 * manipulation by client code. This event can be trapped by client code to
 * add their own custom JavaScript initialization to the form. Using this event
 * instead of the ubiqitous $(document).ready ensures that the same init code
 * works well on both legacy and popup modes.
 *
 * This allows you write code such as this:
 *
 *   $(document).on("crudform.ready", function(event) {
 *     // create/edit form initialization code goes here
 *   });
 */
const CRUDFORM_READY = "crudform.ready";

(function($) {
  $.fn.popupCrud = function (opts) { // wrap all popupcrud functions in a closure
    var _formsetTemplate = null,
    /*
     * Bind a submit function to a form embedded in a Bootstrap modal, which in
     * turn uses AJAX POST request to submit the form data. When the form has been
     * successully submitted, the modal is hidden. If the submitted form has errors
     * the form is re-rendered with field errors highlighted as per Bootstrap
     * rendering guidelines.
     *
       Parameters:
        form: jQuery selector to the form that is to be submited via AJAX
        modal: jQuery selector to the modal dialog to be dismissed post successful
               form submission
        complete: A function to be called upon successful form submission.
     */
    submitModalForm = function(form, modal, complete) {
        $(form).submit(function(e) {
            e.preventDefault();
            $.ajax({
                type: $(this).attr('method'),
                url: $(this).attr('action'),
                data: $(this).serialize(),
                success: function (xhr, ajaxOptions, thrownError) {
                    if ( $(xhr).find('.has-error').length > 0 ||
                         $(xhr).find('.alert').length > 0) {
                        $(modal).find('.modal-body').html(xhr);
                        bindAddAnother($(modal));
                        initFormset(modal)
                        bindSelect2(modal, modal);
                        triggerCrudFormReady(modal);
                        submitModalForm(form, modal, complete);
                    } else {
                        $(modal).modal('hide');
                        if (typeof complete == "function") {
                            complete(xhr); // notify caller
                        }
                    }
                },
                error: function (xhr, ajaxOptions, thrownError) {
                  // todo
                }
            });
        });
    },
    /* Helper function to retrieve cookie value */
    getCookie = function(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    },
    /*
     * Triggers crudform.ready event on an activated form if it's
     * not the delete-form
     */
    triggerCrudFormReady = function(elem) {
      var form = $(elem).find("form");
      if (form) {
        var ev = $.Event(CRUDFORM_READY);
        if (form.attr('id') != 'delete-form') { // exclude delete-form
          form.trigger(ev);
        }
      }
    },
    /*
     * Bind select2 widget to any form controls under 'parent' element
     * with css style .django-select2.
     */
    bindSelect2 = function(selectParent, dropdownParent) {
      var sels = $(selectParent).find('.django-select2');
      if (sels.length > 0) {
        $(sels).djangoSelect2({
          dropdownParent: $(dropdownParent)
        });
      }
    },
    /*
     * Binds all '.add-another' hyperlinks under the given 'elem' with their own
     * modals, each of which will hold the form for the .add-another's data-url
     * value. Each such modal is given an id composed as the value of the 'a'
     * tag's 'id' value + '-modal'. The modal will be added only if a modal with
     * the same id does not exist so as to avoid duplicate modals.

     * The 'a' tag is assigned the newly added modal's id, so that the associated
     * modal dialog can be loaded with the respective url form content and
     * activated in a generic manner from a Javascript function.
     */
    bindAddAnother = function(elem) {
      // Modal dialog template derived from #create-edit-modal that'll be added at
      // the end of the  tag.
      if ($("#add-related-modal").length == 0)
        return;

      var modalTemplate = $('
').html($("#add-related-modal")[0].outerHTML);
      $(modalTemplate)
        .find('.modal').attr('id', 'create-related-modal')
        .find('.modal-title').html('')
        .find('.modal-body').html('')
      var createRelatedModal = modalTemplate.html();
      elem.find(".add-another").each(function(index, elem) {
        var modalId = $(elem).attr('id') + '-modal';
        if ($("#"+modalId).length == 0) {
          $("body").append(createRelatedModal.replace('create-related-modal', modalId))
        }
        $(elem).data('modal', modalId);
      });

      /*
       * Generic function that loads the form associated with a hyperlink into
       * the related modal.
       *
       * Constraints & Behavior:
       *  1.  element is preceded immediately by a <select> element
       *  2.  element has the following data attributes:
       *      a. data-url: the URL to load the form from
       *      b. data-modal: id of the associated modal that is loaded with the
       *         form and activated.
       *  3. The activated modal's title is set to  element's text content.
       *  4. If the activated form submission was successful, the sibling 'select'
       *     element is popuplated with an <option> for the just added element.
       */
      $(elem).find(".add-another").click(function(evtObj) {
        var url = $(this).data('url');
        var title = $(this).text();
        var select = $(this).prevAll('select');
        var modalId = $(this).data('modal');
        var modal = $('#' + modalId);
        modal.find('.modal-body').load(url, function () {
          modal.find('.modal-title').text(title);
          bindAddAnother(modal);
          modal.modal('show');
          submitModalForm(modal.find('#create-edit-form'), '#'+modalId,
            function(xhr) {
              $(select).append($("<option></option>").
                attr("value", xhr.pk).text(xhr.name));
              var newVal = $(select).val();
              if ($(select).attr('multiple')) {
                newVal.push(xhr.pk);
              } else {
                newVal = xhr.pk;
              }
              $(select).val(newVal).trigger('change');
            })
        });
      });
    },
    // handler for New/Edit object actions
    handleCreateEdit = function(evtObj) {
      evtObj.preventDefault();
      var url = $(this).data('url');
      var title = $(this).data('title');
      $('#create-edit-modal .modal-body').load(url, function () {
        cacheFormsetTemplate();
        $('#create-edit-modal .modal-title').text(title);
        bindAddAnother($("#create-edit-modal"));
        $('#create-edit-modal').modal('show');
        submitModalForm('#create-edit-form',
          '#create-edit-modal', function(xhr) {
            location.reload();
          });
      });
    },
    // handler for object detail view
    handleObjectDetail = function(evtObj) {
      evtObj.preventDefault();
      var url = $(this).data('url');
      var title = $(this).data('title');
      $('#detail-modal .modal-body').load(url, function () {
        $('#detail-modal .modal-title').text(title);
        $('#detail-modal').modal('show');
      });
    },
    // delete an object action handler
    handleDeleteObject = function(evtObj) {
      evtObj.preventDefault();
      $('#delete-modal #id_object_name').text(
        $(evtObj.target).parents('tr').children(':nth-child(1)').children('div').data('name'));
      $('#delete-modal .modal-body form').attr(
        'action', $(evtObj.target).parent('a').data('url'));
      $('#delete-modal').modal('show');
      var title = $(this).children('span').attr('title');
      submitModalForm('#delete-form', '#delete-modal',
        function(xhr) {
          showActionResult(xhr.result, title, xhr.message);
        }
      );
    },
    // custom action handler
    handleCustomAction = function(evtObj) {
      evtObj.preventDefault();
      var action = $(this).data('action');
      var title = $(this).attr('title');
      $.ajax({
        type: 'POST',
        data: {
          csrfmiddlewaretoken: getCookie('csrftoken'),
          action: action,
          item: $(this).data('obj')
        },
        success: function (xhr, ajaxOptions, thrownError) {
          showActionResult(xhr.result, title, xhr.message);
        }
      });
    },
    // Show the action result message in a modal
    //
    // Parameters:
    //      result: bool, action success indicator
    //      title: title for the dialog
    //      message: message to be displayed in the modal
    // Returns:
    //      None
    showActionResult = function(result, title, message) {
      $("#action-result-modal .modal-title").text(title);
      $("#action-result-modal #id_action_result").html(message);
      $('#action-result-modal').on('hidden.bs.modal', result ?
        function(evtObj) {
          $('#action-result-modal').off('hidden.bs.modal');
          location.reload();
        } :
        function(evtObj) {
          $('#action-result-modal').off('hidden.bs.modal');
        });
      $('#action-result-modal').modal('show');
    },
    // reads the formset form template and stores in _formsetTemplate variable.
    cacheFormsetTemplate = function() {
      popupCrudFormsetFormTempl = $("#id_formset table>tbody>tr:last").clone(true).removeAttr('id');
      popupCrudFormsetFormTempl.find('input:hidden[id $= "-DELETE"]').remove();
      this._formsetTemplate = popupCrudFormsetFormTempl;
    },
    /**
     * Initializes any embedded formsets in the form such that formset form rows
     * can be added dynamically.
     *
     * If a formset is present, it is expected to be embedded within a
     *

 element.
     *
     * Parameters:
     *  parent - the parent element under which formset will be searched.
     */
    initFormset = function(parent) {
      var formsetDiv = $(parent).find('div#id_formset');
      if (formsetDiv.length > 0) {  // presence of div indicates form has formset.
        var prefix = formsetDiv.find('input[name*=TOTAL_FORMS]').attr('name').split('-')[0];
        formsetDiv.find("table tbody tr").formset({
          formTemplate: this._formsetTemplate,
          prefix: prefix,
          addText: '<button type="button" class="btn btn-default btn-sm" aria-label="Add"></button>',
          deleteText: "",
          added: function (row) {
            var dropDownParent = $(document.body);  // default
            if (formsetDiv.parents('.modal').length > 0) {
              dropDownParent = formsetDiv.parents('.modal');
            }
            bindSelect2(row, dropDownParent);
          }
        });
      }
    };

    /*
     * Adjusts the just activated modal window's z-index to a value higher
     * than the previously activated modal window's z-index. This will
     * ensure that each newly activated modal is layered on top of all
     * previously activated modals achieving the layered dialog effect.

     * Code borrowed from: http://jsfiddle.net/CxdUQ/
     */
    $(document).on('show.bs.modal', '.modal', function (event) {
      // calculate z-index as a function of number of visible modal windows.
      var zIndex = 1040 + (10 * $('.modal:visible').length);
      $(this).css('z-index', zIndex);
      setTimeout(function() {
        $('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - 1).addClass('modal-stack');
      }, 0);
    });
    $(document).on('shown.bs.modal', '.modal', function (event) {
      /*
       * Initialize any django-select2 fields in the dialog. If the dialog does
       * not have any select2 fields, this code does nothing.
       *
       * Also this is a template for any custom initialization to be done
       * by user javascript codes. Typically if the user code has to do UI level
       * processing, it does it from $(document).ready(). But for UI processing
       * on modal dialogs, it has to be done from $(document).on('shown.bs.modal').
       */
      initFormset(this);
      bindSelect2(this, this);
      triggerCrudFormReady(this);
    });

    // Connect the action buttons to their relevant handlers.
    $("[name=create_edit_object]").click(handleCreateEdit);
    $("[name=object_detail]").click(handleObjectDetail);
    $("a[name='delete_object']").click(handleDeleteObject);
    $("a[name='custom_action']").click(handleCustomAction);

    /**
     * This code is only relevant when a CRUD's Create/Update
     * operation form is presented in legacy mode. In this mode
     * we still need to initialize embedded formset, select2
     * controls & and Add Another links, if any.
     */
    var form = document.getElementById("create-edit-form")
    if (form) {
      initFormset(form);
      bindSelect2(form, form);
      bindAddAnother($(form));
      //triggerCrudFormReady(document);
    }
  }
  $(document).ready(function() {
    $.fn.popupCrud({});
  });
})(jQuery);
<commit_msg>Trigger crudform.ready event even in legacy mode so that client scripts can do their JS widget initialization from the same event handler and make the script work in both modal and legacy CRUD modes.<commit_after>/*
 * Event raised after a create/update form is displayed and ready for further
 * manipulation by client code. This event can be trapped by client code to
 * add their own custom JavaScript initialization to the form. Using this event
 * instead of the ubiqitous $(document).ready ensures that the same init code
 * works well on both legacy and popup modes.
 *
 * This allows you write code such as this:
 *
 *   $(document).on("crudform.ready", function(event) {
 *     // create/edit form initialization code goes here
 *   });
 */
const CRUDFORM_READY = "crudform.ready";

(function($) {
  $.fn.popupCrud = function (opts) { // wrap all popupcrud functions in a closure
    var _formsetTemplate = null,
    /*
     * Bind a submit function to a form embedded in a Bootstrap modal, which in
     * turn uses AJAX POST request to submit the form data. When the form has been
     * successully submitted, the modal is hidden. If the submitted form has errors
     * the form is re-rendered with field errors highlighted as per Bootstrap
     * rendering guidelines.
     *
       Parameters:
        form: jQuery selector to the form that is to be submited via AJAX
        modal: jQuery selector to the modal dialog to be dismissed post successful
               form submission
        complete: A function to be called upon successful form submission.
     */
    submitModalForm = function(form, modal, complete) {
        $(form).submit(function(e) {
            e.preventDefault();
            $.ajax({
                type: $(this).attr('method'),
                url: $(this).attr('action'),
                data: $(this).serialize(),
                success: function (xhr, ajaxOptions, thrownError) {
                    if ( $(xhr).find('.has-error').length > 0 ||
                         $(xhr).find('.alert').length > 0) {
                        $(modal).find('.modal-body').html(xhr);
                        bindAddAnother($(modal));
                        initFormset(modal)
                        bindSelect2(modal, modal);
                        triggerCrudFormReady(modal);
                        submitModalForm(form, modal, complete);
                    } else {
                        $(modal).modal('hide');
                        if (typeof complete == "function") {
                            complete(xhr); // notify caller
                        }
                    }
                },
                error: function (xhr, ajaxOptions, thrownError) {
                  // todo
                }
            });
        });
    },
    /* Helper function to retrieve cookie value */
    getCookie = function(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    },
    /*
     * Triggers crudform.ready event on an activated form if it's
     * not the delete-form
     */
    triggerCrudFormReady = function(elem) {
      var form = $(elem).find("form");
      if (form) {
        var ev = $.Event(CRUDFORM_READY);
        if (form.attr('id') != 'delete-form') { // exclude delete-form
          form.trigger(ev);
        }
      }
    },
    /*
     * Bind select2 widget to any form controls under 'parent' element
     * with css style .django-select2.
     */
    bindSelect2 = function(selectParent, dropdownParent) {
      var sels = $(selectParent).find('.django-select2');
      if (sels.length > 0) {
        $(sels).djangoSelect2({
          dropdownParent: $(dropdownParent)
        });
      }
    },
    /*
     * Binds all '.add-another' hyperlinks under the given 'elem' with their own
     * modals, each of which will hold the form for the .add-another's data-url
     * value. Each such modal is given an id composed as the value of the 'a'
     * tag's 'id' value + '-modal'. The modal will be added only if a modal with
     * the same id does not exist so as to avoid duplicate modals.

     * The 'a' tag is assigned the newly added modal's id, so that the associated
     * modal dialog can be loaded with the respective url form content and
     * activated in a generic manner from a Javascript function.
     */
    bindAddAnother = function(elem) {
      // Modal dialog template derived from #create-edit-modal that'll be added at
      // the end of the  tag.
      if ($("#add-related-modal").length == 0)
        return;

      var modalTemplate = $('
').html($("#add-related-modal")[0].outerHTML);
      $(modalTemplate)
        .find('.modal').attr('id', 'create-related-modal')
        .find('.modal-title').html('')
        .find('.modal-body').html('')
      var createRelatedModal = modalTemplate.html();
      elem.find(".add-another").each(function(index, elem) {
        var modalId = $(elem).attr('id') + '-modal';
        if ($("#"+modalId).length == 0) {
          $("body").append(createRelatedModal.replace('create-related-modal', modalId))
        }
        $(elem).data('modal', modalId);
      });

      /*
       * Generic function that loads the form associated with a hyperlink into
       * the related modal.
       *
       * Constraints & Behavior:
       *  1.  element is preceded immediately by a <select> element
       *  2.  element has the following data attributes:
       *      a. data-url: the URL to load the form from
       *      b. data-modal: id of the associated modal that is loaded with the
       *         form and activated.
       *  3. The activated modal's title is set to  element's text content.
       *  4. If the activated form submission was successful, the sibling 'select'
       *     element is popuplated with an <option> for the just added element.
       */
      $(elem).find(".add-another").click(function(evtObj) {
        var url = $(this).data('url');
        var title = $(this).text();
        var select = $(this).prevAll('select');
        var modalId = $(this).data('modal');
        var modal = $('#' + modalId);
        modal.find('.modal-body').load(url, function () {
          modal.find('.modal-title').text(title);
          bindAddAnother(modal);
          modal.modal('show');
          submitModalForm(modal.find('#create-edit-form'), '#'+modalId,
            function(xhr) {
              $(select).append($("<option></option>").
                attr("value", xhr.pk).text(xhr.name));
              var newVal = $(select).val();
              if ($(select).attr('multiple')) {
                newVal.push(xhr.pk);
              } else {
                newVal = xhr.pk;
              }
              $(select).val(newVal).trigger('change');
            })
        });
      });
    },
    // handler for New/Edit object actions
    handleCreateEdit = function(evtObj) {
      evtObj.preventDefault();
      var url = $(this).data('url');
      var title = $(this).data('title');
      $('#create-edit-modal .modal-body').load(url, function () {
        cacheFormsetTemplate();
        $('#create-edit-modal .modal-title').text(title);
        bindAddAnother($("#create-edit-modal"));
        $('#create-edit-modal').modal('show');
        submitModalForm('#create-edit-form',
          '#create-edit-modal', function(xhr) {
            location.reload();
          });
      });
    },
    // handler for object detail view
    handleObjectDetail = function(evtObj) {
      evtObj.preventDefault();
      var url = $(this).data('url');
      var title = $(this).data('title');
      $('#detail-modal .modal-body').load(url, function () {
        $('#detail-modal .modal-title').text(title);
        $('#detail-modal').modal('show');
      });
    },
    // delete an object action handler
    handleDeleteObject = function(evtObj) {
      evtObj.preventDefault();
      $('#delete-modal #id_object_name').text(
        $(evtObj.target).parents('tr').children(':nth-child(1)').children('div').data('name'));
      $('#delete-modal .modal-body form').attr(
        'action', $(evtObj.target).parent('a').data('url'));
      $('#delete-modal').modal('show');
      var title = $(this).children('span').attr('title');
      submitModalForm('#delete-form', '#delete-modal',
        function(xhr) {
          showActionResult(xhr.result, title, xhr.message);
        }
      );
    },
    // custom action handler
    handleCustomAction = function(evtObj) {
      evtObj.preventDefault();
      var action = $(this).data('action');
      var title = $(this).attr('title');
      $.ajax({
        type: 'POST',
        data: {
          csrfmiddlewaretoken: getCookie('csrftoken'),
          action: action,
          item: $(this).data('obj')
        },
        success: function (xhr, ajaxOptions, thrownError) {
          showActionResult(xhr.result, title, xhr.message);
        }
      });
    },
    // Show the action result message in a modal
    //
    // Parameters:
    //      result: bool, action success indicator
    //      title: title for the dialog
    //      message: message to be displayed in the modal
    // Returns:
    //      None
    showActionResult = function(result, title, message) {
      $("#action-result-modal .modal-title").text(title);
      $("#action-result-modal #id_action_result").html(message);
      $('#action-result-modal').on('hidden.bs.modal', result ?
        function(evtObj) {
          $('#action-result-modal').off('hidden.bs.modal');
          location.reload();
        } :
        function(evtObj) {
          $('#action-result-modal').off('hidden.bs.modal');
        });
      $('#action-result-modal').modal('show');
    },
    // reads the formset form template and stores in _formsetTemplate variable.
    cacheFormsetTemplate = function() {
      popupCrudFormsetFormTempl = $("#id_formset table>tbody>tr:last").clone(true).removeAttr('id');
      popupCrudFormsetFormTempl.find('input:hidden[id $= "-DELETE"]').remove();
      this._formsetTemplate = popupCrudFormsetFormTempl;
    },
    /**
     * Initializes any embedded formsets in the form such that formset form rows
     * can be added dynamically.
     *
     * If a formset is present, it is expected to be embedded within a
     *

 element.
     *
     * Parameters:
     *  parent - the parent element under which formset will be searched.
     */
    initFormset = function(parent) {
      var formsetDiv = $(parent).find('div#id_formset');
      if (formsetDiv.length > 0) {  // presence of div indicates form has formset.
        var prefix = formsetDiv.find('input[name*=TOTAL_FORMS]').attr('name').split('-')[0];
        formsetDiv.find("table tbody tr").formset({
          formTemplate: this._formsetTemplate,
          prefix: prefix,
          addText: '<button type="button" class="btn btn-default btn-sm" aria-label="Add"></button>',
          deleteText: "",
          added: function (row) {
            var dropDownParent = $(document.body);  // default
            if (formsetDiv.parents('.modal').length > 0) {
              dropDownParent = formsetDiv.parents('.modal');
            }
            bindSelect2(row, dropDownParent);
          }
        });
      }
    };

    /*
     * Adjusts the just activated modal window's z-index to a value higher
     * than the previously activated modal window's z-index. This will
     * ensure that each newly activated modal is layered on top of all
     * previously activated modals achieving the layered dialog effect.

     * Code borrowed from: http://jsfiddle.net/CxdUQ/
     */
    $(document).on('show.bs.modal', '.modal', function (event) {
      // calculate z-index as a function of number of visible modal windows.
      var zIndex = 1040 + (10 * $('.modal:visible').length);
      $(this).css('z-index', zIndex);
      setTimeout(function() {
        $('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - 1).addClass('modal-stack');
      }, 0);
    });
    $(document).on('shown.bs.modal', '.modal', function (event) {
      /*
       * Initialize any django-select2 fields in the dialog. If the dialog does
       * not have any select2 fields, this code does nothing.
       *
       * Also this is a template for any custom initialization to be done
       * by user javascript codes. Typically if the user code has to do UI level
       * processing, it does it from $(document).ready(). But for UI processing
       * on modal dialogs, it has to be done from $(document).on('shown.bs.modal').
       */
      initFormset(this);
      bindSelect2(this, this);
      triggerCrudFormReady(this);
    });

    // Connect the action buttons to their relevant handlers.
    $("[name=create_edit_object]").click(handleCreateEdit);
    $("[name=object_detail]").click(handleObjectDetail);
    $("a[name='delete_object']").click(handleDeleteObject);
    $("a[name='custom_action']").click(handleCustomAction);

    /**
     * This code is only relevant when a CRUD's Create/Update
     * operation form is presented in legacy mode. In this mode
     * we still need to initialize embedded formset, select2
     * controls & and Add Another links, if any.
     */
    var form = document.getElementById("create-edit-form")
    if (form) {
      initFormset(form);
      bindSelect2(form, form);
      bindAddAnother($(form));
      triggerCrudFormReady(document);
    }
  }
  $(document).ready(function() {
    $.fn.popupCrud({});
  });
})(jQuery);
<|endoftext|>


<commit_before>/* $This file is distributed under the terms of the license in /doc/license.txt$ */

var menuManagementEdit = {
    onLoad: function() {
        this.initObjects();
        this.bindEventListeners();
        this.toggleClassSelection();
        //this.validateMenuItemForm();
    },
    initObjects: function() {
        this.defaultTemplateRadio = $('input.default-template');
        this.customTemplateRadio = $('input.custom-template');
        this.customTemplate = $('#custom-template');
        this.changeContentType = $('#changeContentType');
        this.selectContentType = $('#selectContentType');
        this.existingContentType = $('#existingContentType');
        this.selectClassGroupDropdown = $('#selectClassGroup');
        this.classesForClassGroup = $('#classesInSelectedGroup');
        this.selectedGroupForPage = $('#selectedContentTypeValue');
        // this.selectClassesMessage = $('#selectClassesMessage');
        this.allClassesSelectedCheckbox = $('#allSelected');

    },
    bindEventListeners: function() {
        // Listeners for vClass switching
        this.changeContentType.click(function() {
           menuManagementEdit.showClassGroups();
           return false;
        });
        this.selectClassGroupDropdown.change(function() {
            menuManagementEdit.chooseClassGroup();
        });
        // this.allClassesSelectedCheckbox.change(function() {
        //     menuManagementEdit.toggleClassSelection();
        // });

        // Listeners for template field
        this.defaultTemplateRadio.click(function(){
            menuManagementEdit.customTemplate.addClass('hidden');
        });
        this.customTemplateRadio.click(function(){
            // If checked, hide this input element
            menuManagementEdit.customTemplate.removeClass('hidden');
        });
        $("form").submit(function () {
            var validationError = menuManagementEdit.validateMenuItemForm();
            if (validationError == "") {
            	alert("validation error is empty, submit");
                   $(this).submit();
               } else{
            	   alert("validation error is not empty");
                   $('#error-alert').removeClass('hidden');
                   $('#error-alert p').html(validationError);
                   return false;
               }
         });
    },
    showClassGroups: function() { //User has clicked change content type
    	//Show the section with the class group dropdown
    	this.selectContentType.removeClass("hidden");
    	//Hide the "change content type" section which shows the selected class group
    	this.existingContentType.addClass("hidden");
    	//Hide the checkboxes for classes within the class group
        this.classesForClassGroup.addClass("hidden");
    },
    hideClassGroups: function() { //User has selected class group/content type, page should show classes for class group and 'existing' type with change link
    	//Hide the class group dropdown
    	this.selectContentType.addClass("hidden");
    	//Show the "change content type" section which shows the selected class group
    	this.existingContentType.removeClass("hidden");
    	//Show the classes in the class group
    	this.classesForClassGroup.removeClass("hidden");

    },
    toggleClassSelection: function() {
        /*To do: please fix so selecting all selects all classes and deselecting
         * any class will deselect all
         */
        /*
        if(this.allClassesSelectedCheckbox.is(':checked')) {
            $('#classInClassGroup').attr('checked', 'checked');
        } else {
            $('#classInClassGroup').removeAttr('checked');
        }*/
        // Check/unckeck all classes for selection
        $('input:checkbox[name=allSelected]').click(function(){
             // alert($('input:checkbox[name=classInClassGroup]'));
             if ( this.checked ) {
             // if checked, select all the checkboxes
             $('input:checkbox[name=classInClassGroup]').attr('checked','checked');

             } else {
             // if not checked, deselect all the checkboxes
               $('input:checkbox[name=classInClassGroup]').removeAttr('checked');
             }
        });

        $('input:checkbox[name=classInClassGroup]').click(function(){
            $('input:checkbox[name=allSelected]').removeAttr('checked');
        });
    },
    validateMenuItemForm: function() {
        var validationError = "";

        //Check menu name
        if ($("input[type=text][name=menuName]").val() == ""){
            validationError += "You must supply a Name
";
            }
        //Check pretty url
        if ($("input[type=text][name=prettyUrl]").val() == ""){
            validationError += "You must supply a Pretty URL
";
        }

        //if ($("input[name='selectedTemplate']:checked").val() == "custom") {

           // if ($("input[name=customTemplate]").val() == ""){
               // validationError += " * Enter custom template \n";
            //}
        //}
        //check select class group
        if ($("#selectClassGroup").val() =='-1'){
           validationError += "You must supply a Content type
";
        }
        return validationError;
    },
    chooseClassGroup: function() {
        var url = "dataservice?getVClassesForVClassGroup=1&classgroupUri=";
        var vclassUri = this.selectClassGroupDropdown.val();
        url += encodeURIComponent(vclassUri);
        //Make ajax call to retrieve vclasses
        $.getJSON(url, function(results) {

          if ( results.classes.length == 0 ) {

          } else {
              //update existing content type with correct class group name and hide class group select again
              var _this = menuManagementEdit;
              menuManagementEdit.hideClassGroups();

              menuManagementEdit.selectedGroupForPage.html(results.classGroupName);
                //retrieve classes for class group and display with all selected
              var selectedClassesList = menuManagementEdit.classesForClassGroup.children('ul#selectedClasses');

              selectedClassesList.empty();
              selectedClassesList.append('
 <input type="checkbox" name="allSelected" id="allSelected" value="all" checked="checked" />  All
');

              $.each(results.classes, function(i, item) {
                  var thisClass = results.classes[i];
                  var thisClassName = thisClass.name;
                  //When first selecting new content type, all classes should be selected
                  appendHtml = '
' +
                          '<input type="checkbox" checked="checked" name="classInClassGroup" value="' + thisClass.URI + '" />' +
                         ' ' + thisClassName + '' +
                          '
';
                  selectedClassesList.append(appendHtml);
              });
              menuManagementEdit.toggleClassSelection();
          }

        });
    }
};

$(document).ready(function() {
    menuManagementEdit.onLoad();
});<commit_msg>updates for validation<commit_after>/* $This file is distributed under the terms of the license in /doc/license.txt$ */

var menuManagementEdit = {
    onLoad: function() {
        this.initObjects();
        this.bindEventListeners();
        this.toggleClassSelection();
        //this.validateMenuItemForm();
    },
    initObjects: function() {
        this.defaultTemplateRadio = $('input.default-template');
        this.customTemplateRadio = $('input.custom-template');
        this.customTemplate = $('#custom-template');
        this.changeContentType = $('#changeContentType');
        this.selectContentType = $('#selectContentType');
        this.existingContentType = $('#existingContentType');
        this.selectClassGroupDropdown = $('#selectClassGroup');
        this.classesForClassGroup = $('#classesInSelectedGroup');
        this.selectedGroupForPage = $('#selectedContentTypeValue');
        // this.selectClassesMessage = $('#selectClassesMessage');
        this.allClassesSelectedCheckbox = $('#allSelected');

    },
    bindEventListeners: function() {
        // Listeners for vClass switching
        this.changeContentType.click(function() {
           menuManagementEdit.showClassGroups();
           return false;
        });
        this.selectClassGroupDropdown.change(function() {
            menuManagementEdit.chooseClassGroup();
        });
        // this.allClassesSelectedCheckbox.change(function() {
        //     menuManagementEdit.toggleClassSelection();
        // });

        // Listeners for template field
        this.defaultTemplateRadio.click(function(){
            menuManagementEdit.customTemplate.addClass('hidden');
        });
        this.customTemplateRadio.click(function(){
            // If checked, hide this input element
            menuManagementEdit.customTemplate.removeClass('hidden');
        });
        $("form").submit(function () {
            var validationError = menuManagementEdit.validateMenuItemForm();
            if (validationError == "") {
                   $(this).submit();
               } else{
                   $('#error-alert').removeClass('hidden');
                   $('#error-alert p').html(validationError);
                   return false;
               }
         });
    },
    showClassGroups: function() { //User has clicked change content type
    	//Show the section with the class group dropdown
    	this.selectContentType.removeClass("hidden");
    	//Hide the "change content type" section which shows the selected class group
    	this.existingContentType.addClass("hidden");
    	//Hide the checkboxes for classes within the class group
        this.classesForClassGroup.addClass("hidden");
    },
    hideClassGroups: function() { //User has selected class group/content type, page should show classes for class group and 'existing' type with change link
    	//Hide the class group dropdown
    	this.selectContentType.addClass("hidden");
    	//Show the "change content type" section which shows the selected class group
    	this.existingContentType.removeClass("hidden");
    	//Show the classes in the class group
    	this.classesForClassGroup.removeClass("hidden");

    },
    toggleClassSelection: function() {
        /*To do: please fix so selecting all selects all classes and deselecting
         * any class will deselect all
         */
        /*
        if(this.allClassesSelectedCheckbox.is(':checked')) {
            $('#classInClassGroup').attr('checked', 'checked');
        } else {
            $('#classInClassGroup').removeAttr('checked');
        }*/
        // Check/unckeck all classes for selection
        $('input:checkbox[name=allSelected]').click(function(){
             if ( this.checked ) {
             // if checked, select all the checkboxes
             $('input:checkbox[name=classInClassGroup]').attr('checked','checked');

             } else {
             // if not checked, deselect all the checkboxes
               $('input:checkbox[name=classInClassGroup]').removeAttr('checked');
             }
        });

        $('input:checkbox[name=classInClassGroup]').click(function(){
            $('input:checkbox[name=allSelected]').removeAttr('checked');
        });
    },
    validateMenuItemForm: function() {
        var validationError = "";

        //Check menu name
        if ($("input[type=text][name=menuName]").val() == ""){
            validationError += "You must supply a Name
";
            }
        //Check pretty url
        if ($("input[type=text][name=prettyUrl]").val() == ""){
            validationError += "You must supply a Pretty URL
";
        }

        if ($("input:radio[name=selectedTemplate]:checked").val() == "custom") {
        	if($("input[name=customTemplate]").val() == "") {
        		validationError += " * Enter custom template
";
        	}
        }

        //if no class group selected, this is an error
        if ($("#selectClassGroup").val() =='-1'){
            validationError += "You must supply a Content type
";
        } else {
        	//class group has been selected, make sure there is at least one class selected
        	var noClassesSelected = $("input[name='classInClassGroup']:checked").length;
        	if(noClassesSelected == 0) {
        		//at least one class should be selected
        		validationError += "You must have at least one class selected
";
        	}
        }

        //check select class group

        return validationError;
    },
    chooseClassGroup: function() {
        var url = "dataservice?getVClassesForVClassGroup=1&classgroupUri=";
        var vclassUri = this.selectClassGroupDropdown.val();
        url += encodeURIComponent(vclassUri);
        //Make ajax call to retrieve vclasses
        $.getJSON(url, function(results) {

          if ( results.classes.length == 0 ) {

          } else {
              //update existing content type with correct class group name and hide class group select again
              var _this = menuManagementEdit;
              menuManagementEdit.hideClassGroups();

              menuManagementEdit.selectedGroupForPage.html(results.classGroupName);
                //retrieve classes for class group and display with all selected
              var selectedClassesList = menuManagementEdit.classesForClassGroup.children('ul#selectedClasses');

              selectedClassesList.empty();
              selectedClassesList.append('
 <input type="checkbox" name="allSelected" id="allSelected" value="all" checked="checked" />  All
');

              $.each(results.classes, function(i, item) {
                  var thisClass = results.classes[i];
                  var thisClassName = thisClass.name;
                  //When first selecting new content type, all classes should be selected
                  appendHtml = '
' +
                          '<input type="checkbox" checked="checked" name="classInClassGroup" value="' + thisClass.URI + '" />' +
                         ' ' + thisClassName + '' +
                          '
';
                  selectedClassesList.append(appendHtml);
              });
              menuManagementEdit.toggleClassSelection();
          }

        });
    }
};

$(document).ready(function() {
    menuManagementEdit.onLoad();
});<|endoftext|>


<commit_before>// AppMap.js
//
// creates 'appmap' which can build and store app definitions
// apps have screens
// screens have actions
// actions have parameters
// actions have device-specific implementations

var debugAppmap = false;

(function() {

    var root = this,
        appmap = null;

    // put appmap in namespace of importing code
    if (typeof exports !== 'undefined') {
        appmap = exports;
    } else {
        appmap = root.appmap = {};
    }

    appmap.apps = {};       // all possible apps, keyed by string
    var lastApp;            // state variable for building app
    var lastAppName;        // name of last app
    var lastScreen;         // state variable for building screen
    var lastScreenName;     // name of last screen
    var lastAction;         // state variable for building action
    var lastActionName;     // name of last action
    var lastScreenActiveFn; // action function map of last screen

    // create a new app in the appmap with the given name
    appmap.createApp = function(appName) {
        appmap.lastApp = {}; // screens is empty
        appmap.apps[appName] = appmap.lastApp;
        appmap.lastScreen = null;
        appmap.lastAction = null;
        return this;
    };

    // whether an app exists
    appmap.hasApp = function(appName) {
        return appName in appmap.apps;
    };

    // function to re-start editing a app
    appmap.augmentApp = function(appName) {
        appmap.lastApp = appmap.apps[appName];
        appmap.lastAppName = appName;
        return this;
    };

    // function to do the right thing
    appmap.createOrAugmentApp = function(appName) {
        return appmap.hasApp(appName) ? appmap.augmentApp(appName) : appmap.createApp(appName);
    }

    // create a new screen in the latest app, with the given name
    appmap.withNewScreen = function(screenName) {
        appmap.lastScreen = {};
        appmap.lastScreenName = screenName;
        appmap.lastScreenActiveFn = {};
        appmap.lastAction = null;

        appmap.lastApp[appmap.lastScreenName] = appmap.lastScreen;
        if (debugAppmap) UIALogger.logDebug(" adding screen " + appmap.lastScreenName);

        return this;
    };

    // augment an existing screen
    appmap.augmentScreen = function(screenName) {
        appmap.lastScreenName = screenName;
        appmap.lastScreen = appmap.lastApp[appmap.lastScreenName];
        if (debugAppmap) UIALogger.logDebug(" augmenting screen " + appmap.lastScreenName);
        return this;
    }

    // whether a screen exists
    appmap.hasScreen = function(appName, screenName) {
        return appmap.hasApp(appName) && (screenName in appmap.apps[appName]);
    }

    // function to do the right thing
    appmap.withScreen = function(screenName) {
        return appmap.hasScreen(appmap.lastAppName, screenName) ? appmap.augmentScreen(screenName) : appmap.withNewScreen(screenName);
    }

    // enable the screen on a given device by setting the isActiveFn()
    //  isActiveFn() should return true if the screen is currently both visible and accessible
    appmap.onDevice = function(deviceName, isActiveFn) {
        if (debugAppmap) UIALogger.logDebug("  on Device " + deviceName);
        appmap.lastScreenActiveFn[deviceName] = isActiveFn;

        appmap.withAction("verifyIsActive", "Null op to verify that the " + appmap.lastScreenName + " screen is active")
              .withImplementation(function() {}, deviceName);

        appmap.withAction("verifyNotActive", "Verify that the " + appmap.lastScreenName + " screen is NOT active")
              .withImplementation(function() {
                      if (isActiveFn()) throw "Failed assertion that '" + appmap.lastScreenName + "' is NOT active ";
                  }, deviceName);

        // now modify verifyNotActive's isCorrectScreen array to always return true.  slighly hacky.
        // this is because the meat of the function runs in our generated action
        for (var d in appmap.lastAction.isCorrectScreen) {
            appmap.lastAction.isCorrectScreen[d] = function () { return true; };
        }

        return this;
    };

    // create a new action in the latest screen, with the given name, description, and function
    appmap.withNewAction = function(actionName, desc) {
        // we add screen params to the action so that we can deal in actions alone
        appmap.lastAction = {
            name: actionName,
            isCorrectScreen: appmap.lastScreenActiveFn,
            screenName: appmap.lastScreenName,
            actionFn: {},
            description: desc,
            params: {}
        };
        appmap.lastActionName = actionName;
        if (debugAppmap) UIALogger.logDebug("  adding action " + appmap.lastActionName);
        appmap.lastScreen[appmap.lastActionName] = appmap.lastAction;
        return this;
    };

    // augment an existing action
    appmap.augmentAction = function(actionName) {
        if (debugAppmap) UIALogger.logDebug("  augmenting action " + actionName);
        appmap.lastAction = appmap.lastScreen[actionName];
        appmap.lastActionName = actionName;
        return this;
    }

    // whether an action exists
    appmap.hasAction = function(appName, screenName, actionName) {
        return appmap.hasScreen(appName, screenName) && (actionName in appmap.apps[appName][screenName]);
    }

    // do the right thing
    appmap.withAction = function(actionName, desc) {
        return appmap.hasAction(appmap.lastAppName, appmap.lastScreenName, actionName) ? appmap.augmentAction(actionName) : appmap.withNewAction(actionName, desc);
    }

    // create a new parameter in the latest action, with the given varname and description
    // optionally, useInSummary to indiciate whether the parameter should be printed in the step description
    appmap.withParam = function(paramName, desc, required, useInSummary) {
        if (debugAppmap) UIALogger.logDebug("   adding parameter " + paramName);
        useInSummmary = useInSummary === undefined ? false : useInSummary;
        appmap.lastAction.params[paramName] = {
            description: desc,
            required: required,
            useInSummary: useInSummary
        };
        return this;
    };

    // create a new implementation for the latest action
    // actFn will take one optional argument -- an associative array
    appmap.withImplementation = function(actFn, deviceName) {
        deviceName = deviceName === undefined ? "default" : deviceName;

        // catch implementations for nonexistent devices
        if ("default" != deviceName && !(deviceName in appmap.lastAction.isCorrectScreen)) {
            var devices = [];
            for (var k in appmap.lastAction.isCorrectScreen) {
                devices.push(k);
            }
            var msg = "Screen " + appmap.lastAppName + "." + appmap.lastScreenName;
            msg += " only has devices: '" + devices.join("', '") + "' but tried to add an implementation";
            msg += " for device '" + deviceName + "' in action '" + appmap.lastActionName + "'";
            throw msg;
        }

        if (debugAppmap) UIALogger.logDebug("   adding implementation on " + deviceName);
        appmap.lastAction.actionFn[deviceName] = actFn;
        return this;
    }

    appmap.getApps = function() {
        var ret = [];
        for (d in appmap.apps) ret.push(d);
        return ret;
    };

    appmap.getScreens = function(app) {
        var ret = [];
        for (s in appmap.apps[app]) ret.push(s);
        return ret;
    };

}).call(this);
<commit_msg>[appmap] fix bug where screenIsActive function was being overwritten in every action instead of just one<commit_after>// AppMap.js
//
// creates 'appmap' which can build and store app definitions
// apps have screens
// screens have actions
// actions have parameters
// actions have device-specific implementations

var debugAppmap = false;

(function() {

    var root = this,
        appmap = null;

    // put appmap in namespace of importing code
    if (typeof exports !== 'undefined') {
        appmap = exports;
    } else {
        appmap = root.appmap = {};
    }

    appmap.apps = {};       // all possible apps, keyed by string
    var lastApp;            // state variable for building app
    var lastAppName;        // name of last app
    var lastScreen;         // state variable for building screen
    var lastScreenName;     // name of last screen
    var lastAction;         // state variable for building action
    var lastActionName;     // name of last action
    var lastScreenActiveFn; // action function map of last screen

    // create a new app in the appmap with the given name
    appmap.createApp = function(appName) {
        appmap.lastApp = {}; // screens is empty
        appmap.apps[appName] = appmap.lastApp;
        appmap.lastScreen = null;
        appmap.lastAction = null;
        return this;
    };

    // whether an app exists
    appmap.hasApp = function(appName) {
        return appName in appmap.apps;
    };

    // function to re-start editing a app
    appmap.augmentApp = function(appName) {
        appmap.lastApp = appmap.apps[appName];
        appmap.lastAppName = appName;
        return this;
    };

    // function to do the right thing
    appmap.createOrAugmentApp = function(appName) {
        return appmap.hasApp(appName) ? appmap.augmentApp(appName) : appmap.createApp(appName);
    }

    // create a new screen in the latest app, with the given name
    appmap.withNewScreen = function(screenName) {
        appmap.lastScreen = {};
        appmap.lastScreenName = screenName;
        appmap.lastScreenActiveFn = {};
        appmap.lastAction = null;

        appmap.lastApp[appmap.lastScreenName] = appmap.lastScreen;
        if (debugAppmap) UIALogger.logDebug(" adding screen " + appmap.lastScreenName);

        return this;
    };

    // augment an existing screen
    appmap.augmentScreen = function(screenName) {
        appmap.lastScreenName = screenName;
        appmap.lastScreen = appmap.lastApp[appmap.lastScreenName];
        if (debugAppmap) UIALogger.logDebug(" augmenting screen " + appmap.lastScreenName);
        return this;
    }

    // whether a screen exists
    appmap.hasScreen = function(appName, screenName) {
        return appmap.hasApp(appName) && (screenName in appmap.apps[appName]);
    }

    // function to do the right thing
    appmap.withScreen = function(screenName) {
        return appmap.hasScreen(appmap.lastAppName, screenName) ? appmap.augmentScreen(screenName) : appmap.withNewScreen(screenName);
    }

    // enable the screen on a given device by setting the isActiveFn()
    //  isActiveFn() should return true if the screen is currently both visible and accessible
    appmap.onDevice = function(deviceName, isActiveFn) {
        if (debugAppmap) UIALogger.logDebug("  on Device " + deviceName);
        appmap.lastScreenActiveFn[deviceName] = isActiveFn;

        appmap.withAction("verifyIsActive", "Null op to verify that the " + appmap.lastScreenName + " screen is active");
        // slightly hacky, withImplementation expects actions to come AFTER all the onDevice calls
        appmap.lastAction.isCorrectScreen[deviceName] = isActiveFn;
        appmap.withImplementation(function() {}, deviceName);

        appmap.withAction("verifyNotActive", "Verify that the " + appmap.lastScreenName + " screen is NOT active")
        // slightly hacky, withImplementation expects actions to come AFTER all the onDevice calls
        appmap.lastAction.isCorrectScreen[deviceName] = isActiveFn;
        appmap.withImplementation(function() {
                      if (isActiveFn()) throw "Failed assertion that '" + appmap.lastScreenName + "' is NOT active ";
                  }, deviceName);

        // now modify verifyNotActive's isCorrectScreen array to always return true.  slighly hacky.
        // this is because the meat of the function runs in our generated action
        for (var d in appmap.lastAction.isCorrectScreen) {
            appmap.lastAction.isCorrectScreen[d] = function () {
                UIALogger.logDebug("verifyNotActive is skipping the default screenIsActive function");
                return true;
            };
        }

        return this;
    };

    // create a new action in the latest screen, with the given name, description, and function
    appmap.withNewAction = function(actionName, desc) {
        // we need this hack to prevent problems with the above isCorrectScreen hack --
        //  so that we don't change the original reference, we rebuild the {devicename : function} map manually
        var frozen = function(k) { return appmap.lastScreenActiveFn[k]; };
        var isActiveMap = {};
        for (var k in appmap.lastScreenActiveFn) {
            isActiveMap[k] = frozen(k);
        }

        // we add screen params to the action so that we can deal in actions alone
        appmap.lastAction = {
            name: actionName,
            isCorrectScreen: isActiveMap,
            screenName: appmap.lastScreenName,
            actionFn: {},
            description: desc,
            params: {}
        };
        appmap.lastActionName = actionName;
        if (debugAppmap) UIALogger.logDebug("  adding action " + appmap.lastActionName);
        appmap.lastScreen[appmap.lastActionName] = appmap.lastAction;
        return this;
    };

    // augment an existing action
    appmap.augmentAction = function(actionName) {
        if (debugAppmap) UIALogger.logDebug("  augmenting action " + actionName);
        appmap.lastAction = appmap.lastScreen[actionName];
        appmap.lastActionName = actionName;
        return this;
    }

    // whether an action exists
    appmap.hasAction = function(appName, screenName, actionName) {
        return appmap.hasScreen(appName, screenName) && (actionName in appmap.apps[appName][screenName]);
    }

    // do the right thing
    appmap.withAction = function(actionName, desc) {
        return appmap.hasAction(appmap.lastAppName, appmap.lastScreenName, actionName) ? appmap.augmentAction(actionName) : appmap.withNewAction(actionName, desc);
    }

    // create a new parameter in the latest action, with the given varname and description
    // optionally, useInSummary to indiciate whether the parameter should be printed in the step description
    appmap.withParam = function(paramName, desc, required, useInSummary) {
        if (debugAppmap) UIALogger.logDebug("   adding parameter " + paramName);
        useInSummmary = useInSummary === undefined ? false : useInSummary;
        appmap.lastAction.params[paramName] = {
            description: desc,
            required: required,
            useInSummary: useInSummary
        };
        return this;
    };

    // create a new implementation for the latest action
    // actFn will take one optional argument -- an associative array
    appmap.withImplementation = function(actFn, deviceName) {
        deviceName = deviceName === undefined ? "default" : deviceName;

        // catch implementations for nonexistent devices
        if ("default" != deviceName && !(deviceName in appmap.lastAction.isCorrectScreen)) {
            var devices = [];
            for (var k in appmap.lastAction.isCorrectScreen) {
                devices.push(k);
            }
            var msg = "Screen " + appmap.lastAppName + "." + appmap.lastScreenName;
            msg += " only has defined devices: '" + devices.join("', '") + "' but tried to add an implementation";
            msg += " for device '" + deviceName + "' in action '" + appmap.lastActionName + "'";
            throw msg;
        }

        if (debugAppmap) UIALogger.logDebug("   adding implementation on " + deviceName);
        appmap.lastAction.actionFn[deviceName] = actFn;
        return this;
    }

    appmap.getApps = function() {
        var ret = [];
        for (d in appmap.apps) ret.push(d);
        return ret;
    };

    appmap.getScreens = function(app) {
        var ret = [];
        for (s in appmap.apps[app]) ret.push(s);
        return ret;
    };

}).call(this);
<|endoftext|>


<commit_before>var Spinner = require('../').Spinner;
var numSpinners = Spinner.spinners.length;
var spinner = null;
var index = 0;
var delay = 5000;

function nextSpinner(d) {
  d = d || delay;

  setTimeout(function() {
    if (spinner !== null) {
      spinner.stop();
      process.stdout.write('\n');
    }

    spinner = new Spinner('Spinner ' + (index + 1) + ' of ' + numSpinners);
    spinner.setSpinnerString(index);
    spinner.start();

    if (index < numSpinners) {
      index++;
      nextSpinner();
    } else {
      spinner.stop();
      process.stdout.write('\n');
      spinner = new Spinner('Spinner at custom position %s <--');
      spinner.start();

      setTimeout(function() {
        spinner.stop();
        process.stdout.write('\n');
        spinner = new Spinner('Custom spinner with custom speed');
        spinner.setSpinnerString('slow');
        spinner.setSpinnerDelay(500);
        spinner.start();
      }, delay);
    }
  }, d);
}

nextSpinner(0);
<commit_msg>Fix example<commit_after>var Spinner = require('../').Spinner;
var numSpinners = Spinner.spinners.length;
var spinner = null;
var index = 0;
var delay = 5000;

function nextSpinner(d) {
  d = (d === 0) ? d : delay;

  setTimeout(function() {
    if (spinner !== null) {
      spinner.stop();
      process.stdout.write('\n');
    }

    spinner = new Spinner('Spinner ' + (index + 1) + ' of ' + numSpinners);
    spinner.setSpinnerString(index);
    spinner.start();

    if (index < numSpinners) {
      index++;
      nextSpinner();
    } else {
      spinner.stop();
      process.stdout.write('\n');
      spinner = new Spinner('Spinner at custom position %s <--');
      spinner.start();

      setTimeout(function() {
        spinner.stop();
        process.stdout.write('\n');
        spinner = new Spinner('Custom spinner with custom speed');
        spinner.setSpinnerString('slow');
        spinner.setSpinnerDelay(500);
        spinner.start();
      }, delay);
    }
  }, d);
}

nextSpinner(0);
<|endoftext|>


<commit_before>define([
    'zepto',
    'Underscore',
    'Backbone',
    'models/expense',
    'text!../../tpl/expense-detail.html'
], function ($, _, Backbone, expense, template) {
    var ExpenseDetailsView = Backbone.View.extend({
        events:{
            "click #put":"saveExpense",
            "click #add":"addLine",
            "click #home":"close"
        },

        initialize:function () {
            this.slot=this.options.slot;
            this.model.on("reset", this.render, this);
            this.model.on("sync", this.render, this);
        },
        renderTemplate:function (json) {
            var $expenseElement = $(_.template(template, json));
            _.each(json.lines, function (line, index) {
                $expenseElement.find('#expenseType')[index].value=line.expenseType;
            });
            return $expenseElement;
        },

        render:function () {
            var $element = this.renderTemplate(this.model.toJSON());
            this.setElement($element);
            this.slot.html(this.el);
            return this;
        },

        saveExpense:function () {
            var elements = $('#content td#expense-line');
            var view = this;
            var lines = _.map(elements, function (template) {
                t = $(template);
                return {
                    "expense":(+t.find('#amount').val()),
                    "description":t.find('#description').val(),
                    "valueDate":t.find('#valueDate').val(),
                    "expenseType":t.find('#expenseType').val(),
                    "account":"Xebia"
                };
            });
            if (this.model.isNew()) {
                this.model.unset('id')
            }
            this.model.save({
                startDate:$('#startDate').val(),
                endDate:$('#endDate').val(),
                total:$('#total').val(),
                lines:lines
            });
            return false;
        },
        addLine:function () {
            var $table = $("#content table");
            var $element = this.renderTemplate(this.model.defaults);
            $table.append($element.find('table tbody tr'));
        },
        close:function () {
            $(this.el).unbind();
            $(this.el).empty();
        }

    });
    return ExpenseDetailsView;
});
<commit_msg>fix dom parsing<commit_after>define([
    'zepto',
    'Underscore',
    'Backbone',
    'models/expense',
    'text!../../tpl/expense-detail.html'
], function ($, _, Backbone, expense, template) {
    var ExpenseDetailsView = Backbone.View.extend({
        events:{
            "click #put":"saveExpense",
            "click #add":"addLine",
            "click #home":"close"
        },

        initialize:function () {
            this.slot=this.options.slot;
            this.model.on("reset", this.render, this);
            this.model.on("sync", this.render, this);
        },
        renderTemplate:function (json) {
            var $expenseElement = $(_.template(template, json));
            _.each(json.lines, function (line, index) {
                $expenseElement.find('[name="expenseType"]')[index].value=line.expenseType;
            });
            return $expenseElement;
        },

        render:function () {
            this.$el.html(this.renderTemplate(this.model.toJSON()));
            this.slot.html(this.el);
            return this;
        },

        saveExpense:function () {
            var elements = $('#content td#expense-line');
            var view = this;
            var lines = _.map(elements, function (template) {
                t = $(template);
                return {
                    "expense":(+t.find('[name="amount"]').val()),
                    "description":t.find('[name="description"]').val(),
                    "valueDate":t.find('[name="valueDate"]').val(),
                    "expenseType":t.find('[name="expenseType"]').val(),
                    "account":"Xebia"
                };
            });
            if (this.model.isNew()) {
                this.model.unset('id')
            }
            this.model.save({
                startDate:$('[name="startDate"]').val(),
                endDate:$('[name="endDate"]').val(),
                total:$('[name="total"]').val(),
                lines:lines
            });
            return false;
        },
        addLine:function () {
            var $table = $("#content table");
            var $element = this.renderTemplate(this.model.defaults);
            $table.append($element.find('table tbody tr'));
        },
        close:function () {
            $(this.el).unbind();
            $(this.el).empty();
        }

    });
    return ExpenseDetailsView;
});
<|endoftext|>