<commit_before>// *************************************************************************
// Copyright (c) 2014, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// start.js
//
// 'start' (i.e. HTML display and event handling) methods for menus, forms,
// browsers
//
define ([
    'jquery',
    'lib',
    'target',
    'app/dnotice-hooks'
], function (
    $,
    lib,
    target,
    dnoticeHooks
) {
    var
        //
        // generalized handlers
        //

        // used to suppress submit events when we don't need them
        suppressSubmitEvent = function (event) {
            event.preventDefault();
            console.log("Suppressed submit event");
        },

        //
        // dmenu handlers
        //
        dmenuSubmit = function (dmn) {
            // dmn is dmenu name
            // dmo is dmenu object
            var dmo = target.pull(dmn),
                sel = $('input[name="sel"]').val(),
                len = dmo.entries.length,
                entry,
                selection;

            if ($.isNumeric(sel) && sel >= 0 && sel <= len) {
                // we can only select the entry if we have sufficient priv level
                selection = target.pull(dmo.entries[sel]);
                if (lib.privCheck(selection.aclProfile)) {
                    //console.log('Selection ' + sel + ' passed priv check');
                    entry = selection;
                }
            } else if (sel === 'X' || sel === 'x') {
                entry = target.pull(dmo.back);
            } else if (sel === '') {
                // user hit 'enter'
                return;
            }
            if (entry !== undefined) {
                //console.log("Selected " + dmn + " menu entry: " + entry.name);
                console.log("About to call the 'start' method of ", entry);
                entry.start();
            }
        },
        dmenuSubmitKey = function (dmn) {
            return function (event) {
                event.preventDefault();
                //console.log("Submitting: " + dmn);
                dmenuSubmit(dmn);
            };
        },
        dmenuKeyListener = function (dmn) {
            return function (event) {
                lib.logKeyPress(event);
                if (event.keyCode === 13) {
                    dmenuSubmitKey(dmn);
                } else if (event.keyCode === 9) {
                    event.preventDefault();
                }
            };
        },

        //
        // miniMenu handlers
        //
        // mmKeyListener - generalized keypress handler for miniMenus
        // (see below for usage)
        mmKeyListener = function (evt) {

            var len = $("input:text").length,
                n = $("input:text").index($(document.activeElement));

            lib.logKeyPress(evt);

            if (evt.keyCode === 13) {
                console.log('MiniMenu listener detected <ENTER> keypress');
                console.log("This form has elements 0 through " + (len - 1));
                console.log("The current element is no. " + n);
                evt.preventDefault();
                if ( n === len - 1 ) {
                    console.log("Triggering submit button click");
                    $('#submitButton').click();
                } else {
                    $("input:text")[n + 1].focus();
                }

            } else if (evt.keyCode === 9) {
                var elnam = $(document.activeElement).attr("name");
                if (
                        (elnam === 'entry0' && evt.shiftKey) ||
                        (elnam === 'sel' && len === 1) ||
                        (elnam === 'sel' && !evt.shiftKey)
                   ) {
                    evt.preventDefault();
                }
            }

        },

        //
        // dform handlers
        //
        dformSubmit = function (dfn, obj) {
            // dfn is dform name
            // dfo is dform object
            var dfo = target.pull(dfn);

            //console.log("Entering MFILE.dformSubmit with argument " + dfn);
            lib.clearResult();

            var sel = $('input[name="sel"]').val(),
                len,
                i,
                newObj,
                selection,
                entry,
                item,
                wlen;

            // if miniMenu has zero or one entries, 'Back' is the only option
            len = dfo.miniMenu.entries.length;
            if (len === 0) {
                console.log("Setting sel to 'X' by default because miniMenu has no entries");
                sel = 'X';
            } else if (sel === '') {
                // user hit ENTER ambiguously
                return;
            }

            // clone the object
            newObj = $.extend({}, obj);

            // replace the writable properties with the values from the form
            if (dfo.entriesWrite) {
                wlen = dfo.entriesWrite.length;
                for (i = 0; i < wlen; i += 1) {
                    entry = dfo.entriesWrite[i];
                    newObj[entry.prop] = $('#' + entry.name).val();
                }
                //console.log("Modified object based on form contents", newObj);
            }

            console.log("sel === " + sel + " and len === " + len);
            if (sel >= 0 && sel <= len) {
                //console.log("sel " + sel + " is within range");
                // we can only select the item if we have sufficient priv level
                selection = target.pull(dfo.miniMenu.entries[sel]);
                if (lib.privCheck(selection.aclProfile)) {
                    //console.log('Selection ' + sel + ' passed priv check');
                    item = selection;
                }
            } else if (sel === 'X' || sel === 'x') {
                //console.log('dfo.menu.back:', dfo.miniMenu.back);
                item = target.pull(dfo.miniMenu.back[1]);
            } else {
                console.log('Selection is ' + sel + ' (invalid) -- doing nothing');
            }

            if (item !== undefined) {
                //console.log("Selected " + dfn + " menu item: " + item.name);
                // WARNING about the next line: we send the new object
                // to the selected target's start method, but it will only
                // be available to the start method if the target is a
                // daction. If the target is a dform or a dbrowser, nothing
                // will be passed in because the start methods of these
                // target types do not have an argument. If you want to use
                // this new value in a form or browser target (??), you will need
                // to define a daction that sets up that target's hook to
                // provide this value to the form/browser, and _then_ calls
                // the intended target.
                item.start(newObj);
            }
        },
        dformListen = function (dfn, obj) {
            console.log("Listening in form " + dfn);
            var dfo = target.pull(dfn);
            $('#' + dfn).submit( suppressSubmitEvent );
            $('input[name="sel"]').val('');
            if ($('input[name="entry0"]').length) {
                $('input[name="entry0"]').focus();
            } else {
                $('input[name="sel"]').focus();
            }
            $('#submitButton').on("click", function (event) {
                event.preventDefault;
                console.log("Submitting form " + dfn);
                dformSubmit(dfn, obj);
            });
            $('#' + dfn).on("keypress", mmKeyListener);
        },

        //
        // storage for dbrowser state
        //
        dbrowserState = {
            "obj": null,  // the dbrowser object itself
            "set": null,  // the set we are browsing
            "pos": null   // the current position within that setn
        },

        //
        // dbrowser handlers
        //
        dbrowserSubmit = function () {
            var dbo = dbrowserState.obj,
                set = dbrowserState.set,
                pos = dbrowserState.pos;

            console.log("Submitting browser, whatever that means: ", set[pos]);
            lib.clearResult();

            var sel = $('input[name="sel"]').val(),
                len,
                i,
                selection,
                entry,
                item;

            // if miniMenu has zero or one entries, 'Back' is the only option
            len = dbo.miniMenu.entries.length;
            if (len === 0) {
                console.log("Setting sel to 'X' by default because miniMenu has no entries");
                sel = 'X';
            } else if (sel === '') {
                // user hit ENTER ambiguously
                return;
            }

            console.log("sel === " + sel + " and len === " + len);
            if (sel >= 0 && sel <= len) {
                //console.log("sel " + sel + " is within range");
                // we can only select the item if we have sufficient priv level
                selection = target.pull(dbo.miniMenu.entries[sel]);
                if (lib.privCheck(selection.aclProfile)) {
                    //console.log('Selection ' + sel + ' passed priv check');
                    item = selection;
                }
            } else if (sel === 'X' || sel === 'x') {
                //console.log('dfo.menu.back:', dfo.miniMenu.back);
                item = target.pull(dbo.miniMenu.back[1]);
            } else {
                console.log('Selection is ' + sel + ' (invalid) -- doing nothing');
            }

            if (item !== undefined) {
                console.log("Selected " + dbo.name + " miniMenu item " + item.name);
                // WARNING about the next line: we send the current object
                // to the selected target's start method, but it will only
                // be available to the start method if the target is a
                // daction. If the target is a dform or a dbrowser, nothing
                // will be passed in because the start methods of these
                // target types do not have an argument. Use the 'hook'
                // property, instead.
                item.start(set[pos]);
            }
        },
        dbrowserKeyListener = function () {
            var set = dbrowserState.set,
                pos = dbrowserState.pos;

            return function (evt) {

                lib.logKeyPress(evt);

                // since the dbrowser has (may have) a navigation menu, we
                // check first for those keys before moving to miniMenu handler
                if (evt.keyCode === 37) { // <-
                    if (evt.ctrlKey) {
                        console.log('Listener detected CTRL-\u2190 keypress');
                        if ($("#navJumpToBegin").length) {
                            dbrowserState.pos = 0;
                            dbrowserListen();
                        }
                    } else {
                        console.log('Listener detected \u2190 keypress');
                        if ($("#navBack").length) {
                            dbrowserState.pos -= 1;
                            dbrowserListen();
                        }
                    }
                } else if (evt.keyCode === 39) { // ->
                    if (evt.ctrlKey) {
                        console.log('Listener detected CTRL-\u2192 keypress');
                        if ($("#navJumpToEnd").length) {
                            dbrowserState.pos = set.length - 1;
                            dbrowserListen();
                        }
                    } else {
                        console.log('Listener detected \u2192 keypress');
                        if ($("#navForward").length) {
                            dbrowserState.pos += 1;
                            dbrowserListen();
                        }
                    }
                } else {
                    mmKeyListener(evt);
                }
            };
        },
        dbrowserListen = function () {
            var dbo = dbrowserState.obj,
                set = dbrowserState.set,
                pos = dbrowserState.pos;

            console.log("Listening in browser " + dbo.name);
            $('#mainarea').html(dbo.source(set, pos));
            lib.holdObject(set[pos]); // hold object so hooks can get it
            $('#result').html("Displaying no. " + (pos + 1) + " of " + set.length + " objects in result set");
            $('#' + dbo.name).submit( suppressSubmitEvent );
            $('input[name="sel"]').val('').focus();
            $('#submitButton').on("click", function (event) {
                event.preventDefault;
                console.log("Submitting browser " + dbo.name);
                dbrowserSubmit();
            });
            $('#' + dbo.name).on("keypress", dbrowserKeyListener());
        },

        //
        // dnotice handlers
        //
        dnoticeSubmit = function (dno) {
            target.pull(dno.back).start();
        },
        dnoticeListen = function (dno) {
            $('#submitButton').on("click", function (event) {
                event.preventDefault;
                console.log("Submitting form " + dno.name);
                dnoticeSubmit(dno);
            });
            $('#' + dno.name).on("keypress", mmKeyListener);
        };

    return {
        dmenu: function (dmn) {
            // dmn is dmenu name
            // dmo is dmenu object
            var dmo = target.pull(dmn);
            return function () {
                console.log('Entering start.dmenu with argument: ' + dmn);
                lib.clearResult();
                $('#mainarea').html(dmo.source);
                $('input[name="sel"]').val('').focus();
                $('#' + dmn).submit(dmenuSubmitKey(dmn));
                $('input[name="sel"]').keydown(dmenuKeyListener(dmn));
            };
        },
        dform: function (dfn) {
            var dfo = target.pull(dfn);
            return function () {
                console.log('Entering start.dform with argument: ' + dfn);
                lib.clearResult();
                var obj = dfo.hook();
                console.log('The object we are working with is:', obj);
                $('#mainarea').html(dfo.source(obj));
                dformListen(dfn, obj);
            };
        },
        dbrowser: function (dbn) {
            if (dbn) {
                // when called with dbn (dbrowser name) argument, we assume
                // that we are being called from the second stage of dbrowser
                // initialization (i.e., one-time event) -- generate and
                // return the start function for this dbrowser
                return function () {
                    lib.clearResult();
                    console.log('Starting new ' + dbn + ' dbrowser');
                    // (re)initialize dbrowser state
                    dbrowserState.obj = target.pull(dbn);
                    dbrowserState.set = dbrowserState.obj.hook();
                    dbrowserState.pos = 0;
                    // start browsing
                    dbrowserListen();
                };
            } else {
                // when called _without_ an argument, we assume that there
                // is an existing browser state to return to
                console.log('Returning to previous ' + dbrowserState.obj.name + ' dbrowser state');
                dbrowserListen();
            }
        },
        dnotice: function (dnn) {
            var dno = target.pull(dnn);
            return function () {
                console.log("Entering start.dnotice with argument: " + dnn);
                lib.clearResult();
                $('#mainarea').html(dno.source()); // write HTML to screen
                dnoticeHooks[dnn]();               // AJAX call and fill #noticeText
                $('input[name="sel"]').focus();
                dnoticeListen(dno);
            };
        }
    }
});
<commit_msg>dbrowser: fire itemHook in listener function<commit_after>// *************************************************************************
// Copyright (c) 2014-2016, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// start.js
//
// 'start' (i.e. HTML display and event handling) methods for menus, forms,
// browsers
//
define ([
    'jquery',
    'lib',
    'target',
    'app/dnotice-hooks'
], function (
    $,
    lib,
    target,
    dnoticeHooks
) {
    var
        //
        // generalized handlers
        //

        // used to suppress submit events when we don't need them
        suppressSubmitEvent = function (event) {
            event.preventDefault();
            console.log("Suppressed submit event");
        },

        //
        // dmenu handlers
        //
        dmenuSubmit = function (dmn) {
            // dmn is dmenu name
            // dmo is dmenu object
            var dmo = target.pull(dmn),
                sel = $('input[name="sel"]').val(),
                len = dmo.entries.length,
                entry,
                selection;

            if ($.isNumeric(sel) && sel >= 0 && sel <= len) {
                // we can only select the entry if we have sufficient priv level
                selection = target.pull(dmo.entries[sel]);
                if (lib.privCheck(selection.aclProfile)) {
                    //console.log('Selection ' + sel + ' passed priv check');
                    entry = selection;
                }
            } else if (sel === 'X' || sel === 'x') {
                entry = target.pull(dmo.back);
            } else if (sel === '') {
                // user hit 'enter'
                return;
            }
            if (entry !== undefined) {
                //console.log("Selected " + dmn + " menu entry: " + entry.name);
                console.log("About to call the 'start' method of ", entry);
                entry.start();
            }
        },
        dmenuSubmitKey = function (dmn) {
            return function (event) {
                event.preventDefault();
                //console.log("Submitting: " + dmn);
                dmenuSubmit(dmn);
            };
        },
        dmenuKeyListener = function (dmn) {
            return function (event) {
                lib.logKeyPress(event);
                if (event.keyCode === 13) {
                    dmenuSubmitKey(dmn);
                } else if (event.keyCode === 9) {
                    event.preventDefault();
                }
            };
        },

        //
        // miniMenu handlers
        //
        // mmKeyListener - generalized keypress handler for miniMenus
        // (see below for usage)
        mmKeyListener = function (evt) {

            var len = $("input:text").length,
                n = $("input:text").index($(document.activeElement));

            lib.logKeyPress(evt);

            if (evt.keyCode === 13) {
                console.log('MiniMenu listener detected <ENTER> keypress');
                console.log("This form has elements 0 through " + (len - 1));
                console.log("The current element is no. " + n);
                evt.preventDefault();
                if ( n === len - 1 ) {
                    console.log("Triggering submit button click");
                    $('#submitButton').click();
                } else {
                    $("input:text")[n + 1].focus();
                }

            } else if (evt.keyCode === 9) {
                var elnam = $(document.activeElement).attr("name");
                if (
                        (elnam === 'entry0' && evt.shiftKey) ||
                        (elnam === 'sel' && len === 1) ||
                        (elnam === 'sel' && !evt.shiftKey)
                   ) {
                    evt.preventDefault();
                }
            }

        },

        //
        // dform handlers
        //
        dformSubmit = function (dfn, obj) {
            // dfn is dform name
            // dfo is dform object
            var dfo = target.pull(dfn);

            //console.log("Entering MFILE.dformSubmit with argument " + dfn);
            lib.clearResult();

            var sel = $('input[name="sel"]').val(),
                len,
                i,
                newObj,
                selection,
                entry,
                item,
                wlen;

            // if miniMenu has zero or one entries, 'Back' is the only option
            len = dfo.miniMenu.entries.length;
            if (len === 0) {
                console.log("Setting sel to 'X' by default because miniMenu has no entries");
                sel = 'X';
            } else if (sel === '') {
                // user hit ENTER ambiguously
                return;
            }

            // clone the object
            newObj = $.extend({}, obj);

            // replace the writable properties with the values from the form
            if (dfo.entriesWrite) {
                wlen = dfo.entriesWrite.length;
                for (i = 0; i < wlen; i += 1) {
                    entry = dfo.entriesWrite[i];
                    newObj[entry.prop] = $('#' + entry.name).val();
                }
                //console.log("Modified object based on form contents", newObj);
            }

            console.log("sel === " + sel + " and len === " + len);
            if (sel >= 0 && sel <= len) {
                //console.log("sel " + sel + " is within range");
                // we can only select the item if we have sufficient priv level
                selection = target.pull(dfo.miniMenu.entries[sel]);
                if (lib.privCheck(selection.aclProfile)) {
                    //console.log('Selection ' + sel + ' passed priv check');
                    item = selection;
                }
            } else if (sel === 'X' || sel === 'x') {
                //console.log('dfo.menu.back:', dfo.miniMenu.back);
                item = target.pull(dfo.miniMenu.back[1]);
            } else {
                console.log('Selection is ' + sel + ' (invalid) -- doing nothing');
            }

            if (item !== undefined) {
                //console.log("Selected " + dfn + " menu item: " + item.name);
                // WARNING about the next line: we send the new object
                // to the selected target's start method, but it will only
                // be available to the start method if the target is a
                // daction. If the target is a dform or a dbrowser, nothing
                // will be passed in because the start methods of these
                // target types do not have an argument. If you want to use
                // this new value in a form or browser target (??), you will need
                // to define a daction that sets up that target's hook to
                // provide this value to the form/browser, and _then_ calls
                // the intended target.
                item.start(newObj);
            }
        },
        dformListen = function (dfn, obj) {
            console.log("Listening in form " + dfn);
            var dfo = target.pull(dfn);
            $('#' + dfn).submit( suppressSubmitEvent );
            $('input[name="sel"]').val('');
            if ($('input[name="entry0"]').length) {
                $('input[name="entry0"]').focus();
            } else {
                $('input[name="sel"]').focus();
            }
            $('#submitButton').on("click", function (event) {
                event.preventDefault;
                console.log("Submitting form " + dfn);
                dformSubmit(dfn, obj);
            });
            $('#' + dfn).on("keypress", mmKeyListener);
        },

        //
        // storage for dbrowser state
        //
        dbrowserState = {
            "obj": null,  // the dbrowser object itself
            "set": null,  // the set we are browsing
            "pos": null   // the current position within that set
        },

        //
        // dbrowser handlers
        //
        dbrowserSubmit = function () {
            var dbo = dbrowserState.obj,
                set = dbrowserState.set,
                pos = dbrowserState.pos;

            console.log("Submitting browser, whatever that means: ", set[pos]);
            lib.clearResult();

            var sel = $('input[name="sel"]').val(),
                len,
                i,
                selection,
                entry,
                item;

            // if miniMenu has zero or one entries, 'Back' is the only option
            len = dbo.miniMenu.entries.length;
            if (len === 0) {
                console.log("Setting sel to 'X' by default because miniMenu has no entries");
                sel = 'X';
            } else if (sel === '') {
                // user hit ENTER ambiguously
                return;
            }

            console.log("sel === " + sel + " and len === " + len);
            if (sel >= 0 && sel <= len) {
                //console.log("sel " + sel + " is within range");
                // we can only select the item if we have sufficient priv level
                selection = target.pull(dbo.miniMenu.entries[sel]);
                if (lib.privCheck(selection.aclProfile)) {
                    //console.log('Selection ' + sel + ' passed priv check');
                    item = selection;
                }
            } else if (sel === 'X' || sel === 'x') {
                //console.log('dfo.menu.back:', dfo.miniMenu.back);
                item = target.pull(dbo.miniMenu.back[1]);
            } else {
                console.log('Selection is ' + sel + ' (invalid) -- doing nothing');
            }

            if (item !== undefined) {
                console.log("Selected " + dbo.name + " miniMenu item " + item.name);
                // WARNING about the next line: we send the current object
                // to the selected target's start method, but it will only
                // be available to the start method if the target is a
                // daction. If the target is a dform or a dbrowser, nothing
                // will be passed in because the start methods of these
                // target types do not have an argument. Use the 'hook'
                // property, instead.
                item.start(set[pos]);
            }
        },
        dbrowserKeyListener = function () {
            var set = dbrowserState.set,
                pos = dbrowserState.pos;

            return function (evt) {

                lib.logKeyPress(evt);

                // since the dbrowser has (may have) a navigation menu, we
                // check first for those keys before moving to miniMenu handler
                if (evt.keyCode === 37) { // <-
                    if (evt.ctrlKey) {
                        console.log('Listener detected CTRL-\u2190 keypress');
                        if ($("#navJumpToBegin").length) {
                            dbrowserState.pos = 0;
                            dbrowserListen();
                        }
                    } else {
                        console.log('Listener detected \u2190 keypress');
                        if ($("#navBack").length) {
                            dbrowserState.pos -= 1;
                            dbrowserListen();
                        }
                    }
                } else if (evt.keyCode === 39) { // ->
                    if (evt.ctrlKey) {
                        console.log('Listener detected CTRL-\u2192 keypress');
                        if ($("#navJumpToEnd").length) {
                            dbrowserState.pos = set.length - 1;
                            dbrowserListen();
                        }
                    } else {
                        console.log('Listener detected \u2192 keypress');
                        if ($("#navForward").length) {
                            dbrowserState.pos += 1;
                            dbrowserListen();
                        }
                    }
                } else {
                    mmKeyListener(evt);
                }
            };
        },
        dbrowserListen = function () {
            var dbo = dbrowserState.obj,
                set = dbrowserState.set,
                pos = dbrowserState.pos;

            console.log("Listening in browser " + dbo.name);
            dbo.itemHook(set[pos]);
            $('#mainarea').html(dbo.source(set, pos));
            lib.holdObject(set[pos]); // hold object so hooks can get it
            $('#result').html("Displaying no. " + (pos + 1) + " of " + set.length + " objects in result set");
            $('#' + dbo.name).submit( suppressSubmitEvent );
            $('input[name="sel"]').val('').focus();
            $('#submitButton').on("click", function (event) {
                event.preventDefault;
                console.log("Submitting browser " + dbo.name);
                dbrowserSubmit();
            });
            $('#' + dbo.name).on("keypress", dbrowserKeyListener());
        },

        //
        // dnotice handlers
        //
        dnoticeSubmit = function (dno) {
            target.pull(dno.back).start();
        },
        dnoticeListen = function (dno) {
            $('#submitButton').on("click", function (event) {
                event.preventDefault;
                console.log("Submitting form " + dno.name);
                dnoticeSubmit(dno);
            });
            $('#' + dno.name).on("keypress", mmKeyListener);
        };

    return {
        dmenu: function (dmn) {
            // dmn is dmenu name
            // dmo is dmenu object
            var dmo = target.pull(dmn);
            return function () {
                console.log('Entering start.dmenu with argument: ' + dmn);
                lib.clearResult();
                $('#mainarea').html(dmo.source);
                $('input[name="sel"]').val('').focus();
                $('#' + dmn).submit(dmenuSubmitKey(dmn));
                $('input[name="sel"]').keydown(dmenuKeyListener(dmn));
            };
        },
        dform: function (dfn) {
            var dfo = target.pull(dfn);
            return function () {
                console.log('Entering start.dform with argument: ' + dfn);
                lib.clearResult();
                var obj = dfo.hook();
                console.log('The object we are working with is:', obj);
                $('#mainarea').html(dfo.source(obj));
                dformListen(dfn, obj);
            };
        },
        dbrowser: function (dbn) {
            if (dbn) {
                // when called with dbn (dbrowser name) argument, we assume
                // that we are being called from the second stage of dbrowser
                // initialization (i.e., one-time event) -- generate and
                // return the start function for this dbrowser
                return function () {
                    lib.clearResult();
                    console.log('Starting new ' + dbn + ' dbrowser');
                    // (re)initialize dbrowser state
                    dbrowserState.obj = target.pull(dbn);
                    dbrowserState.set = dbrowserState.obj.hook();
                    dbrowserState.pos = 0;
                    // start browsing
                    dbrowserListen();
                };
            } else {
                // when called _without_ an argument, we assume that there
                // is an existing browser state to return to
                console.log('Returning to previous ' + dbrowserState.obj.name + ' dbrowser state');
                dbrowserListen();
            }
        },
        dnotice: function (dnn) {
            var dno = target.pull(dnn);
            return function () {
                console.log("Entering start.dnotice with argument: " + dnn);
                lib.clearResult();
                $('#mainarea').html(dno.source()); // write HTML to screen
                dnoticeHooks[dnn]();               // AJAX call and fill #noticeText
                $('input[name="sel"]').focus();
                dnoticeListen(dno);
            };
        }
    }
});
<|endoftext|>


<commit_before>module.exports = function(grunt) {
	var Ftp = require("ftp");
	var fs = require("fs");
	var http = require("http");
	var _ = grunt.utils._;
	// if DEBUG is true no actual release will be performed
	// to enable debug mode execute `grunt release -d`
	var DEBUG = !!grunt.option("debug");

	var FtpUploader = function(config) {
		var self = this;
		this.config = config;
		this.client = new Ftp({
			host: config.auth.host,
			port: config.auth.port,
			connTimeout: 600000 // 10 minutes
		});
		this.client.on("connect", function() { self.start(); });
		this.client.connect();
	};

	FtpUploader.prototype.start = function() {
		var self = this;
		this.client.auth(this.config.auth.user, this.config.auth.password, function(err) {
			if (err) {
				self._error(err);
				return;
			}

			self.queue = [];
			_.each(self.config.upload, function(upload) {
				var dir = "";
				// substring(1) because variable starts from slash symbol
				_.each(upload.to.substring(1).split("/"), function(name) {
					dir += "/" + name;
					self.enqueue("makeDir", dir);
				});
				self.enqueueDir("", self.config.rootDir + upload.from, upload.to);
			});
			!DEBUG && grunt.verbose.writeln("Starting upload");
			self.currentStep = 1;
			self.totalSteps = self.queue.length;
			self._next();
		});
	};

	FtpUploader.prototype.enqueueDir = function(dir, fromPrefix, toPrefix) {
		var self = this;
		var names = fs.readdirSync(fromPrefix + dir);
		_.each(names, function(name) {
			// skip hidden files
			if (/^\./.test(name)) {
				return;
			}

			name = dir + "/" + name;
			var stats = fs.statSync(fromPrefix + name);
			if (stats.isFile()) {
				self.enqueue("uploadFile", name, fromPrefix, toPrefix);
			} else if (stats.isDirectory()) {
				self.enqueue("makeDir", toPrefix + name);
				self.enqueueDir(name, fromPrefix, toPrefix);
			}
		});
	};

	FtpUploader.prototype.enqueue = function() {
		var args = _.toArray(arguments);
		var name = args.shift();
		this.queue.push(function() {
			this["_" + name].apply(this, args);
		});
	};

	FtpUploader.prototype._uploadFile = function(name, fromPrefix, toPrefix) {
		var self = this;
		this._log(toPrefix + name);
		if (DEBUG) {
			this._next();
			return;
		}
		this.client.put(fs.createReadStream(fromPrefix + name), toPrefix + name, function(err) {
			if (err) {
				self._error(err);
			} else {
				self._next();
			}
		});
	};

	FtpUploader.prototype._makeDir = function(name) {
		var self = this;
		this._log(name);
		if (DEBUG) {
			this._next();
			return;
		}
		this.client.mkdir(name, function() {
			self._next();
		});
	};

	FtpUploader.prototype._next = function() {
		if (!this.queue.length) {
			this.config.complete();
			return;
		}
		this.queue.shift().call(this);
	};

	FtpUploader.prototype._log = function(name) {
		grunt.log.writeln(this.currentStep++ + "/" + this.totalSteps + ": " + name);
	};

	FtpUploader.prototype._error = function(text) {
		grunt.log.error(text);
		this.client.end();
		this.config.complete(false);
	};

	grunt.registerInitTask("release", "Release", function(target, subtarget) {
		var config = grunt.config("local");
		if (!config || !config.release) {
			grunt.fail.fatal("No configuration for this task.");
		}
		DEBUG && delete config.domain;
		if (config.domain) {
			grunt.fail.fatal("Can't release files from dev environment");
		}
		// TODO: check if we have modified files, we must not release this
		// TODO: execute `grunt` before releasing anything
		// TODO? check versions before running particular task
		if (!target) {
			grunt.task.run([
				"check:versions",
				"release:sdk:latest",
				"patch:loader:stable",
				"release:sdk:stable",
				"release:apps"
			]);
			return;
		}
		console.time(target.yellow);
		// Tell grunt this task is asynchronous.
		var _complete = this.async();
		var done = function() {
			console.timeEnd(target.yellow);
			_complete();
		};
		var version = grunt.config("pkg.version");
		var majorVersion = version.split(".")[0];
		var params;
		switch (target) {
			case "sdk":
				if (!subtarget) {
					subtarget = "latest";
				}
				_.each(config.release[target][subtarget].upload, function(upload) {
					upload.from = upload.from
						.replace("{majorVersion}", majorVersion)
						.replace("{version}", version);
					upload.to = upload.to
						.replace("{majorVersion}", majorVersion)
						.replace("{version}", version);
				});
				params = config.release[target][subtarget];
				// no break statement!
			case "apps":
				params = params || config.release[target];
				new FtpUploader(_.extend({
					"complete": done,
					"rootDir": grunt.config("dirs.dest") + "/"
				}, params));
				grunt.task.run("release:purge:" + target);
				break;
			case "purge":
				// TODO: purge all paths in one request
				grunt.helper("cdn_purge", [subtarget], config.release, done);
				break;
		}
	});

	grunt.registerHelper("cdn_purge", function(paths, config, done) {
		if (DEBUG) {
			console.log(arguments[0]);
			done();
			return;
		}
		var xml =
			'<?xml version="1.0" encoding="utf-8"?>' +
			'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +
				'<soap:Header>' +
					'<AuthHeader xmlns="http://www.llnw.com/Purge">' +
						'<Username>' + config.soap.user + '</Username>' +
						'<Password>' + config.soap.password + '</Password>' +
					'</AuthHeader>' +
				'</soap:Header>' +
				'<soap:Body>' +
					'<CreatePurgeRequest xmlns="http://www.llnw.com/Purge">' +
					'<request>' +
						'<EmailType>detail</EmailType>' +
						'<EmailSubject>[JS-SDK] [Limelight] Code pushed to CDN</EmailSubject>' +
						'<EmailTo>' + config.email + '</EmailTo>' +
						'<EmailCc>' + (config.emailCC || "") + '</EmailCc>' +
						'<EmailBcc></EmailBcc>' +
						'<Entries>' +
							paths.map(function(path) {
								return '<PurgeRequestEntry>' +
									'<Shortname>' + config.soap.target.name + '</Shortname>' +
									'<Url>' + config.soap.target.url.replace("{path}", path) + '</Url>' +
									'<Regex>true</Regex>' +
								'</PurgeRequestEntry>';
							}).join("") +
						'</Entries>' +
					'</request>' +
					'</CreatePurgeRequest>' +
				'</soap:Body>' +
			'</soap:Envelope>';
		var req = http.request({
			"host": config.soap.host,
			"path": config.soap.path,
			"method": "POST",
			"headers": {
				"Content-Type": "text/xml"
			}
		}, function(response) {
			if (response.statusCode == 200) {
				grunt.log.ok();
				done();
			} else if (response.statusCode == 500) {
				response.on("data", function (text) {
					grunt.log.writeln(text);
					grunt.fail.fatal("Can't purge");
				});
			} else {
				grunt.fail.fatal("Can't purge: " + response.statusCode + " error");
			}
		});
		req.on("error", function(e) {
			grunt.fail.fatal("Problem with request: " + e.message);
		});
		req.write(xml);
		req.end();
	});
};
<commit_msg>Let's run full default task before release<commit_after>module.exports = function(grunt) {
	var Ftp = require("ftp");
	var fs = require("fs");
	var http = require("http");
	var _ = grunt.utils._;
	// if DEBUG is true no actual release will be performed
	// to enable debug mode execute `grunt release -d`
	var DEBUG = !!grunt.option("debug");

	var FtpUploader = function(config) {
		var self = this;
		this.config = config;
		this.client = new Ftp({
			host: config.auth.host,
			port: config.auth.port,
			connTimeout: 600000 // 10 minutes
		});
		this.client.on("connect", function() { self.start(); });
		this.client.connect();
	};

	FtpUploader.prototype.start = function() {
		var self = this;
		this.client.auth(this.config.auth.user, this.config.auth.password, function(err) {
			if (err) {
				self._error(err);
				return;
			}

			self.queue = [];
			_.each(self.config.upload, function(upload) {
				var dir = "";
				// substring(1) because variable starts from slash symbol
				_.each(upload.to.substring(1).split("/"), function(name) {
					dir += "/" + name;
					self.enqueue("makeDir", dir);
				});
				self.enqueueDir("", self.config.rootDir + upload.from, upload.to);
			});
			!DEBUG && grunt.verbose.writeln("Starting upload");
			self.currentStep = 1;
			self.totalSteps = self.queue.length;
			self._next();
		});
	};

	FtpUploader.prototype.enqueueDir = function(dir, fromPrefix, toPrefix) {
		var self = this;
		var names = fs.readdirSync(fromPrefix + dir);
		_.each(names, function(name) {
			// skip hidden files
			if (/^\./.test(name)) {
				return;
			}

			name = dir + "/" + name;
			var stats = fs.statSync(fromPrefix + name);
			if (stats.isFile()) {
				self.enqueue("uploadFile", name, fromPrefix, toPrefix);
			} else if (stats.isDirectory()) {
				self.enqueue("makeDir", toPrefix + name);
				self.enqueueDir(name, fromPrefix, toPrefix);
			}
		});
	};

	FtpUploader.prototype.enqueue = function() {
		var args = _.toArray(arguments);
		var name = args.shift();
		this.queue.push(function() {
			this["_" + name].apply(this, args);
		});
	};

	FtpUploader.prototype._uploadFile = function(name, fromPrefix, toPrefix) {
		var self = this;
		this._log(toPrefix + name);
		if (DEBUG) {
			this._next();
			return;
		}
		this.client.put(fs.createReadStream(fromPrefix + name), toPrefix + name, function(err) {
			if (err) {
				self._error(err);
			} else {
				self._next();
			}
		});
	};

	FtpUploader.prototype._makeDir = function(name) {
		var self = this;
		this._log(name);
		if (DEBUG) {
			this._next();
			return;
		}
		this.client.mkdir(name, function() {
			self._next();
		});
	};

	FtpUploader.prototype._next = function() {
		if (!this.queue.length) {
			this.config.complete();
			return;
		}
		this.queue.shift().call(this);
	};

	FtpUploader.prototype._log = function(name) {
		grunt.log.writeln(this.currentStep++ + "/" + this.totalSteps + ": " + name);
	};

	FtpUploader.prototype._error = function(text) {
		grunt.log.error(text);
		this.client.end();
		this.config.complete(false);
	};

	grunt.registerInitTask("release", "Release", function(target, subtarget) {
		var config = grunt.config("local");
		if (!config || !config.release) {
			grunt.fail.fatal("No configuration for this task.");
		}
		DEBUG && delete config.domain;
		if (config.domain) {
			grunt.fail.fatal("Can't release files from dev environment");
		}
		// TODO: check if we have modified files, we must not release this
		if (!target) {
			grunt.task.run([
				"default",
				"release:sdk:latest",
				"patch:loader:stable",
				"release:sdk:stable",
				"release:apps"
			]);
			return;
		}
		console.time(target.yellow);
		// Tell grunt this task is asynchronous.
		var _complete = this.async();
		var done = function() {
			console.timeEnd(target.yellow);
			_complete();
		};
		var version = grunt.config("pkg.version");
		var majorVersion = version.split(".")[0];
		var params;
		switch (target) {
			case "sdk":
				if (!subtarget) {
					subtarget = "latest";
				}
				_.each(config.release[target][subtarget].upload, function(upload) {
					upload.from = upload.from
						.replace("{majorVersion}", majorVersion)
						.replace("{version}", version);
					upload.to = upload.to
						.replace("{majorVersion}", majorVersion)
						.replace("{version}", version);
				});
				params = config.release[target][subtarget];
				// no break statement!
			case "apps":
				params = params || config.release[target];
				new FtpUploader(_.extend({
					"complete": done,
					"rootDir": grunt.config("dirs.dest") + "/"
				}, params));
				grunt.task.run("release:purge:" + target);
				break;
			case "purge":
				// TODO: purge all paths in one request
				grunt.helper("cdn_purge", [subtarget], config.release, done);
				break;
		}
	});

	grunt.registerHelper("cdn_purge", function(paths, config, done) {
		if (DEBUG) {
			console.log(arguments[0]);
			done();
			return;
		}
		var xml =
			'<?xml version="1.0" encoding="utf-8"?>' +
			'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +
				'<soap:Header>' +
					'<AuthHeader xmlns="http://www.llnw.com/Purge">' +
						'<Username>' + config.soap.user + '</Username>' +
						'<Password>' + config.soap.password + '</Password>' +
					'</AuthHeader>' +
				'</soap:Header>' +
				'<soap:Body>' +
					'<CreatePurgeRequest xmlns="http://www.llnw.com/Purge">' +
					'<request>' +
						'<EmailType>detail</EmailType>' +
						'<EmailSubject>[JS-SDK] [Limelight] Code pushed to CDN</EmailSubject>' +
						'<EmailTo>' + config.email + '</EmailTo>' +
						'<EmailCc>' + (config.emailCC || "") + '</EmailCc>' +
						'<EmailBcc></EmailBcc>' +
						'<Entries>' +
							paths.map(function(path) {
								return '<PurgeRequestEntry>' +
									'<Shortname>' + config.soap.target.name + '</Shortname>' +
									'<Url>' + config.soap.target.url.replace("{path}", path) + '</Url>' +
									'<Regex>true</Regex>' +
								'</PurgeRequestEntry>';
							}).join("") +
						'</Entries>' +
					'</request>' +
					'</CreatePurgeRequest>' +
				'</soap:Body>' +
			'</soap:Envelope>';
		var req = http.request({
			"host": config.soap.host,
			"path": config.soap.path,
			"method": "POST",
			"headers": {
				"Content-Type": "text/xml"
			}
		}, function(response) {
			if (response.statusCode == 200) {
				grunt.log.ok();
				done();
			} else if (response.statusCode == 500) {
				response.on("data", function (text) {
					grunt.log.writeln(text);
					grunt.fail.fatal("Can't purge");
				});
			} else {
				grunt.fail.fatal("Can't purge: " + response.statusCode + " error");
			}
		});
		req.on("error", function(e) {
			grunt.fail.fatal("Problem with request: " + e.message);
		});
		req.write(xml);
		req.end();
	});
};
<|endoftext|>


<commit_before>/**
 * Callbacks to be passed to ajaxFormSubmit.js 'init' method
 **/
require('jquery');

var ajaxCallbacks = {
  clientAccessResponse: {
    callbacks: {
      success: function (response, data, helpers, container, type) {
        helpers.insertResponseHtml(helpers, type, data, $(container), response);
      },

      error: function (response, data, helpers, container) {
        if (response.readyState === 0) {
          document.location.href = "/account/sign-in?continue=" + encodeURIComponent(document.location.href);
        }
        else {
          helpers.insertResponseHtml(helpers, 'before', data, $(container + ' .form-field:has(>input[name][type="text"])'), response);
        }
      },

      always: function (response, data, helpers, container, type) {
        helpers.resetForms(helpers, type, data, container);
      }
    }
  },
  helpers: {
    insertResponseHtml: function (helpers, type, data, $target, response) {
      var html = !!response.responseText ? response.responseText : response,
        htmlText = helpers.utilities.cleanHtml(html),
        htmlNodes = $.parseHTML(htmlText),
        nodeCount = htmlNodes.length || 0,
        i = 0,
        $node, $errorTarget,
        isError = !!response.status || response.status === 500;

      if(!$.isFunction($.fn[type])) {
        $target.html('');
        type  = 'append';
      }

      if (helpers.utilities.isFullPageError(helpers, htmlText)) {
        helpers.insertFullPageErrorHtml($target, helpers, htmlText);
      }
      else {
        if (!isError) {
          $target.addClass('js-hidden');
        }

        $target.removeClass('error');
        $target.parent().find('.alert--success, .alert--failure').remove();

        for (; i < nodeCount; i++) {
          $node = $(htmlNodes[i]);

          if (isError) {
            $errorTarget = $target.find('>input[name="' + $node.attr('data-input-for') +  '"]');
            $errorTarget.closest('.form-field').addClass('error');
            $errorTarget.blur();
          }

          $.fn[type].apply($errorTarget || $target, [$node]);
        }

        if (data.indexOf('missingclient=true') > -1) {
          helpers.bindEvents($target, data);
        }
        else {
          $target.removeClass('js-hidden');
        }
      }
    },

    insertFullPageErrorHtml: function ($target, helpers, htmlText) {
      var $heading = helpers.utilities.getElementInnerHtml(htmlText, 'h1'),
          $button = $target.closest('*:has([data-ajax-submit="true"])').find('button[type="submit"], input[type="submit"]');

      $target.removeClass('error');
      $target.parent().find('.alert--success, .alert--failure').remove();

      $button.parent('.form-field').addClass('error');

      $('
' +
        '' + $heading.text() + '' +
      '
').insertBefore($button);
    },

    resetForms: function (helpers, type, data, target) {
      var emailValue = helpers.getEmailValue(target, data);

      $("input[type=submit]:disabled, button[type=submit]:disabled").prop('disabled', false);

      $('input[name="email"]').each(function (index, element) {
        $(element).val(emailValue);
      });
    },

    getEmailValue: function(container, data) {
      var reEmail = /&?email=([^&]+)/gi,
        emailMatch = (function(d, re) {
            var m = !!d ? d.match(re) : null;
            return (!!d && !!m) ? m[0] : [''];
          })(data, reEmail),
            emailValue = $(container).find('input[name="email"]').val();
      return emailValue || decodeURIComponent(emailMatch).replace(reEmail, '$1') || "";
    },

    bindEvents: function ($container) {
      $('#another-missing-client').one("click keydown", function (event) {
        event.preventDefault();

        var $this = $(event.target);

        $this.parent().find('.error').removeClass('error');
        $this.parent().find('.alert--success, .alert--failure').remove();
        $this.parent().find('input[name="payeref"]').prop('value', null);

        $this.remove();

        $container.removeClass('js-hidden');
      });
    },

    utilities: {
      getElementInnerHtml: function(html, nodeName) {
        var re = new RegExp("^(.*)<" + nodeName + "[^>]*>(.+?)<\/" + nodeName + ">(.*)$", "gi");
        return re.test(html) ? $($.parseHTML(html.replace(re, "$2"))) : '';
      },

      cleanHtml: function(htmlString) {
        return $.trim(htmlString).replace(/[\r\n\f\t]/g, '').replace(/>\s+</g, '><');
      },

      isFullPageError: function(helpers, html) {
        var heading;
        if (!!html && !!html.length) {
          heading = helpers.utilities.getElementInnerHtml(html, 'h1');
          return !!heading && heading.text() === 'Sorry, we’re experiencing technical difficulties';
        }
        return false;
      }
    }
  }
};

module.exports = ajaxCallbacks;
<commit_msg>[AOSS-731] Updated handling of session timed out to be in error callback<commit_after>/**
 * Callbacks to be passed to ajaxFormSubmit.js 'init' method
 **/
require('jquery');

var ajaxCallbacks = {
  clientAccessResponse: {
    callbacks: {
      success: function (response, data, helpers, container, type) {
        helpers.insertResponseHtml(helpers, type, data, $(container), response);
      },

      error: function (response, data, helpers, container) {

        // if session has timed out
        if(response.status === 401 &&
           response.responseJSON &&
           response.responseJSON.redirectUri) {

          // go to login page with specified redirect URI
          document.location.href = response.responseJSON.redirectUri;
        }
        else {
          helpers.insertResponseHtml(helpers, 'before', data, $(container + ' .form-field:has(>input[name][type="text"])'), response);
        }

      },

      always: function (response, data, helpers, container, type) {
        helpers.resetForms(helpers, type, data, container);
      }
    }
  },
  helpers: {
    insertResponseHtml: function (helpers, type, data, $target, response) {
      var html = !!response.responseText ? response.responseText : response,
        htmlText = helpers.utilities.cleanHtml(html),
        htmlNodes = $.parseHTML(htmlText),
        nodeCount = htmlNodes.length || 0,
        i = 0,
        $node, $errorTarget,
        isError = !!response.status || response.status === 500;

      if(!$.isFunction($.fn[type])) {
        $target.html('');
        type  = 'append';
      }

      if (helpers.utilities.isFullPageError(helpers, htmlText)) {
        helpers.insertFullPageErrorHtml($target, helpers, htmlText);
      }
      else {
        if (!isError) {
          $target.addClass('js-hidden');
        }

        $target.removeClass('error');
        $target.parent().find('.alert--success, .alert--failure').remove();

        for (; i < nodeCount; i++) {
          $node = $(htmlNodes[i]);

          if (isError) {
            $errorTarget = $target.find('>input[name="' + $node.attr('data-input-for') +  '"]');
            $errorTarget.closest('.form-field').addClass('error');
            $errorTarget.blur();
          }

          $.fn[type].apply($errorTarget || $target, [$node]);
        }

        if (data.indexOf('missingclient=true') > -1) {
          helpers.bindEvents($target, data);
        }
        else {
          $target.removeClass('js-hidden');
        }
      }
    },

    insertFullPageErrorHtml: function ($target, helpers, htmlText) {
      var $heading = helpers.utilities.getElementInnerHtml(htmlText, 'h1'),
          $button = $target.closest('*:has([data-ajax-submit="true"])').find('button[type="submit"], input[type="submit"]');

      $target.removeClass('error');
      $target.parent().find('.alert--success, .alert--failure').remove();

      $button.parent('.form-field').addClass('error');

      $('
' +
        '' + $heading.text() + '' +
      '
').insertBefore($button);
    },

    resetForms: function (helpers, type, data, target) {
      var emailValue = helpers.getEmailValue(target, data);

      $("input[type=submit]:disabled, button[type=submit]:disabled").prop('disabled', false);

      $('input[name="email"]').each(function (index, element) {
        $(element).val(emailValue);
      });
    },

    getEmailValue: function(container, data) {
      var reEmail = /&?email=([^&]+)/gi,
        emailMatch = (function(d, re) {
            var m = !!d ? d.match(re) : null;
            return (!!d && !!m) ? m[0] : [''];
          })(data, reEmail),
            emailValue = $(container).find('input[name="email"]').val();
      return emailValue || decodeURIComponent(emailMatch).replace(reEmail, '$1') || "";
    },

    bindEvents: function ($container) {
      $('#another-missing-client').one("click keydown", function (event) {
        event.preventDefault();

        var $this = $(event.target);

        $this.parent().find('.error').removeClass('error');
        $this.parent().find('.alert--success, .alert--failure').remove();
        $this.parent().find('input[name="payeref"]').prop('value', null);

        $this.remove();

        $container.removeClass('js-hidden');
      });
    },

    utilities: {
      getElementInnerHtml: function(html, nodeName) {
        var re = new RegExp("^(.*)<" + nodeName + "[^>]*>(.+?)<\/" + nodeName + ">(.*)$", "gi");
        return re.test(html) ? $($.parseHTML(html.replace(re, "$2"))) : '';
      },

      cleanHtml: function(htmlString) {
        return $.trim(htmlString).replace(/[\r\n\f\t]/g, '').replace(/>\s+</g, '><');
      },

      isFullPageError: function(helpers, html) {
        var heading;
        if (!!html && !!html.length) {
          heading = helpers.utilities.getElementInnerHtml(html, 'h1');
          return !!heading && heading.text() === 'Sorry, we’re experiencing technical difficulties';
        }
        return false;
      }
    }
  }
};

module.exports = ajaxCallbacks;
<|endoftext|>


<commit_before>/* @flow */
'use strict'

import Relay from 'react-relay'
import React from 'react'
import { Dimensions, View, StyleSheet } from 'react-native'

import Show from './show'

class LargeList extends React.Component {
  state: {
    width: number,
    height: number
  }

  constructor(props) {
    super(props)
    this.state = this.imageDimensions()
  }

  imageDimensions() {
    const window = Dimensions.get('window')
    const isPad = window.width > 700
    const imageWidth = (isPad ? ((window.width - 100) / 2) : (window.width - 40))
    const imageHeight = Math.floor(imageWidth / 1.6)
    return { width: imageWidth, height: imageHeight }
  }

  onLayout = (e) => {
    this.setState(this.imageDimensions())
  }

  render() {
    const showStyles = StyleSheet.create({
      container: {
        margin: 10,
        width: this.state.width,
      },
      image: {
        width: this.state.width,
        height: this.state.height,
        marginBottom: 5,
      },
    })

    return (
      <View style={styles.container} onLayout={this.onLayout}>
        { this.props.shows.map(show => this.renderShow(show, showStyles)) }
      </View>
    )
  }

  renderShow(show, showStyles) {
    return <Show show={show} styles={showStyles} key={show.__id} />
  }
}

const styles = StyleSheet.create({
  container: {
    justifyContent: 'flex-start',
    flexDirection: 'row',
    flexWrap: 'wrap',
    alignItems: 'flex-start',
    marginTop: 0,
    marginBottom: 0,
    marginLeft: -10,
    marginRight: -10,
  },
})

export default Relay.createContainer(LargeList,{
  fragments: {
    shows: () => Relay.QL`
      fragment on PartnerShow @relay(plural: true) {
        __id
        ${Show.getFragment('show')}
      }
    `,
  }
})
<commit_msg>[Artist] Fix for incorrect layout on-device in Artist Shows component<commit_after>/* @flow */
'use strict'

import Relay from 'react-relay'
import React from 'react'
import type { LayoutEvent } from '../../system/events'
import { Dimensions, View, StyleSheet } from 'react-native'

import Show from './show'

class LargeList extends React.Component {
  state: {
    width: number,
    height: number
  }

  constructor(props) {
    super(props)
    this.state = {
      width: 1,
      height: 1,
    }
  }

  imageDimensions(layout) {
    const width = layout.width
    const isPad = layout.width > 700
    const imageWidth = (isPad ? ((layout.width - 40) / 2) : (layout.width - 40))
    const imageHeight = Math.floor(imageWidth / 1.6)
    return { width: imageWidth, height: imageHeight }
  }

  onLayout = (event: LayoutEvent) => {
    const layout = event.nativeEvent.layout
    console.log(layout)
    this.setState(this.imageDimensions(layout))
  }

  render() {
    const showStyles = StyleSheet.create({
      container: {
        margin: 10,
        width: this.state.width,
      },
      image: {
        width: this.state.width,
        height: this.state.height,
        marginBottom: 5,
      },
    })

    return (
      <View style={styles.container} onLayout={this.onLayout}>
        { this.props.shows.map(show => this.renderShow(show, showStyles)) }
      </View>
    )
  }

  renderShow(show, showStyles) {
    return <Show show={show} styles={showStyles} key={show.__id} />
  }
}

const styles = StyleSheet.create({
  container: {
    justifyContent: 'flex-start',
    flexDirection: 'row',
    flexWrap: 'wrap',
    alignItems: 'flex-start',
    marginTop: 0,
    marginBottom: 0,
    marginLeft: -10,
    marginRight: -10,
  },
})

export default Relay.createContainer(LargeList,{
  fragments: {
    shows: () => Relay.QL`
      fragment on PartnerShow @relay(plural: true) {
        __id
        ${Show.getFragment('show')}
      }
    `,
  }
})
<|endoftext|>


<commit_before>const mongoose = require('mongoose');

const createscheduleSchema = new mongoose.Schema({
  user_id: String,
  {name: String},
  { timestamps: true });

const Createschedule = mongoose.model('Createschedule', createscheduleSchema);

module.exports = Createschedule;
<commit_msg>adding user id again again again<commit_after>const mongoose = require('mongoose');

const createscheduleSchema = new mongoose.Schema({
  user_id: String},
  {name: String},
  { timestamps: true });

const Createschedule = mongoose.model('Createschedule', createscheduleSchema);

module.exports = Createschedule;
<|endoftext|>


<commit_before>'use strict';

var httpOk = function (resp) {
  if (resp.login) {
    this.attr('class', 'fa fa-circle ok')
      .attr('title', 'Conexão estabelecida')
      .html('');

  } else {
    this.attr('class', 'fa fa-circle nok')
      .attr('title', 'Sessão expirada')
      .html('Sua sessão expirou! Clique aqui para entrar novamente.');
  }
};

var httpError = function (resp) {
  this.attr('class', 'fa fa-circle nok')
    .attr('title', 'Conexão perdida')
    .html('');
};

var atualizarStatus = function () {
  this.attr('class', 'fa fa-spinner fa-spin')
    .attr('title', 'Verificando conexão')
    .html('');

  m.request({
    url: '/editar/api/ping',
    extract: function (xhr) {
      if (/\/editar\/login/.test(xhr.responseURL)) {
        return '{"login": null}';
      }
      return xhr.responseText;
    },
    background: true
  }).then(_.bind(httpOk, this), _.bind(httpError, this));
};

var config = function (element, isInitialized) {
  if (isInitialized) {
    return;
  }

  var e = jQuery(element);
  var fn = _.bind(atualizarStatus, e);

  window.setInterval(fn, 30000);
  fn();
};

module.exports = {

  view: function () {
    return m('span#status-conexao', {
      config: config
    });
  }
};
<commit_msg>Verifica conexão com maior frequência (10s, não 30s)<commit_after>'use strict';

var httpOk = function (resp) {
  if (resp.login) {
    this.attr('class', 'fa fa-circle ok')
      .attr('title', 'Conexão estabelecida')
      .html('');

  } else {
    this.attr('class', 'fa fa-circle nok')
      .attr('title', 'Sessão expirada')
      .html('Sua sessão expirou! Clique aqui para entrar novamente.');
  }
};

var httpError = function (resp) {
  this.attr('class', 'fa fa-circle nok')
    .attr('title', 'Conexão perdida')
    .html('');
};

var atualizarStatus = function () {
  this.attr('class', 'fa fa-spinner fa-spin')
    .attr('title', 'Verificando conexão')
    .html('');

  m.request({
    url: '/editar/api/ping',
    extract: function (xhr) {
      if (/\/editar\/login/.test(xhr.responseURL)) {
        return '{"login": null}';
      }
      return xhr.responseText;
    },
    background: true
  }).then(_.bind(httpOk, this), _.bind(httpError, this));
};

var config = function (element, isInitialized) {
  if (isInitialized) {
    return;
  }

  var e = jQuery(element);
  var fn = _.bind(atualizarStatus, e);

  window.setInterval(fn, 10000);
  fn();
};

module.exports = {

  view: function () {
    return m('span#status-conexao', {
      config: config
    });
  }
};
<|endoftext|>


<commit_before>var extname = require('path').extname;
var watchify = require('watchify');

var transformMap = {
  '.coffee': 'coffeeify',
  '.gs': 'gorillaify',
  '.iced': 'icsify',
  '.ls': 'liveify',
  '.coco': 'cocoify',
  '.ts': 'typescriptifier'
};

module.exports = function (files) {
  var ext = extname(files[0]);
  var transform = ext != '.js';
  var ret;

  if (transform) {
    ret = watchify({extensions: [ext, '.js', '.json']});
  } else {
    ret = watchify();
  }

  files.forEach(function (file) {
    ret.add(file);
  });

  if (transform) ret.transform(transformMap[ext]);

  return ret;
};
<commit_msg>add hbsfy transform<commit_after>var extname = require('path').extname;
var watchify = require('watchify');

var transformMap = {
  '.coffee': 'coffeeify',
  '.gs': 'gorillaify',
  '.iced': 'icsify',
  '.ls': 'liveify',
  '.coco': 'cocoify',
  '.ts': 'typescriptifier',
  '.hbs': 'hbsfy'
};

module.exports = function (files) {
  var ext = extname(files[0]);
  var transform = ext != '.js';
  var ret;

  if (transform) {
    ret = watchify({extensions: [ext, '.js', '.json']});
  } else {
    ret = watchify();
  }

  files.forEach(function (file) {
    ret.add(file);
  });

  if (transform) ret.transform(transformMap[ext]);

  return ret;
};
<|endoftext|>


<commit_before>import { parse } from 'meteor-babel';
import { analyze as analyzeScope } from 'escope';

// Like babel.parse, but annotates any thrown error with $ParseError = true.
function tryToParse(source) {
  try {
    return parse(source, {strictMode: false});
  } catch (e) {
    if (typeof e.loc === 'object') {
      e.$ParseError = true;
    }
    throw e;
  }
}

// Analyze the JavaScript source code `source` and return a dictionary of all
// globals which are assigned to in the package. The values in the dictionary
// are all `true`.
//
// This is intended for use in detecting package-scope variables in Meteor
// packages, where the linker needs to add a "var" statement to prevent them
// from staying as globals.
//
// It only cares about assignments to variables; an assignment to a field on an
// object (`Foo.Bar = true`) neither causes `Foo` nor `Foo.Bar` to be returned.
export function findAssignedGlobals(source) {
  // escope's analyzer treats vars in the top-level "Program" node as globals.
  // The \n// */\n is necessary in case source ends with an unclosed comment.
  const wrappedSource = 'function wrapper() {' + source + '\n// */\n}';

  const ast = tryToParse(wrappedSource);
  // We have to pass ignoreEval; otherwise, the existence of a direct eval call
  // causes escope to not bother to resolve references in the eval's scope.
  // This is because an eval can pull references inward:
  //
  //   function outer() {
  //     var i = 42;
  //     function inner() {
  //       eval('var i = 0');
  //       i;  // 0, not 42
  //     }
  //   }
  //
  // But it can't pull references outward, so for our purposes it is safe to
  // ignore.
  const scopeManager = analyzeScope(ast, { ignoreEval: true });
  const globalScope = scopeManager.acquire(ast);

  const assignedGlobals = {};
  // Underscore is not available in this package.
  globalScope.implicit.variables.forEach((variable) => {
    assignedGlobals[variable.name] = true;
  });

  return assignedGlobals;
}
<commit_msg>Implement findImportedModuleIdentifiers.<commit_after>import { parse } from 'meteor-babel';
import { analyze as analyzeScope } from 'escope';

// Like babel.parse, but annotates any thrown error with $ParseError = true.
function tryToParse(source) {
  try {
    return parse(source, {
      strictMode: false,
      ecmaVersion: 6,
      sourceType: "module",
      allowReturnOutsideFunction: true,
      allowImportExportEverywhere: true,
    });
  } catch (e) {
    if (typeof e.loc === 'object') {
      e.$ParseError = true;
    }
    throw e;
  }
}

var dependencyKeywordPattern = /\b(require|import|export)\b/g;

/**
 * The `findImportedModuleIdentifiers` function takes a string of module
 * source code and returns a map from imported module identifiers to AST
 * nodes. The keys of this map are used in ./import-scanner.js to traverse
 * the module dependency graph. The AST nodes are generally ignored.
 *
 * The implementation uses a regular expression to scan quickly for
 * possible locations of certain tokens (`require`, `import`, `export`),
 * then uses that location information to steer the AST traversal, so that
 * it visits only subtrees that contain interesting tokens, saving a lot
 * of time by ignoring the rest of the AST. The AST traversal determines
 * if the tokens were actually what we thought they were (a `require`
 * function call, or an `import` or `export` statement).
 */
export function findImportedModuleIdentifiers(source) {
  const identifiers = {};
  const possibleIndexes = [];
  let match;

  dependencyKeywordPattern.lastIndex = 0;
  while ((match = dependencyKeywordPattern.exec(source))) {
    possibleIndexes.push(match.index);
  }

  if (!possibleIndexes.length) {
    return {};
  }

  const ast = tryToParse(source);

  function walk(node, left, right) {
    if (left >= right) {
      // The window of possible indexes is empty, so we can ignore
      // the entire subtree rooted at this node.
    } else if (Array.isArray(node)) {
      for (var i = 0, len = node.length; i < len; ++i) {
        walk(node[i], left, right);
      }
    } else if (isNode(node)) {
      const start = node.start;
      const end = node.end;

      // Narrow the left-right window to exclude possible indexes
      // that fall outside of the current node.
      while (left < right && possibleIndexes[left] < start) ++left;
      while (left < right && end < possibleIndexes[right - 1]) --right;

      if (left < right) {
        let id = getRequiredModuleId(node);
        if (typeof id === "string") {
          identifiers[id] = node;
          return;
        }

        id = getImportedModuleId(node);
        if (typeof id === "string") {
          identifiers[id] = node;
          return;
        }

        // Continue traversing the children of this node.
        for (const key of Object.keys(node)) {
          switch (key) {
          case "type":
          case "loc":
          case "start":
          case "end":
            // Ignore common keys that are never nodes.
            continue;
          }

          walk(node[key], left, right);
        }
      }
    }
  }

  walk(ast, 0, possibleIndexes.length);

  return identifiers;
}

function isNode(value) {
  return value
    && typeof value === "object"
    && typeof value.type === "string"
    && typeof value.start === "number"
    && typeof value.end === "number";
}

function getRequiredModuleId(node) {
  if (node.type === "CallExpression" &&
      node.callee.type === "Identifier" &&
      node.callee.name === "require") {
    const args = node.arguments;
    const argc = args.length;
    if (argc > 0) {
      const arg = args[0];
      if (arg.type === "Literal" &&
          typeof arg.value === "string") {
        return arg.value;
      }
    }
  }
}

function getImportedModuleId(node) {
  if (node.type === "ImportDeclaration" ||
      node.type === "ExportAllDeclaration" ||
      node.type === "ExportNamedDeclaration") {
    // The .source of an ImportDeclaration or Export{Named,All}Declaration
    // is always a string-valued Literal node, if not null.
    if (isNode(node.source)) {
      return node.source.value;
    }
  }
}

// Analyze the JavaScript source code `source` and return a dictionary of all
// globals which are assigned to in the package. The values in the dictionary
// are all `true`.
//
// This is intended for use in detecting package-scope variables in Meteor
// packages, where the linker needs to add a "var" statement to prevent them
// from staying as globals.
//
// It only cares about assignments to variables; an assignment to a field on an
// object (`Foo.Bar = true`) neither causes `Foo` nor `Foo.Bar` to be returned.
export function findAssignedGlobals(source) {
  // escope's analyzer treats vars in the top-level "Program" node as globals.
  // The \n// */\n is necessary in case source ends with an unclosed comment.
  const wrappedSource = 'function wrapper() {' + source + '\n// */\n}';

  const ast = tryToParse(wrappedSource);
  // We have to pass ignoreEval; otherwise, the existence of a direct eval call
  // causes escope to not bother to resolve references in the eval's scope.
  // This is because an eval can pull references inward:
  //
  //   function outer() {
  //     var i = 42;
  //     function inner() {
  //       eval('var i = 0');
  //       i;  // 0, not 42
  //     }
  //   }
  //
  // But it can't pull references outward, so for our purposes it is safe to
  // ignore.
  const scopeManager = analyzeScope(ast, {
    ecmaVersion: 6,
    sourceType: "module",
    ignoreEval: true,
  });
  const globalScope = scopeManager.acquire(ast);

  const assignedGlobals = {};
  // Underscore is not available in this package.
  globalScope.implicit.variables.forEach((variable) => {
    assignedGlobals[variable.name] = true;
  });

  return assignedGlobals;
}
<|endoftext|>


<commit_before>// TODO: You've got the isVolunteer action happening pre-save and the contact group addition happening post-save.  Can we consolidate?
var keystone			= require('keystone'),
	Types				= keystone.Field.Types,
	async				= require('async'),
	// Export to make it available using require.  The keystone.list import throws a ReferenceError when importing a list
	// that comes later when sorting alphabetically
	OutsideContactGroup = require('./OutsideContactGroup');

// Create model
var OutsideContact = new keystone.List('Outside Contact', {
	autokey: { path: 'key', from: 'slug', unique: true },
	map: { name: 'name.full' },
	defaultSort: 'name.full'
});

// Create fields
OutsideContact.add( 'General Information', {

	type: { type: Types.Relationship, label: 'type of contact', ref: 'Outside Contact Group', many: true, required: true, initial: true },

	name: {
		first: { type: Types.Text, label: 'first name', required: true, initial: true },
		last: { type: Types.Text, label: 'last name', required: true, initial: true },
		full: { type: Types.Text, label: 'name', hidden: true, noedit: true, initial: false }
	},

	organization: { type: Types.Text, label: 'organization', initial: true }

}, 'Contact Information', {

	email: { type: Types.Email, label: 'email address', unique: true, required: true, initial: true },

	phone: {
		work: { type: Types.Text, label: 'work phone number', initial: true },
		mobile: { type: Types.Text, label: 'mobile phone number', initial: true },
		preferred: { type: Types.Select, label: 'preferred phone', options: 'work, mobile', initial: true }
	}

}, 'Address', {

	address: {
		street1: { type: Types.Text, label: 'street 1', initial: true },
		street2: { type: Types.Text, label: 'street 2', initial: true },
		city: { type: Types.Text, label: 'city', initial: true },
		state: { type: Types.Relationship, label: 'state', ref: 'State', initial: true },
		zipCode: { type: Types.Text, label: 'zip code', initial: true }
	}

}, { heading: 'Relationship Maps', hidden: true }, {

	isVolunteer: { type: Types.Boolean, hidden: true, noedit: true }

/* Container for data migration fields ( these should be kept until after phase 2 and the old system is phased out completely ) */
}, {
	// system field to store an appropriate file prefix
	oldId: { type: Types.Text, hidden: true }

});

// Pre Save
OutsideContact.schema.pre('save', function(next) {
	'use strict';

	var model			= this,
		contactGroups	= this.type;

	// Populate the full name string for better identification when linking through Relationship field types
	this.name.full = this.name.first + ' ' + this.name.last;
	// Reset the isVolunteer flag to allow a fresh check every save
	this.isVolunteer = false;

	// Loop through each of the outside contact groups the user should be added to and mark the outside contact as a volunteer
	// if they are part of the 'volunteers' outside contact group
	async.each(contactGroups, function(listID, callback) {

		OutsideContactGroup.model.findById(listID)
				.exec()
				.then(function(result) {
					// Events have outside contacts who are volunteers listed, we need to capture a reference to which outside contacts are volunteers
					if(result.name === 'volunteers') {
						model.isVolunteer = true;
					}

					callback();

				}, function(err) {
					console.log(err);
					callback();
				});

	}, function(err){
			// if anything produces an error, err will equal that error
			if( err ) {
				// One of the iterations produced an error.  All processing will now stop.
				console.log('an error occurred saving the outside contact\'s volunteer status');
				next();
			} else {
				console.log('outside contact volunteer status saved successfully');
				next();
			}
	});
});

// Post Save
OutsideContact.schema.post('save', function() {
	'use strict';

	var model			= this,
		contactGroups	= this.type;

	// Loop through each of the outside contact groups the user should be added to and add them.  Async allows all assignments
	// to happen in parallel before handling the result.
	async.each(contactGroups, function(listID, callback) {
		console.log('listID 2:', listID);
		OutsideContactGroup.model.findById(listID)
				.exec()
				.then(function(result) {
					// Use the model _id to determine if they are already part of the outside contact group
					var inList = result.outsideContacts.indexOf(model._id) !== -1;
					// Add the model to the list only if they aren't already included
					if( !inList ) {
						result.outsideContacts.push(model._id);
						result.save(function(err) {
							console.log('outside contact saved to ' +  result.name + ' group');
							callback();
						}, function(err) {
							console.log(err);
							callback();
						});
					} else {
						callback();
					}

				}, function(err) {
					console.log(err);
					callback();
				});

	}, function(err){
			// if anything produces an error, err will equal that error
			if( err ) {
				// One of the iterations produced an error.  All processing will now stop.
				console.log('an error occurred saving the outside contact to one or more outside contact groups');
			} else {
				console.log('outside contact saved to all outside contact groups successfully');
			}
	});
});

// Set up relationship values to show up at the bottom of the model if any exist
OutsideContact.relationship({ ref: 'Outside Contact Group', refPath: 'outsideContacts', path: 'outside-contact-groups', label: 'outside contact groups' });
OutsideContact.relationship({ ref: 'Mailing List', refPath: 'outsideContactSubscribers', path: 'mailing-lists', label: 'mailing lists' });

// Define default columns in the admin interface and register the model
OutsideContact.defaultColumns = 'name.full, type, organization';
OutsideContact.register();
<commit_msg>#21 | merge first, last, and full name into a single name field for Outside Contacts<commit_after>// TODO: You've got the isVolunteer action happening pre-save and the contact group addition happening post-save.  Can we consolidate?
var keystone			= require('keystone'),
	Types				= keystone.Field.Types,
	async				= require('async'),
	// Export to make it available using require.  The keystone.list import throws a ReferenceError when importing a list
	// that comes later when sorting alphabetically
	OutsideContactGroup = require('./OutsideContactGroup');

// Create model
var OutsideContact = new keystone.List('Outside Contact', {
	autokey: { path: 'key', from: 'slug', unique: true },
	map: { name: 'name.full' },
	defaultSort: 'name.full'
});

// Create fields
OutsideContact.add( 'General Information', {

	type: { type: Types.Relationship, label: 'type of contact', ref: 'Outside Contact Group', many: true, required: true, initial: true },

	name: { type: Types.Text, label: 'name', required: true, initial: true },

	organization: { type: Types.Text, label: 'organization', initial: true }

}, 'Contact Information', {

	email: { type: Types.Email, label: 'email address', unique: true, required: true, initial: true },

	phone: {
		work: { type: Types.Text, label: 'work phone number', initial: true },
		mobile: { type: Types.Text, label: 'mobile phone number', initial: true },
		preferred: { type: Types.Select, label: 'preferred phone', options: 'work, mobile', initial: true }
	}

}, 'Address', {

	address: {
		street1: { type: Types.Text, label: 'street 1', initial: true },
		street2: { type: Types.Text, label: 'street 2', initial: true },
		city: { type: Types.Text, label: 'city', initial: true },
		state: { type: Types.Relationship, label: 'state', ref: 'State', initial: true },
		zipCode: { type: Types.Text, label: 'zip code', initial: true }
	}

}, { heading: 'Relationship Maps', hidden: true }, {

	isVolunteer: { type: Types.Boolean, hidden: true, noedit: true }

/* Container for data migration fields ( these should be kept until after phase 2 and the old system is phased out completely ) */
}, {
	// system field to store an appropriate file prefix
	oldId: { type: Types.Text, hidden: true }

});

// Pre Save
OutsideContact.schema.pre('save', function(next) {
	'use strict';

	var model			= this,
		contactGroups	= this.type;

	// Reset the isVolunteer flag to allow a fresh check every save
	this.isVolunteer = false;

	// Loop through each of the outside contact groups the user should be added to and mark the outside contact as a volunteer
	// if they are part of the 'volunteers' outside contact group
	async.each(contactGroups, function(listID, callback) {

		OutsideContactGroup.model.findById(listID)
				.exec()
				.then(function(result) {
					// Events have outside contacts who are volunteers listed, we need to capture a reference to which outside contacts are volunteers
					if(result.name === 'volunteers') {
						model.isVolunteer = true;
					}

					callback();

				}, function(err) {
					console.log(err);
					callback();
				});

	}, function(err){
			// if anything produces an error, err will equal that error
			if( err ) {
				// One of the iterations produced an error.  All processing will now stop.
				console.log('an error occurred saving the outside contact\'s volunteer status');
				next();
			} else {
				console.log('outside contact volunteer status saved successfully');
				next();
			}
	});
});

// Post Save
OutsideContact.schema.post('save', function() {
	'use strict';

	var model			= this,
		contactGroups	= this.type;

	// Loop through each of the outside contact groups the user should be added to and add them.  Async allows all assignments
	// to happen in parallel before handling the result.
	async.each(contactGroups, function(listID, callback) {
		console.log('listID 2:', listID);
		OutsideContactGroup.model.findById(listID)
				.exec()
				.then(function(result) {
					// Use the model _id to determine if they are already part of the outside contact group
					var inList = result.outsideContacts.indexOf(model._id) !== -1;
					// Add the model to the list only if they aren't already included
					if( !inList ) {
						result.outsideContacts.push(model._id);
						result.save(function(err) {
							console.log('outside contact saved to ' +  result.name + ' group');
							callback();
						}, function(err) {
							console.log(err);
							callback();
						});
					} else {
						callback();
					}

				}, function(err) {
					console.log(err);
					callback();
				});

	}, function(err){
			// if anything produces an error, err will equal that error
			if( err ) {
				// One of the iterations produced an error.  All processing will now stop.
				console.log('an error occurred saving the outside contact to one or more outside contact groups');
			} else {
				console.log('outside contact saved to all outside contact groups successfully');
			}
	});
});

// Set up relationship values to show up at the bottom of the model if any exist
OutsideContact.relationship({ ref: 'Outside Contact Group', refPath: 'outsideContacts', path: 'outside-contact-groups', label: 'outside contact groups' });
OutsideContact.relationship({ ref: 'Mailing List', refPath: 'outsideContactSubscribers', path: 'mailing-lists', label: 'mailing lists' });

// Define default columns in the admin interface and register the model
OutsideContact.defaultColumns = 'name.full, type, organization';
OutsideContact.register();
<|endoftext|>


<commit_before>/**
 * Helper to create new views in the templates
 */

var _ = require('underscore'),
    getProperty = require('../../lib/getProperty'),
    BaseView;

module.exports = function (Handlebars) {
  return function (viewName, options) {
    var isServer = typeof window === 'undefined',
        html, viewOptions, view, app;

    viewOptions = options.hash || {};
    app = getProperty('_app', this, options);

    // Pass through a reference to the app.
    if (app) {
      viewOptions.app = app;
      viewName = app.modelUtils.underscorize(viewName);
    } else{
      throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.")
    }

    // allow views to be passed optional block elements
    if (_.isFunction(options.fn)) {
      var blockOptions = _.extend({}, this, viewOptions);
      viewOptions._block = options.fn(blockOptions);
    }

    if (isServer) {
      var parentView = getProperty('_view', this, options);
      html = getServerHtml(viewName, viewOptions, parentView);
    } else {
      html = getClientPlaceholder(viewName, viewOptions);
    }

    return new Handlebars.SafeString(html);
  };
};

function getServerHtml(viewName, viewOptions, parentView) {
  var ViewClass, view;

  if (!BaseView) { BaseView = require('rendr/shared/base/view'); }

  // Pass through a reference to the parent view.
  if (parentView) { viewOptions.parentView = parentView; }

  // get the Backbone.View based on viewName
  ViewClass = BaseView.getView(viewName, viewOptions.app.options.entryPath);
  view = new ViewClass(viewOptions);

  // create the outerHTML using className, tagName
  return view.getHtml();
}

function getClientPlaceholder(viewName, viewOptions) {
  if (!BaseView) { BaseView = require('rendr/shared/base/view'); }
  var fetchSummary;

  // Builds a fetch_summary attribute
  viewOptions = BaseView.parseModelAndCollection(viewOptions.app.modelUtils, viewOptions);
  fetchSummary = BaseView.extractFetchSummary(viewOptions.app.modelUtils, viewOptions);
  viewOptions['fetch_summary'] = fetchSummary
  viewOptions = _.omit(viewOptions, _.keys(fetchSummary).concat(['model', 'collection', 'app']));

  // create a list of data attributes
  var attrString = _.inject(viewOptions, function(memo, value, key) {
    if (_.isArray(value) || _.isObject(value)) { value = JSON.stringify(value); }
    return memo += " data-" + key + "=\"" + _.escape(value) + "\"";
  }, '');

  return '

';
}
<commit_msg>fix nested partial view client render issue<commit_after>/**
 * Helper to create new views in the templates
 */

var _ = require('underscore'),
    getProperty = require('../../lib/getProperty'),
    BaseView;

module.exports = function (Handlebars) {
  return function (viewName, options) {
    var isServer = typeof window === 'undefined',
        html, viewOptions, view, app;

    viewOptions = options.hash || {};
    app = getProperty('_app', this, options);

    // Pass through a reference to the app.
    if (app) {
      viewOptions.app = app;
      viewName = app.modelUtils.underscorize(viewName);
    } else{
      throw new Error("An App instance is required when rendering a view, it could not be extracted from the options.")
    }

    // allow views to be passed optional block elements
    if (_.isFunction(options.fn)) {
      var blockOptions = _.extend({}, this, viewOptions);
      viewOptions._block = options.fn(blockOptions);
    }

    if (isServer) {
      var parentView = getProperty('_view', this, options);
      html = getServerHtml(viewName, viewOptions, parentView);
    } else {
      html = getClientPlaceholder(viewName, viewOptions, Handlebars);
    }

    return new Handlebars.SafeString(html);
  };
};

function getServerHtml(viewName, viewOptions, parentView) {
  var ViewClass, view;

  if (!BaseView) { BaseView = require('rendr/shared/base/view'); }

  // Pass through a reference to the parent view.
  if (parentView) { viewOptions.parentView = parentView; }

  // get the Backbone.View based on viewName
  ViewClass = BaseView.getView(viewName, viewOptions.app.options.entryPath);
  view = new ViewClass(viewOptions);

  // create the outerHTML using className, tagName
  return view.getHtml();
}

function getClientPlaceholder(viewName, viewOptions, Handlebars) {
  if (!BaseView) { BaseView = require('rendr/shared/base/view'); }
  var fetchSummary;

  // Builds a fetch_summary attribute
  viewOptions = BaseView.parseModelAndCollection(viewOptions.app.modelUtils, viewOptions);
  fetchSummary = BaseView.extractFetchSummary(viewOptions.app.modelUtils, viewOptions);
  viewOptions['fetch_summary'] = fetchSummary
  viewOptions = _.omit(viewOptions, _.keys(fetchSummary).concat(['model', 'collection', 'app']));

  // create a list of data attributes
  var attrString = _.inject(viewOptions, function(memo, value, key) {
    if (_.isArray(value) || _.isObject(value)) {
      if (key === '_block' && value.contructor === Handlebars.SafeString) {
        value = value.string;
      } else {
        value = JSON.stringify(value);
      }
    }
    return memo += " data-" + key + "=\"" + _.escape(value) + "\"";
  }, '');

  return '

';
}
<|endoftext|>


<commit_before>const mongoose = require('mongoose')
  , config = require('../config.js')
  , Location = require('./location');

// Creates a new Mongoose Schema object
const Schema = mongoose.Schema;
const locationLimit = config.server.locationServices.userCollectionLocationLimit;

// Creates the Schema for FFT Locations
const CappedLocationSchema = new Schema({
  userId: {type: Schema.Types.ObjectId, required: false, sparse: true, ref: 'User'},
  eventId: {type: Number, required: false, sparse: true, ref:'Event'},
  locations: [Location.Model.schema]
},{
  versionKey: false
});

// TODO: this seems superfluous - probably remove because there's already an index on eventId in the field definition
CappedLocationSchema.index({'eventId': 1});
// TODO: this seems superflous because there's already an index on properties.timestamp in LocationSchema. do child-schema indexes get created on parent collections?
CappedLocationSchema.index({'locations.properties.timestamp': 1});
CappedLocationSchema.index({'locations.properties.timestamp': 1, 'eventId': 1});

const transform = function (userLocation, ret) {
  if ('function' !== typeof userLocation.ownerDocument) {
    if (userLocation.populated('userId')) {
      ret.user = userLocation.userId.toObject();
      delete ret.user.icon;
    }

    delete ret._id;
    ret.id = userLocation.userId.id || userLocation.userId;
    ret.locations = (userLocation.locations || []).reverse();
  }
}

CappedLocationSchema.set("toJSON", {
  transform: transform
});

CappedLocationSchema.set("toObject", {
  transform: transform
});

// Creates the Model for the User Schema
const CappedLocation = mongoose.model('CappedLocation', CappedLocationSchema);
exports.Model = CappedLocation;

exports.addLocations = function(user, event, locations, callback) {
  const update = {
    $push: {
      locations: {$each: locations, $sort: {"properties.timestamp": 1}, $slice: -1 * locationLimit}
    }
  };

  CappedLocation.findOneAndUpdate({userId: user._id, eventId: event._id}, update, {upsert: true, new: true}, function(err, user) {
    callback(err, user);
  });
};

exports.getLocations = function(options, callback) {
  let limit = options.limit;
  limit = limit <= locationLimit ? limit : locationLimit;

  const filter = options.filter;

  const parameters = {};
  if (filter.eventId) parameters.eventId = filter.eventId;

  const query = CappedLocation.find(parameters, {userId: 1, locations: {$slice: -1 * limit}});

  // TODO take out where
  if (filter.startDate) {
    query.where('locations.properties.timestamp').gte(filter.startDate);
  }

  if (filter.endDate) {
    query.where('locations.properties.timestamp').lt(filter.endDate);
  }

  if (options.populate) {
    query.populate({
      path: 'userId',
      select: 'icon displayName email phones'
    })
  }

  query.exec(callback);
};

exports.removeLocationsForUser = function(user, callback) {
  const conditions = {"userId": user._id};
  CappedLocation.remove(conditions, function(err) {
    callback(err);
  });
};
<commit_msg>Fix user location id<commit_after>const mongoose = require('mongoose')
  , config = require('../config.js')
  , Location = require('./location');

// Creates a new Mongoose Schema object
const Schema = mongoose.Schema;
const locationLimit = config.server.locationServices.userCollectionLocationLimit;

// Creates the Schema for FFT Locations
const CappedLocationSchema = new Schema({
  userId: {type: Schema.Types.ObjectId, required: false, sparse: true, ref: 'User'},
  eventId: {type: Number, required: false, sparse: true, ref:'Event'},
  locations: [Location.Model.schema]
},{
  versionKey: false
});

// TODO: this seems superfluous - probably remove because there's already an index on eventId in the field definition
CappedLocationSchema.index({'eventId': 1});
// TODO: this seems superflous because there's already an index on properties.timestamp in LocationSchema. do child-schema indexes get created on parent collections?
CappedLocationSchema.index({'locations.properties.timestamp': 1});
CappedLocationSchema.index({'locations.properties.timestamp': 1, 'eventId': 1});

const transform = function (userLocation, ret) {
  if ('function' !== typeof userLocation.ownerDocument) {
    if (userLocation.populated('userId')) {
      ret.user = userLocation.userId.toObject();
      delete ret.user.icon;
    }

    delete ret._id;
    ret.id = ret.user ? ret.user.id : userLocation.userId;
    console.log('************************  ret.id is', ret.id)

    ret.locations = (userLocation.locations || []).reverse();
  }
}

CappedLocationSchema.set("toJSON", {
  transform: transform
});

CappedLocationSchema.set("toObject", {
  transform: transform
});

// Creates the Model for the User Schema
const CappedLocation = mongoose.model('CappedLocation', CappedLocationSchema);
exports.Model = CappedLocation;

exports.addLocations = function(user, event, locations, callback) {
  const update = {
    $push: {
      locations: {$each: locations, $sort: {"properties.timestamp": 1}, $slice: -1 * locationLimit}
    }
  };

  CappedLocation.findOneAndUpdate({userId: user._id, eventId: event._id}, update, {upsert: true, new: true}, function(err, user) {
    callback(err, user);
  });
};

exports.getLocations = function(options, callback) {
  let limit = options.limit;
  limit = limit <= locationLimit ? limit : locationLimit;

  const filter = options.filter;

  const parameters = {};
  if (filter.eventId) parameters.eventId = filter.eventId;

  const query = CappedLocation.find(parameters, {userId: 1, locations: {$slice: -1 * limit}});

  // TODO take out where
  if (filter.startDate) {
    query.where('locations.properties.timestamp').gte(filter.startDate);
  }

  if (filter.endDate) {
    query.where('locations.properties.timestamp').lt(filter.endDate);
  }

  if (options.populate) {
    query.populate({
      path: 'userId',
      select: 'icon displayName email phones'
    })
  }

  query.exec(callback);
};

exports.removeLocationsForUser = function(user, callback) {
  const conditions = {"userId": user._id};
  CappedLocation.remove(conditions, function(err) {
    callback(err);
  });
};
<|endoftext|>