<commit_before>let getId = (function() {
  let id = 0;
  return function() {
    id += 1;
    return id - 1;
  }
}());

var resetMenu = function(menu) {
  menu.forEach(function(item) {
    item.quantityOrdered = 0;
  });
};

var northMenu = function() {
  var menu = [{
    id: getId(),
    name: "Salmon & Yuzu Tartar",
    description: "Salmon, yuzu tobiko, salmon keta roe tartar, coriander dressing",
    foodType: "Sashimi",
    restaurant: "Yo! Sushi",
    price: "600"
  }, {
    id: getId(),
    name: "California Roll",
    description: "2 pieces. Surumi, avocado, mayo, sesame",
    foodType: "Rolls",
    restaurant: "Yo! Sushi",
    price: "360"
  }, {
    id: getId(),
    name: "Nigiri Selection",
    description: "1 of each. Yellowfin tuna, salmon, prawn, wasabi",
    foodType: "Nigiri",
    restaurant: "Yo! Sushi",
    price: "450"
  }, {
    id: getId(),
    name: "Avocado & Egg on Rye",
    description: "Layers of freshly sliced avocado, free-range eggs and pink pickled onions on top of our German-style rye bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "450"
  }, {
    id: getId(),
    name: "Pole & Line Caught Tuna & Rocket",
    description: "Skipjack tuna mayo mashed with spring onions, chopped capers, a touch of anchovy paste and a squeeze of lemon, on malted bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "475"
  }, {
    id: getId(),
    name: "Chef's Italian Chicken Salad",
    description: "Caesar dressed chicken with beautiful Spanish roasted red peppers, kalamata olives and a handful of toasted pistachios",
    foodType: "Specials",
    restaurant: "Pret A Manger",
    price: "425"
  }, {
    id: getId(),
    name: "Yaki Udon",
    description: "Udon noodles in curry oil with chicken, prawns, chikuwa, egg, beansprouts, leeks, mushrooms and peppers. garnished with fried shallots, pickled ginger and sesame seeds",
    foodType: "Teppanyaki",
    restaurant: "Wagamama",
    price: "950"
  }, {
    id: getId(),
    name: "Warm Chilli Chicken Salad",
    description: "Stir-fried chicken with red peppers, mangetout, tender stem broccoli and red onion on a bed of baby gem lettuce. dressed in a sweet chilli sauce. garnished with chillies, spring onions and cashew nuts",
    foodType: "Salads",
    restaurant: "Wagamama",
    price: "995"
  }, {
    id: getId(),
    name: "Mini Yasai Yaki Soba",
    description: "Teppan-fried soba noodles with fried tofu, egg, sweetcorn, mangetout, peppers and amai sauce",
    foodType: "Kids",
    restaurant: "Wagamama",
    price: "400"
  }, {
    id: getId(),
    name: "Falafel Wrap",
    description: "Chickpeas, broad beans, coriander, garlic, peppers, tomato, pickled turnip and parsley with tahini sauce",
    foodType: "Wraps & Bread",
    restaurant: "Comptoir Libanais",
    price: "545"
  }, {
    id: getId(),
    name: "Mezze Platter - For Two",
    description: "Baba ghanuj, hommos, tabbouleh, falafel, lentil salad, cheese sambousek, pickles & pita bread",
    foodType: "Mezzes",
    restaurant: "Comptoir Libanais",
    price: "1595"
  }, {
    id: getId(),
    name: "Lamb & Prunes",
    description: "Tender lamb, butternut squash and peas with prunes and roasted almonds",
    foodType: "Tagines",
    restaurant: "Comptoir Libanais",
    price: "1095"
  }, {
    id: getId(),
    name: "Balik No.1",
    description: "A unique and rare salmon from Norwegian waters, smoked and prepared in the original Balik oven",
    foodType: "Smoked Salmon",
    restaurant: "Caviar House",
    price: "4600"
  }, {
    id: getId(),
    name: "Prunier - Tradition - 30gr",
    description: "This caviar’s pure taste, with hints of almond and walnuts, leaves a long lasting-taste on the palate",
    foodType: "Caviar",
    restaurant: "Caviar House",
    price: "6000"
  }, {
    id: getId(),
    name: "Tsarina",
    description: "Blini topped with crème fraîche, served with Balik smoked salmon and 10g of Prunier caviar.",
    foodType: "Classics",
    restaurant: "Caviar House",
    price: "2900"
  }, {
    id: getId(),
    name: "Proper Porridge",
    description: "With either fresh market berries; banana & toasted coconut; or stewed bramble fruit, topped with golden syrup, honey or brown sugar",
    foodType: "Get Your Oats",
    restaurant: "Jamies",
    price: "450"
  }, {
    id: getId(),
    name: "Bacon Buttie",
    description: "With free-range smoked bacon",
    foodType: "Breakfast Butties",
    restaurant: "Jamies",
    price: "495"
  }, {
    id: getId(),
    name: "American Pancakes - Blueberry",
    description: "Thick, fluffy pancake stack topped with blueberry compote",
    foodType: "Toast, Pastries & Sweet Things",
    restaurant: "Jamies",
    price: "6.50"
  }];

  resetMenu(menu);
  return menu;
}

var southMenu = function() {
  var menu = [{
    id: getId(),
    name: "A bowl of superfood",
    description: "Quinoa, Beluga lentils, avocado, baby spinach, raw apple, pumpkin seeds, crumbled goat's feta",
    foodType: "Salads",
    restaurant: "Grain Store",
    price: "1200"
  }, {
    id: getId(),
    name: "Wild mushroom and truffle burger",
    description: "Chicory, apple and hazelnut salad, milk bun",
    foodType: "Homemade Burgers",
    restaurant: "Grain Store",
    price: "1300"
  }, {
    id: getId(),
    name: "Smoked Haddock Kedgeree",
    description: "With mango chutney",
    foodType: "Specials",
    restaurant: "Grain Store",
    price: "1150"
  }, {
    id: getId(),
    name: "Avocado & Egg on Rye",
    description: "Layers of freshly sliced avocado, free-range eggs and pink pickled onions on top of our German-style rye bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "450"
  }, {
    id: getId(),
    name: "Pole & Line Caught Tuna & Rocket",
    description: "Skipjack tuna mayo mashed with spring onions, chopped capers, a touch of anchovy paste and a squeeze of lemon, on malted bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "475"
  }, {
    id: getId(),
    name: "Chef's Italian Chicken Salad",
    description: "Caesar dressed chicken with beautiful Spanish roasted red peppers, kalamata olives and a handful of toasted pistachios",
    foodType: "Specials",
    restaurant: "Pret A Manger",
    price: "425"
  }, {
    id: getId(),
    name: "Yaki Udon",
    description: "Udon noodles in curry oil with chicken, prawns, chikuwa, egg, beansprouts, leeks, mushrooms and peppers. garnished with fried shallots, pickled ginger and sesame seeds",
    foodType: "Teppanyaki",
    restaurant: "Wagamama",
    price: "950"
  }, {
    id: getId(),
    name: "Warm Chilli Chicken Salad",
    description: "Stir-fried chicken with red peppers, mangetout, tender stem broccoli and red onion on a bed of baby gem lettuce. dressed in a sweet chilli sauce. garnished with chillies, spring onions and cashew nuts",
    foodType: "Salads",
    restaurant: "Wagamama",
    price: "995"
  }, {
    id: getId(),
    name: "Mini Yasai Yaki Soba",
    description: "Teppan-fried soba noodles with fried tofu, egg, sweetcorn, mangetout, peppers and amai sauce",
    foodType: "Kids",
    restaurant: "Wagamama",
    price: "400"
  }, {
    id: getId(),
    name: "Griddled Buttermilk Pancakes",
    description: "With bananas, strawberries, orange segments, toasted pumpkin seeds & maple syrup",
    foodType: "Breakfast",
    restaurant: "Wonder Tree",
    price: "775"
  }, {
    id: getId(),
    name: "Green Quinoa Salad",
    description: "Kale, baby spinach, quinoa, pomegranate, broccolini florets, avocado, omega rich seeds, honey-mustard & orange dressing",
    foodType: "Salads",
    restaurant: "Wonder Tree",
    price: "1075"
  }, {
    id: getId(),
    name: "Californian Chicken & Avocado Club",
    description: "Grilled herby chicken, avocado, “wonderslaw”, green leaves & aioli verde mayo in crusty ciabatta bap",
    foodType: "Two Handed Burgers",
    restaurant: "Wonder Tree",
    price: "1145"
  }, {
    id: getId(),
    name: "1/4 Chicken Breast - 2 sides",
    description: "Quarter chicken breast, on the bone - choose 2 sides to go with",
    foodType: "On The Bone",
    restaurant: "Nandos",
    price: "760"
  }, {
    id: getId(),
    name: "Portobello Mushroom and Halloumi Wrap - No Sides",
    description: "Chilli Jam and Yogurt Mayonnaise sauce options",
    foodType: "Burgers, Pittas, Wraps",
    restaurant: "Nandos",
    price: "740"
  }, {
    id: getId(),
    name: "Chips - Regular",
    description: "This size included where Sides included in Main Order, e.g. Pitta + 2 Sides",
    foodType: "Sides",
    restaurant: "Nandos",
    price: "240"
  }, {
    id: getId(),
    name: "Balik No.1",
    description: "A unique and rare salmon from Norwegian waters, smoked and prepared in the original Balik oven",
    foodType: "Smoked Salmon",
    restaurant: "Caviar House",
    price: "4600"
  }, {
    id: getId(),
    name: "Prunier - Tradition - 30gr",
    description: "This caviar’s pure taste, with hints of almond and walnuts, leaves a long lasting-taste on the palate",
    foodType: "Caviar",
    restaurant: "Caviar House",
    price: "6000"
  }, {
    id: getId(),
    name: "Tsarina",
    description: "Blini topped with crème fraîche, served with Balik smoked salmon and 10g of Prunier caviar.",
    foodType: "Classics",
    restaurant: "Caviar House",
    price: "2900"
  }];

  resetMenu(menu);
  return menu;
}

module.exports = {
  northMenu: northMenu,
  southMenu: southMenu
}
<commit_msg>Capitalised Grain Store menu items #321<commit_after>let getId = (function() {
  let id = 0;
  return function() {
    id += 1;
    return id - 1;
  }
}());

var resetMenu = function(menu) {
  menu.forEach(function(item) {
    item.quantityOrdered = 0;
  });
};

var northMenu = function() {
  var menu = [{
    id: getId(),
    name: "Salmon & Yuzu Tartar",
    description: "Salmon, yuzu tobiko, salmon keta roe tartar, coriander dressing",
    foodType: "Sashimi",
    restaurant: "Yo! Sushi",
    price: "600"
  }, {
    id: getId(),
    name: "California Roll",
    description: "2 pieces. Surumi, avocado, mayo, sesame",
    foodType: "Rolls",
    restaurant: "Yo! Sushi",
    price: "360"
  }, {
    id: getId(),
    name: "Nigiri Selection",
    description: "1 of each. Yellowfin tuna, salmon, prawn, wasabi",
    foodType: "Nigiri",
    restaurant: "Yo! Sushi",
    price: "450"
  }, {
    id: getId(),
    name: "Avocado & Egg on Rye",
    description: "Layers of freshly sliced avocado, free-range eggs and pink pickled onions on top of our German-style rye bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "450"
  }, {
    id: getId(),
    name: "Pole & Line Caught Tuna & Rocket",
    description: "Skipjack tuna mayo mashed with spring onions, chopped capers, a touch of anchovy paste and a squeeze of lemon, on malted bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "475"
  }, {
    id: getId(),
    name: "Chef's Italian Chicken Salad",
    description: "Caesar dressed chicken with beautiful Spanish roasted red peppers, kalamata olives and a handful of toasted pistachios",
    foodType: "Specials",
    restaurant: "Pret A Manger",
    price: "425"
  }, {
    id: getId(),
    name: "Yaki Udon",
    description: "Udon noodles in curry oil with chicken, prawns, chikuwa, egg, beansprouts, leeks, mushrooms and peppers. garnished with fried shallots, pickled ginger and sesame seeds",
    foodType: "Teppanyaki",
    restaurant: "Wagamama",
    price: "950"
  }, {
    id: getId(),
    name: "Warm Chilli Chicken Salad",
    description: "Stir-fried chicken with red peppers, mangetout, tender stem broccoli and red onion on a bed of baby gem lettuce. dressed in a sweet chilli sauce. garnished with chillies, spring onions and cashew nuts",
    foodType: "Salads",
    restaurant: "Wagamama",
    price: "995"
  }, {
    id: getId(),
    name: "Mini Yasai Yaki Soba",
    description: "Teppan-fried soba noodles with fried tofu, egg, sweetcorn, mangetout, peppers and amai sauce",
    foodType: "Kids",
    restaurant: "Wagamama",
    price: "400"
  }, {
    id: getId(),
    name: "Falafel Wrap",
    description: "Chickpeas, broad beans, coriander, garlic, peppers, tomato, pickled turnip and parsley with tahini sauce",
    foodType: "Wraps & Bread",
    restaurant: "Comptoir Libanais",
    price: "545"
  }, {
    id: getId(),
    name: "Mezze Platter - For Two",
    description: "Baba ghanuj, hommos, tabbouleh, falafel, lentil salad, cheese sambousek, pickles & pita bread",
    foodType: "Mezzes",
    restaurant: "Comptoir Libanais",
    price: "1595"
  }, {
    id: getId(),
    name: "Lamb & Prunes",
    description: "Tender lamb, butternut squash and peas with prunes and roasted almonds",
    foodType: "Tagines",
    restaurant: "Comptoir Libanais",
    price: "1095"
  }, {
    id: getId(),
    name: "Balik No.1",
    description: "A unique and rare salmon from Norwegian waters, smoked and prepared in the original Balik oven",
    foodType: "Smoked Salmon",
    restaurant: "Caviar House",
    price: "4600"
  }, {
    id: getId(),
    name: "Prunier - Tradition - 30gr",
    description: "This caviar’s pure taste, with hints of almond and walnuts, leaves a long lasting-taste on the palate",
    foodType: "Caviar",
    restaurant: "Caviar House",
    price: "6000"
  }, {
    id: getId(),
    name: "Tsarina",
    description: "Blini topped with crème fraîche, served with Balik smoked salmon and 10g of Prunier caviar.",
    foodType: "Classics",
    restaurant: "Caviar House",
    price: "2900"
  }, {
    id: getId(),
    name: "Proper Porridge",
    description: "With either fresh market berries; banana & toasted coconut; or stewed bramble fruit, topped with golden syrup, honey or brown sugar",
    foodType: "Get Your Oats",
    restaurant: "Jamies",
    price: "450"
  }, {
    id: getId(),
    name: "Bacon Buttie",
    description: "With free-range smoked bacon",
    foodType: "Breakfast Butties",
    restaurant: "Jamies",
    price: "495"
  }, {
    id: getId(),
    name: "American Pancakes - Blueberry",
    description: "Thick, fluffy pancake stack topped with blueberry compote",
    foodType: "Toast, Pastries & Sweet Things",
    restaurant: "Jamies",
    price: "6.50"
  }];

  resetMenu(menu);
  return menu;
}

var southMenu = function() {
  var menu = [{
    id: getId(),
    name: "A Bowl of Superfood",
    description: "Quinoa, Beluga lentils, avocado, baby spinach, raw apple, pumpkin seeds, crumbled goat's feta",
    foodType: "Salads",
    restaurant: "Grain Store",
    price: "1200"
  }, {
    id: getId(),
    name: "Wild Mushroom and Truffle Burger",
    description: "Chicory, apple and hazelnut salad, milk bun",
    foodType: "Homemade Burgers",
    restaurant: "Grain Store",
    price: "1300"
  }, {
    id: getId(),
    name: "Smoked Haddock Kedgeree",
    description: "With mango chutney",
    foodType: "Specials",
    restaurant: "Grain Store",
    price: "1150"
  }, {
    id: getId(),
    name: "Avocado & Egg on Rye",
    description: "Layers of freshly sliced avocado, free-range eggs and pink pickled onions on top of our German-style rye bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "450"
  }, {
    id: getId(),
    name: "Pole & Line Caught Tuna & Rocket",
    description: "Skipjack tuna mayo mashed with spring onions, chopped capers, a touch of anchovy paste and a squeeze of lemon, on malted bread",
    foodType: "Sandwiches",
    restaurant: "Pret A Manger",
    price: "475"
  }, {
    id: getId(),
    name: "Chef's Italian Chicken Salad",
    description: "Caesar dressed chicken with beautiful Spanish roasted red peppers, kalamata olives and a handful of toasted pistachios",
    foodType: "Specials",
    restaurant: "Pret A Manger",
    price: "425"
  }, {
    id: getId(),
    name: "Yaki Udon",
    description: "Udon noodles in curry oil with chicken, prawns, chikuwa, egg, beansprouts, leeks, mushrooms and peppers. garnished with fried shallots, pickled ginger and sesame seeds",
    foodType: "Teppanyaki",
    restaurant: "Wagamama",
    price: "950"
  }, {
    id: getId(),
    name: "Warm Chilli Chicken Salad",
    description: "Stir-fried chicken with red peppers, mangetout, tender stem broccoli and red onion on a bed of baby gem lettuce. dressed in a sweet chilli sauce. garnished with chillies, spring onions and cashew nuts",
    foodType: "Salads",
    restaurant: "Wagamama",
    price: "995"
  }, {
    id: getId(),
    name: "Mini Yasai Yaki Soba",
    description: "Teppan-fried soba noodles with fried tofu, egg, sweetcorn, mangetout, peppers and amai sauce",
    foodType: "Kids",
    restaurant: "Wagamama",
    price: "400"
  }, {
    id: getId(),
    name: "Griddled Buttermilk Pancakes",
    description: "With bananas, strawberries, orange segments, toasted pumpkin seeds & maple syrup",
    foodType: "Breakfast",
    restaurant: "Wonder Tree",
    price: "775"
  }, {
    id: getId(),
    name: "Green Quinoa Salad",
    description: "Kale, baby spinach, quinoa, pomegranate, broccolini florets, avocado, omega rich seeds, honey-mustard & orange dressing",
    foodType: "Salads",
    restaurant: "Wonder Tree",
    price: "1075"
  }, {
    id: getId(),
    name: "Californian Chicken & Avocado Club",
    description: "Grilled herby chicken, avocado, “wonderslaw”, green leaves & aioli verde mayo in crusty ciabatta bap",
    foodType: "Two Handed Burgers",
    restaurant: "Wonder Tree",
    price: "1145"
  }, {
    id: getId(),
    name: "1/4 Chicken Breast - 2 sides",
    description: "Quarter chicken breast, on the bone - choose 2 sides to go with",
    foodType: "On The Bone",
    restaurant: "Nandos",
    price: "760"
  }, {
    id: getId(),
    name: "Portobello Mushroom and Halloumi Wrap - No Sides",
    description: "Chilli Jam and Yogurt Mayonnaise sauce options",
    foodType: "Burgers, Pittas, Wraps",
    restaurant: "Nandos",
    price: "740"
  }, {
    id: getId(),
    name: "Chips - Regular",
    description: "This size included where Sides included in Main Order, e.g. Pitta + 2 Sides",
    foodType: "Sides",
    restaurant: "Nandos",
    price: "240"
  }, {
    id: getId(),
    name: "Balik No.1",
    description: "A unique and rare salmon from Norwegian waters, smoked and prepared in the original Balik oven",
    foodType: "Smoked Salmon",
    restaurant: "Caviar House",
    price: "4600"
  }, {
    id: getId(),
    name: "Prunier - Tradition - 30gr",
    description: "This caviar’s pure taste, with hints of almond and walnuts, leaves a long lasting-taste on the palate",
    foodType: "Caviar",
    restaurant: "Caviar House",
    price: "6000"
  }, {
    id: getId(),
    name: "Tsarina",
    description: "Blini topped with crème fraîche, served with Balik smoked salmon and 10g of Prunier caviar.",
    foodType: "Classics",
    restaurant: "Caviar House",
    price: "2900"
  }];

  resetMenu(menu);
  return menu;
}

module.exports = {
  northMenu: northMenu,
  southMenu: southMenu
}
<|endoftext|>


<commit_before>Blend.defineClass('Blend.layout.container.Box', {
    extend: 'Blend.layout.container.Layout',
    requires: [
        'Blend.layout.utils.Box',
        'Blend.ui.Splitter'
    ],
    align: 'start',
    pack: 'start',
    margin: 0,
    layoutContext: null,
    direction: null,
    handler: null,
    render: function () {
        var me = this;
        /**
         * Remove splitters because we are going to render and include them
         * in the layout phase
         */
        me.removeSplitters();
        return me.callParent.apply(me, arguments);
    },
    performLayout: function (force) {
        var me = this, ctx;
        force = force || false;
        if (force) {
            me.layoutContext = null;
        }
        ctx = me.prepareLayout();
        me.createLayoutContext();
        me.handler(me.containerEl, me.getElements(), ctx, me.layoutContext);
        // call the parent to layout the children
        me.callParent.apply(me, arguments);
    },
    createLayoutContext: function () {
        var me = this;
        if (!me.layoutContext) {
            me.layoutContext = {
                pack: me._hasSplitter ? 'center' : me.pack,
                align: me._hasSplitter ? 'stretch' : me.align,
                margin: me.margin,
                direction: me.direction,
                bounds: Blend.Element.getBounds(me.containerEl),
                layoutHandler: function (el, bounds, idx) {
                    /**
                     * Only call the sizeChanged event if the bounds of the
                     * view is changed. This will avoid unnecessary layout flows
                     */
                    var view = me.view.items[idx];
                    var vbounds = Blend.Element.getBounds(view.getElement());
                    Blend.Style.set(el, bounds);
                    if (!me.boundsEqual(vbounds, bounds)) {
                        me.view.items[idx].fireEvent('sizeChanged', bounds.width, bounds.height);
                    }
                }
            };
        }
    },
    prepareLayout: function () {
        var me = this, result = [];
        if (me._hasSplitter && !me._didSplitters) {
            me.recreateSplitters();
        }
        Blend.foreach(me.view.items, function (view) {
            result.push(view.getLayoutContext());
        });
        return result;
    },
    recreateSplitters: function () {
        var me = this,
                splitter,
                isFirst, isLast,
                isLastSplitter, // is the last view a splitter?
                views = [],
                docFrag = Blend.createDocumentFragment(),
                last = me.view.items.length - 1;
        Blend.foreach(me.view.items, function (view, index) {
            if (view.split && view.split === true) {
                isFirst = (index === 0);
                isLast = index === last;
                if (isFirst && !isLast) {
                    splitter = me.createNewSplitter();
                    views.push(view);
                    views.push(splitter);
                    docFrag.appendChild(view.getElement());
                    docFrag.appendChild(splitter.getElement());
                    isLastSplitter = true;
                } else if (!isLastSplitter && !(isFirst && isLast)) {
                    // if the last item is not an splitter and it is not the only item!
                    splitter = me.createNewSplitter();
                    views.push(splitter);
                    views.push(view);
                    docFrag.appendChild(splitter.getElement());
                    docFrag.appendChild(view.getElement());
                    isLastSplitter = false;
                } else {
                    views.push(view);
                    docFrag.appendChild(view.getElement());
                    isLastSplitter = false;
                }
            } else {
                views.push(view);
                docFrag.appendChild(view.getElement());
                isLastSplitter = false;
            }
        });
        me.containerEl.appendChild(docFrag);
        me.view.items = views;
        me._didSplitters = true;
        return;
    },
    createNewSplitter: function () {
        var me = this, splitter;
        splitter = me.createItemView({
            type: 'ui.splitter',
            reference: 'splitter' + (me.splitCnt++),
        });
        me.createItemLayoutContext(splitter);
        return splitter;
    },
    /**
     * @private
     * Removes or destroy existing splitters from the views items. Also
     */
    removeSplitters: function () {
        var me = this, itm, sp;
        for (var a = 0; a < me.view.items.length; a++) {
            sp = false;
            itm = me.view.items[a];
            if (me.isSplitter(itm)) {
                sp = true;
            } else if (Blend.isObject(itm) && Blend.isInstanceOf(itm, Blend.ui.Splitter)) {
                Blend.Element.destroy(itm.getElement());
                sp = true;
            }
            if (sp) {
                me.view.items.splice(a, 1);
                a = 0; // start the loop again
            }
        }
    },
    isSplitter: function (itm) {
        if (
                (Blend.isString(itm) && itm.indexOf('splitter')) ||
                (Blend.isObject(itm) && itm.type && itm.type === 'ui.splitter')) {
            return true;
        } else {
            return false;
        }
    }
});<commit_msg>Added splitter binder<commit_after>Blend.defineClass('Blend.layout.container.Box', {
    extend: 'Blend.layout.container.Layout',
    requires: [
        'Blend.layout.utils.Box',
        'Blend.ui.Splitter'
    ],
    align: 'start',
    pack: 'start',
    margin: 0,
    layoutContext: null,
    direction: null,
    handler: null,
    render: function () {
        var me = this;
        /**
         * Remove splitters because we are going to render and include them
         * in the layout phase
         */
        me.removeSplitters();
        return me.callParent.apply(me, arguments);
    },
    performLayout: function (force) {
        var me = this, ctx;
        ctx = me.prepareLayout(force);
        me.createLayoutContext();
        me.handler(me.containerEl, me.getElements(), ctx, me.layoutContext);
        // call the parent to layout the children
        me.callParent.apply(me, arguments);
    },
    createLayoutContext: function (force) {
        var me = this;
        me.layoutContext = force ? null : me.layoutContext;
        if (!me.layoutContext) {
            me.layoutContext = {
                pack: me._hasSplitter ? 'center' : me.pack,
                align: me._hasSplitter ? 'stretch' : me.align,
                margin: me.margin,
                direction: me.direction,
                bounds: Blend.Element.getBounds(me.containerEl),
                layoutHandler: function (el, bounds, idx) {
                    /**
                     * Only call the sizeChanged event if the bounds of the
                     * view is changed. This will avoid unnecessary layout flows
                     */
                    var view = me.view.items[idx];
                    var vbounds = Blend.Element.getBounds(view.getElement());
                    Blend.Style.set(el, bounds);
                    if (!me.boundsEqual(vbounds, bounds)) {
                        me.view.items[idx].fireEvent('sizeChanged', bounds.width, bounds.height);
                    }
                }
            };
        }
    },
    prepareLayout: function () {
        var me = this, result = [];
        if (me._hasSplitter && !me._didSplitters) {
            me.recreateSplitters();
        }
        Blend.foreach(me.view.items, function (view) {
            result.push(view.getLayoutContext());
        });
        return result;
    },
    recreateSplitters: function () {
        var me = this,
                splitter,
                isFirst, isLast,
                isLastSplitter, // is the last view a splitter?
                views = [],
                docFrag = Blend.createDocumentFragment(),
                last = me.view.items.length - 1;
        Blend.foreach(me.view.items, function (view, index) {
            if (view.split && view.split === true) {
                isFirst = (index === 0);
                isLast = index === last;
                if (isFirst && !isLast) {
                    splitter = me.createNewSplitter();
                    views.push(view);
                    views.push(splitter);
                    docFrag.appendChild(view.getElement());
                    docFrag.appendChild(splitter.getElement());
                    isLastSplitter = true;
                } else if (!isLastSplitter && !(isFirst && isLast)) {
                    // if the last item is not an splitter and it is not the only item!
                    splitter = me.createNewSplitter();
                    views.push(splitter);
                    views.push(view);
                    docFrag.appendChild(splitter.getElement());
                    docFrag.appendChild(view.getElement());
                    isLastSplitter = false;
                } else {
                    views.push(view);
                    docFrag.appendChild(view.getElement());
                    isLastSplitter = false;
                }
            } else {
                views.push(view);
                docFrag.appendChild(view.getElement());
                isLastSplitter = false;
            }
        });
        me.containerEl.appendChild(docFrag);
        me.view.items = views;
        me._didSplitters = true;
        me.bindSplitters();
        return;
    },
    createNewSplitter: function () {
        var me = this, splitter;
        splitter = me.createItemView({
            type: 'ui.splitter',
            reference: 'splitter' + (me.splitCnt++),
        });
        me.createItemLayoutContext(splitter);
        return splitter;
    },
    /**
     * @private
     * Binds the splitters to their corresponding components
     */
    bindSplitters: function () {
        var me = this, items = me.view.items;
        Blend.foreach(items, function (cmp, index) {
            if (Blend.isInstanceOf(cmp, Blend.ui.Splitter)) {
                cmp.getAComponent = function () {
                    return items[index - 1];
                };
                cmp.getBComponent = function () {
                    return items[index + 1];
                };
            }
        });
    },
    /**
     * @private
     * Removes or destroy existing splitters from the views items. Also
     */
    removeSplitters: function () {
        var me = this, itm, sp;
        for (var a = 0; a < me.view.items.length; a++) {
            sp = false;
            itm = me.view.items[a];
            if (me.isSplitter(itm)) {
                sp = true;
            } else if (Blend.isObject(itm) && Blend.isInstanceOf(itm, Blend.ui.Splitter)) {
                Blend.Element.destroy(itm.getElement());
                sp = true;
            }
            if (sp) {
                me.view.items.splice(a, 1);
                a = 0; // start the loop again
            }
        }
    },
    isSplitter: function (itm) {
        if ((Blend.isString(itm) && itm.indexOf('splitter')) ||
                (Blend.isObject(itm) && itm.type && itm.type === 'ui.splitter')) {
            return true;
        } else {
            return false;
        }
    }
});<|endoftext|>


<commit_before>'use strict';

describe('Module: tubular', () => {
  beforeEach(() => {
    module('tubular.models');
    module('tubular.services');
    module('tubular.directives');
    module('tubular');
  });

  it('should load successfully', () => {});

  xit('should have version', () => {
    // How?
    inject(tubular => {
      expect(tubular.info().version).toBeDefined();
    });
  });
});
<commit_msg>fixing tubular module spec<commit_after>'use strict';

describe('Module: tubular', () => {

  it('should have version', () => {
      expect(angular.module('tubular').info().version).toBeDefined();
  });
});
<|endoftext|>


<commit_before>import tv4 from 'tv4';

import FormUtil from './FormUtil';
import Util from './Util';

function filteredPaths(combinedPath) {
  return combinedPath.split('/').filter(function (path) {
    return path.length > 0;
  });
}

function setDefinitionValue(thingToSet, definition, renderRemove) {
  let {path, value} = thingToSet;
  let definitionToSet = SchemaFormUtil.getDefinitionFromPath(definition, path);

  if (Array.isArray(value) && value.length !== 0) {
    let prop = path[path.length - 1];

    let firstIndex = 0;
    definitionToSet.definition.find(function (field, i) {
      if (FormUtil.isFieldInstanceOfProp(prop, field)) {
        firstIndex = i;
        return true;
      }

      return false;
    });

    FormUtil.removePropID(definitionToSet.definition, prop);
    value.forEach(function (item) {
      let propID = Util.uniqueID(prop);
      let instanceDefinition = FormUtil.getMultipleFieldDefinition(
        prop,
        propID,
        definitionToSet.definition.itemShapes[prop].definition,
        item
      );
      instanceDefinition.push(
        renderRemove(definitionToSet.definition, prop, propID)
      );
      definitionToSet.definition.splice(firstIndex, 0, instanceDefinition);
      firstIndex++;
    });
  }

  definitionToSet.value = value;
  definitionToSet.startValue = value;
}

function getThingsToSet(model, path) {
  path = path || [];
  let thingsToSet = [];

  if (Array.isArray(model)) {
    thingsToSet.push({
      path,
      value: model
    });

    return thingsToSet;
  }

  Object.keys(model).forEach(function (key) {
    let pathCopy = path.concat([key]);
    let value = model[key];

    if (typeof value === 'object' && value !== null) {
      thingsToSet = thingsToSet.concat(getThingsToSet(value, pathCopy));
    } else if (value != null) {
      thingsToSet.push({
        path: pathCopy,
        value
      });
    }
  });

  return thingsToSet;
}

function processValue(value, valueType) {
  if (valueType === 'integer' || valueType === 'number') {
    if (value !== null && value !== '') {
      let parsedNumber = Number(value);
      if (isNaN(parsedNumber)) {
        return value;
      }

      return parsedNumber;
    }
  }

  if (valueType === 'boolean' && value == null) {
    return false;
  }

  if (value == null || value === '') {
    return null;
  }

  if (valueType === 'array' && typeof value === 'string') {
    return value.split(',')
      .map(function (val) { return val.trim(); })
      .filter(function (val) { return val !== ''; });
  }

  return value;
}

let SchemaFormUtil = {
  getDefinitionFromPath(definition, paths) {
    if (definition[paths[0]]) {
      definition = definition[paths[0]];
      paths = paths.slice(1);
    }

    paths.forEach(function (path) {
      if (definition.definition == null) {
        return;
      }

      let nextDefinition = Array.prototype
        .concat.apply([], definition.definition).find(
          function (definitionField) {
            return definitionField.name === path
              || definitionField.title === path;
          }
        );

      if (nextDefinition) {
        definition = nextDefinition;
      }
    });

    return definition;
  },

  processFormModel(model, multipleDefinition, prevPath = []) {
    let newModel = {};

    Object.keys(model).forEach(function (key) {
      let value = model[key];
      let path = prevPath.concat([key]);

      // Nested model.
      if (typeof value === 'object' && value !== null) {
        newModel[key] = SchemaFormUtil.processFormModel(
          value, multipleDefinition, path
        );
        return;
      }

      let definition = SchemaFormUtil.getDefinitionFromPath(
        multipleDefinition, path
      );
      if (definition == null) {
        return;
      }

      let {isRequired, valueType} = definition;
      let processedValue = processValue(value, valueType);
      if (processedValue != null || isRequired) {
        newModel[key] = processedValue;
      }
    });

    return FormUtil.modelToCombinedProps(newModel);
  },

  mergeModelIntoDefinition(model, definition, renderRemove) {
    let thingsToSet = getThingsToSet(model);

    thingsToSet.forEach(function (thingToSet) {
      setDefinitionValue(thingToSet, definition, renderRemove);
    });
  },

  parseTV4Error(tv4Error) {
    let errorObj = {
      message: tv4Error.message,
      path: filteredPaths(tv4Error.dataPath)
    };

    let schemaPath = tv4Error.schemaPath.split('/');

    if (tv4Error.code === 302) {
      errorObj.path.push(tv4Error.params.key);
    }

    if (schemaPath[schemaPath.length - 2] === 'items') {
      errorObj.path.pop();
    }

    return errorObj;
  },

  validateModelWithSchema(model, schema) {
    let result = tv4.validateMultiple(model, schema);

    if (result == null || result.valid) {
      return [];
    }

    return result.errors.map(function (error) {
      return SchemaFormUtil.parseTV4Error(error);
    });
  }
};

module.exports = SchemaFormUtil;
<commit_msg>Combine line<commit_after>import tv4 from 'tv4';

import FormUtil from './FormUtil';
import Util from './Util';

function filteredPaths(combinedPath) {
  return combinedPath.split('/').filter(function (path) {
    return path.length > 0;
  });
}

function setDefinitionValue(thingToSet, definition, renderRemove) {
  let {path, value} = thingToSet;
  let definitionToSet = SchemaFormUtil.getDefinitionFromPath(definition, path);

  if (Array.isArray(value) && value.length !== 0) {
    let prop = path[path.length - 1];

    let firstIndex = 0;
    definitionToSet.definition.find(function (field, i) {
      if (FormUtil.isFieldInstanceOfProp(prop, field)) {
        firstIndex = i;
        return true;
      }

      return false;
    });

    FormUtil.removePropID(definitionToSet.definition, prop);
    value.forEach(function (item) {
      let propID = Util.uniqueID(prop);
      let instanceDefinition = FormUtil.getMultipleFieldDefinition(
        prop,
        propID,
        definitionToSet.definition.itemShapes[prop].definition,
        item
      );
      instanceDefinition.push(
        renderRemove(definitionToSet.definition, prop, propID)
      );
      definitionToSet.definition.splice(firstIndex++, 0, instanceDefinition);
    });
  }

  definitionToSet.value = value;
  definitionToSet.startValue = value;
}

function getThingsToSet(model, path) {
  path = path || [];
  let thingsToSet = [];

  if (Array.isArray(model)) {
    thingsToSet.push({
      path,
      value: model
    });

    return thingsToSet;
  }

  Object.keys(model).forEach(function (key) {
    let pathCopy = path.concat([key]);
    let value = model[key];

    if (typeof value === 'object' && value !== null) {
      thingsToSet = thingsToSet.concat(getThingsToSet(value, pathCopy));
    } else if (value != null) {
      thingsToSet.push({
        path: pathCopy,
        value
      });
    }
  });

  return thingsToSet;
}

function processValue(value, valueType) {
  if (valueType === 'integer' || valueType === 'number') {
    if (value !== null && value !== '') {
      let parsedNumber = Number(value);
      if (isNaN(parsedNumber)) {
        return value;
      }

      return parsedNumber;
    }
  }

  if (valueType === 'boolean' && value == null) {
    return false;
  }

  if (value == null || value === '') {
    return null;
  }

  if (valueType === 'array' && typeof value === 'string') {
    return value.split(',')
      .map(function (val) { return val.trim(); })
      .filter(function (val) { return val !== ''; });
  }

  return value;
}

let SchemaFormUtil = {
  getDefinitionFromPath(definition, paths) {
    if (definition[paths[0]]) {
      definition = definition[paths[0]];
      paths = paths.slice(1);
    }

    paths.forEach(function (path) {
      if (definition.definition == null) {
        return;
      }

      let nextDefinition = Array.prototype
        .concat.apply([], definition.definition).find(
          function (definitionField) {
            return definitionField.name === path
              || definitionField.title === path;
          }
        );

      if (nextDefinition) {
        definition = nextDefinition;
      }
    });

    return definition;
  },

  processFormModel(model, multipleDefinition, prevPath = []) {
    let newModel = {};

    Object.keys(model).forEach(function (key) {
      let value = model[key];
      let path = prevPath.concat([key]);

      // Nested model.
      if (typeof value === 'object' && value !== null) {
        newModel[key] = SchemaFormUtil.processFormModel(
          value, multipleDefinition, path
        );
        return;
      }

      let definition = SchemaFormUtil.getDefinitionFromPath(
        multipleDefinition, path
      );
      if (definition == null) {
        return;
      }

      let {isRequired, valueType} = definition;
      let processedValue = processValue(value, valueType);
      if (processedValue != null || isRequired) {
        newModel[key] = processedValue;
      }
    });

    return FormUtil.modelToCombinedProps(newModel);
  },

  mergeModelIntoDefinition(model, definition, renderRemove) {
    let thingsToSet = getThingsToSet(model);

    thingsToSet.forEach(function (thingToSet) {
      setDefinitionValue(thingToSet, definition, renderRemove);
    });
  },

  parseTV4Error(tv4Error) {
    let errorObj = {
      message: tv4Error.message,
      path: filteredPaths(tv4Error.dataPath)
    };

    let schemaPath = tv4Error.schemaPath.split('/');

    if (tv4Error.code === 302) {
      errorObj.path.push(tv4Error.params.key);
    }

    if (schemaPath[schemaPath.length - 2] === 'items') {
      errorObj.path.pop();
    }

    return errorObj;
  },

  validateModelWithSchema(model, schema) {
    let result = tv4.validateMultiple(model, schema);

    if (result == null || result.valid) {
      return [];
    }

    return result.errors.map(function (error) {
      return SchemaFormUtil.parseTV4Error(error);
    });
  }
};

module.exports = SchemaFormUtil;
<|endoftext|>


<commit_before>/*jslint nomen: true */
/*globals define: true */

define(function (require) {
  'use strict';
  var $ = require('jquery');
  var _ = require('lib/lodash');
  var Autolinker = require('lib/autolinker');
  var Backbone = require('backbone');

  // LocalData
  var settings = require('settings');
  var util = require('util');

  // Templates
  var template = require('text!templates/responses/item.html');

  var ResponseView = Backbone.View.extend({
    className: 'response',

    template: _.template(template, {
      imports: {
        autolinker: new Autolinker() // Create links from urls in text fields
      }
    }),

    responseEdits: {}, // changes to make to the responses object.

    events: {
      // Deleting
      'click .action-show-confirm': 'confirm',
      'click .action-delete': 'destroy',
      'click .cancel': 'cancel',

      // Editing
      'click .action-show-edit': 'edit',
      'click .action-save-edit': 'save',
      'click .action-cancel-edit': 'cancelEdit',
      'change .edit': 'questionEdited',

      // Flagging
      'click .action-flag': 'flag',
      'click .action-accept': 'accept'
    },

    initialize: function(options) {
      this.listenTo(this.model, "sync", this.render);
      this.listenTo(this.model, "destroy", this.remove);

      this.surveyOptions = options.surveyOptions || {};
      this.exploration = options.exploration;
      this.labels = options.labels;
      this.forms = options.forms;
    },

    makeAnswerLabels: function() {
      var form = this.forms.getFlattenedForm();
      var questions = {};
      _.each(form, function(question, key) {
        questions[key] = {};
        _.each(question.answers, function(answer) {
          questions[key][answer.value] = answer.text;
        });
      });
      return questions;
    },

    render: function() {
      var $el = $(this.el);

      // If this is meant for anonymous consumption, then ignore the fact
      // that the user might be logged in through the primary dashboard.
      // TODO: If the entire interface is meant for anonymous consumption,
      // then we should not even be asking the API for the user's info.
      if (this.surveyOptions.anonymous) {
        this.surveyOptions.loggedIn = false;
      } else {
        this.surveyOptions.loggedIn = settings.user && settings.user.isLoggedIn();
      }

      var responses = this.model.get('responses') || {};
      var form = this.forms.getMostRecentForm().getFormV2();
      var fields = [];
      var processed = {};

      // Go through the questions in the latest form and map the question-answer
      // slug pairs to text pairs.
      _.forEach(form.questions, function (question) {
        var valueSlug = responses[question.slug];
        var value;
        var key;

        // Don't display a question/answer pair twice.
        // Don't process group questions
        if (!processed[question.slug] && question.type !== 'group') {
          key = question.text;

          // If the value slug matches an answer, then we can also use the text
          // for that answer.
          value = _.pluck(_.where(question.answers, { value: valueSlug}), 'text')[0];
          if (!value) {
            value = valueSlug;
          }

          // Set up the answers with a slug, if they don't already have one
          var answers = [];
          _.each(question.answers, function(answer) {
            if (!_.has(answer, 'slug')) {
              answer.slug = answer.value;
            }
            answers.push(answer);
          });

          if(question.type === 'checkbox') {
            answers.push({
              slug: 'yes',
              text: 'Yes'
            });
          }

          fields.push({
            question: key,
            questionSlug: question.slug,
            type: question.type,
            answer: value,
            answerSlug: valueSlug,
            answerOptions: answers,
            parentName: question.parentName
          });

          processed[question.slug] = true;
        } else if (question.type === 'text') {
          value = _.pluck(_.where(question.answers, { value: valueSlug}), 'text')[0];

          fields.push({
            question: question.text,
            questionSlug: question.slug,
            type: question.type,
            answer: value
          });
        }
      });

      // Go through the full set of question-answer slug pairs in this entry and
      // add any that didn't map to the form.
      _.forEach(_.keys(responses).reverse(), function (key) {
        if (!processed[key]) {
          fields.push({
            question: key,
            questionSlug: key,
            answer: responses[key]
          });
        }
      });

      var r = this.model.toJSON();
      if (!r.responses) {
        r.responses = {};
      }

      var options = {
        r: r,
        fields: fields,
        surveyOptions: this.surveyOptions,
        exploration: this.exploration
      };

      $el.html(this.template(options));
      return this;
    },

    // Deleting
    confirm: function(event) {
      event.preventDefault();
      this.$('.action-show-confirm').hide();
      this.$('.confirm-delete').show();
    },

    cancel: function(event) {
      event.preventDefault();
      this.$('.confirm-delete').hide();
      this.$('.action-show-confirm').show();
    },

    destroy: function(event) {
      event.preventDefault();

      function success(model, repsonse) {
      }

      function error(model, xhr, options) {
        console.log("Error destroying", xhr, options);
        console.log($('.error'));
        $('.error').show();
      }

      this.model.destroy({
        success: success,
        error: error,
        // Wait for the response before issuing the destroy event, otherwise we
        // have a race condition.
        wait: true
      });

      util.track('survey.response.delete');
    },

    // Editing
    edit: function(event) {
      event.preventDefault();
      util.track('survey.response.edit');

      this.$('.value').hide();
      this.$('.action-show-edit').hide();

      this.$('.editanswers').show();
      this.$('.answers').hide();
      this.$('.action-save-edit').show();
      this.$('.action-cancel-edit').show();
    },

    questionEdited: function(event) {
      var question = $(event.target).attr('data-question');
      var answer = $(event.target).val();

      var form = this.forms.getFlattenedForm();
      var originalQuestionObject = form[question];

      if (answer === 'no response' || answer === '') {
        delete this.model.attributes.responses[question];
        return;
      }

      this.model.attributes.responses[question] = answer;
    },

    cancelEdit: function(event) {
      event.preventDefault();
      util.track('survey.response.edit.cancel');

      // Show the values and edit button
      this.$('.value').show();
      this.$('.action-show-edit').show();

      // Hide the form and save / cancel buttons
      this.$('.editanswers').hide();
      this.$('.answers').show();
      this.$('.action-save-edit').hide();
      this.$('.action-cancel-edit').hide();

      // Fetch the attributes to make sure we have the latest version.
      this.model.fetch();
    },

    save: function(event) {
      event.preventDefault();
      util.track('survey.response.edit.save');

      this.model.save(this.model.attributes, {
        wait: true, // wait until sync to update attributes
        success: function (event) {
          // We need to fetch the model because put resets the local
          // attributes.
          this.model.fetch({ reset: true });
        }.bind(this)
      });
    },

    // Flagging
    flag: function(event) {
      event.preventDefault();
      util.track('survey.response.flag');

      this.model.save({
        responses: {
          reviewed: 'flagged'
        }
      }, {
        patch: true,
        wait: true, // wait until sync to update attributes
        success: function (event) {
          // We need to fetch the model because patch resets the local
          // attributes.
          this.model.fetch({ reset: true });
        }.bind(this)
      });
    },

    accept: function(event) {
      event.preventDefault();
      util.track('survey.response.accept');

      this.model.save({
        responses: {
          reviewed: 'accepted'
        }
      }, {
        patch: true,
        wait: true,
        success: function (event) {
          this.model.fetch({ reset: true });
        }.bind(this)
      });
    }
  });

  return ResponseView;
});
<commit_msg>Fix editing answers when there is no response data<commit_after>/*jslint nomen: true */
/*globals define: true */

define(function (require) {
  'use strict';
  var $ = require('jquery');
  var _ = require('lib/lodash');
  var Autolinker = require('lib/autolinker');
  var Backbone = require('backbone');

  // LocalData
  var settings = require('settings');
  var util = require('util');

  // Templates
  var template = require('text!templates/responses/item.html');

  var ResponseView = Backbone.View.extend({
    className: 'response',

    template: _.template(template, {
      imports: {
        autolinker: new Autolinker() // Create links from urls in text fields
      }
    }),

    responseEdits: {}, // changes to make to the responses object.

    events: {
      // Deleting
      'click .action-show-confirm': 'confirm',
      'click .action-delete': 'destroy',
      'click .cancel': 'cancel',

      // Editing
      'click .action-show-edit': 'edit',
      'click .action-save-edit': 'save',
      'click .action-cancel-edit': 'cancelEdit',
      'change .edit': 'questionEdited',

      // Flagging
      'click .action-flag': 'flag',
      'click .action-accept': 'accept'
    },

    initialize: function(options) {
      this.listenTo(this.model, "sync", this.render);
      this.listenTo(this.model, "destroy", this.remove);

      this.surveyOptions = options.surveyOptions || {};
      this.exploration = options.exploration;
      this.labels = options.labels;
      this.forms = options.forms;
    },

    makeAnswerLabels: function() {
      var form = this.forms.getFlattenedForm();
      var questions = {};
      _.each(form, function(question, key) {
        questions[key] = {};
        _.each(question.answers, function(answer) {
          questions[key][answer.value] = answer.text;
        });
      });
      return questions;
    },

    render: function() {
      var $el = $(this.el);

      // If this is meant for anonymous consumption, then ignore the fact
      // that the user might be logged in through the primary dashboard.
      // TODO: If the entire interface is meant for anonymous consumption,
      // then we should not even be asking the API for the user's info.
      if (this.surveyOptions.anonymous) {
        this.surveyOptions.loggedIn = false;
      } else {
        this.surveyOptions.loggedIn = settings.user && settings.user.isLoggedIn();
      }

      var responses = this.model.get('responses') || {};
      var form = this.forms.getMostRecentForm().getFormV2();
      var fields = [];
      var processed = {};

      // Go through the questions in the latest form and map the question-answer
      // slug pairs to text pairs.
      _.forEach(form.questions, function (question) {
        var valueSlug = responses[question.slug];
        var value;
        var key;

        // Don't display a question/answer pair twice.
        // Don't process group questions
        if (!processed[question.slug] && question.type !== 'group') {
          key = question.text;

          // If the value slug matches an answer, then we can also use the text
          // for that answer.
          value = _.pluck(_.where(question.answers, { value: valueSlug}), 'text')[0];
          if (!value) {
            value = valueSlug;
          }

          // Set up the answers with a slug, if they don't already have one
          var answers = [];
          _.each(question.answers, function(answer) {
            if (!_.has(answer, 'slug')) {
              answer.slug = answer.value;
            }
            answers.push(answer);
          });

          if(question.type === 'checkbox') {
            answers.push({
              slug: 'yes',
              text: 'Yes'
            });
          }

          fields.push({
            question: key,
            questionSlug: question.slug,
            type: question.type,
            answer: value,
            answerSlug: valueSlug,
            answerOptions: answers,
            parentName: question.parentName
          });

          processed[question.slug] = true;
        } else if (question.type === 'text') {
          value = _.pluck(_.where(question.answers, { value: valueSlug}), 'text')[0];

          fields.push({
            question: question.text,
            questionSlug: question.slug,
            type: question.type,
            answer: value
          });
        }
      });

      // Go through the full set of question-answer slug pairs in this entry and
      // add any that didn't map to the form.
      _.forEach(_.keys(responses).reverse(), function (key) {
        if (!processed[key]) {
          fields.push({
            question: key,
            questionSlug: key,
            answer: responses[key]
          });
        }
      });

      var r = this.model.toJSON();
      if (!r.responses) {
        r.responses = {};
      }

      var options = {
        r: r,
        fields: fields,
        surveyOptions: this.surveyOptions,
        exploration: this.exploration
      };

      $el.html(this.template(options));
      return this;
    },

    // Deleting
    confirm: function(event) {
      event.preventDefault();
      this.$('.action-show-confirm').hide();
      this.$('.confirm-delete').show();
    },

    cancel: function(event) {
      event.preventDefault();
      this.$('.confirm-delete').hide();
      this.$('.action-show-confirm').show();
    },

    destroy: function(event) {
      event.preventDefault();

      function success(model, repsonse) {
      }

      function error(model, xhr, options) {
        console.log("Error destroying", xhr, options);
        console.log($('.error'));
        $('.error').show();
      }

      this.model.destroy({
        success: success,
        error: error,
        // Wait for the response before issuing the destroy event, otherwise we
        // have a race condition.
        wait: true
      });

      util.track('survey.response.delete');
    },

    // Editing
    edit: function(event) {
      event.preventDefault();
      util.track('survey.response.edit');

      this.$('.value').hide();
      this.$('.action-show-edit').hide();

      this.$('.editanswers').show();
      this.$('.answers').hide();
      this.$('.action-save-edit').show();
      this.$('.action-cancel-edit').show();
    },

    questionEdited: function(event) {
      var question = $(event.target).attr('data-question');
      var answer = $(event.target).val();

      var form = this.forms.getFlattenedForm();
      var originalQuestionObject = form[question];

      if (answer === 'no response' || answer === '') {
        delete this.model.attributes.responses[question];
        return;
      }

      if (this.model.attributes.responses === null) {
        this.model.attributes.responses = {};
      }

      this.model.attributes.responses[question] = answer;
    },

    cancelEdit: function(event) {
      event.preventDefault();
      util.track('survey.response.edit.cancel');

      // Show the values and edit button
      this.$('.value').show();
      this.$('.action-show-edit').show();

      // Hide the form and save / cancel buttons
      this.$('.editanswers').hide();
      this.$('.answers').show();
      this.$('.action-save-edit').hide();
      this.$('.action-cancel-edit').hide();

      // Fetch the attributes to make sure we have the latest version.
      this.model.fetch();
    },

    save: function(event) {
      event.preventDefault();
      util.track('survey.response.edit.save');

      this.model.save(this.model.attributes, {
        wait: true, // wait until sync to update attributes
        success: function (event) {
          // We need to fetch the model because put resets the local
          // attributes.
          this.model.fetch({ reset: true });
        }.bind(this)
      });
    },

    // Flagging
    flag: function(event) {
      event.preventDefault();
      util.track('survey.response.flag');

      this.model.save({
        responses: {
          reviewed: 'flagged'
        }
      }, {
        patch: true,
        wait: true, // wait until sync to update attributes
        success: function (event) {
          // We need to fetch the model because patch resets the local
          // attributes.
          this.model.fetch({ reset: true });
        }.bind(this)
      });
    },

    accept: function(event) {
      event.preventDefault();
      util.track('survey.response.accept');

      this.model.save({
        responses: {
          reviewed: 'accepted'
        }
      }, {
        patch: true,
        wait: true,
        success: function (event) {
          this.model.fetch({ reset: true });
        }.bind(this)
      });
    }
  });

  return ResponseView;
});
<|endoftext|>


<commit_before>module.exports = function handleWebpackResult(stats, webpackWarningFilters) {
    if (stats) {
        try {
            assertFiltersValid(webpackWarningFilters);
            stats.compilation.warnings = filterWebpackWarnings(stats.compilation.warnings, webpackWarningFilters);
        } catch (error) {
            console.error(error);
        }
        stats = stats.toJson();
    }
    var nonFatalErrors = (stats && stats.errors) || [];
    var warnings = (stats && stats.warnings) || [];

    if (nonFatalErrors.length > 0 || warnings.length > 0) {
        console.error("Something went wrong while generating the test file");

        var byLine = function(str, cb) {
            str.split("\n").forEach(cb);
        };

        var logErrorsByLine = function (errors) {
            byLine(errors, function (err) {
                console.log("    ", err, typeof err);
            });
        };

        var pluralizeIf = function(noun, count) {
            return count > 1 ? noun + "s" : noun;
        };

        if (nonFatalErrors.length > 0) {
            console.log(pluralizeIf("Non-fatal error", nonFatalErrors.length) + ":");
            nonFatalErrors.forEach(logErrorsByLine);
        }

        if (warnings.length > 0) {
            console.log(pluralizeIf("Warning", warnings.length) + ":");
            warnings.forEach(logErrorsByLine);
        }

        console.error("Building finished, but the result might not work!");
    } else {
        console.log("Building succeeded!");
    }

};

/**
 * Checks the sanity of the webpackWarningFilters-configuration.
 */
function assertFiltersValid(webpackWarningFilters) {
    if (!webpackWarningFilters) {
        // No filters are configured. That's okay.
        return;
    }
    if (!Array.isArray(webpackWarningFilters)) {
        throw new TypeError("config.webpackWarningFilters must be an array.");
    }
    for (var i = 0; i < webpackWarningFilters.length; i++) {
        var webpackWarningFilter = webpackWarningFilters[i];
        if ("name" in webpackWarningFilter
            && "origin/rawRequest" in webpackWarningFilter
            && "dependencies/0/request" in webpackWarningFilter
            && Object.keys(webpackWarningFilter).length === 3 // That is, webpackWarningFilter contains no keys other than these three.
            && webpackWarningFilter.name === "ModuleNotFoundError"
        ) {
            // This filter config is supported. Proceed.
        } else {
            // This filter config isn't supported. Abort.
            throw new Error(
                "config.webpackWarningFilters[" + i + "] must be an object similar to {\n" +
                "   'name': 'ModuleNotFoundError',\n" +
                "   'origin/rawRequest': 'imports?process=>undefined!when',\n" +
                "   'dependencies/0/request': 'vertx'\n" +
                "}."
            );
        }
    }
}

function filterWebpackWarnings(unfilteredWarnings, webpackWarningFilters) {
    if (!webpackWarningFilters) {
        // No filters are configured. Use the complete, unfiltered list of warnings.
        return unfilteredWarnings;
    }

    // There's at least one filter and all filter configs are supported. Do the actual filtering.
    var filteredWarnings = unfilteredWarnings.filter(function filterWarning(warning, index, unfilteredWarnings) {
        if (warning.name !== "ModuleNotFoundError") {
            // filterWebpackWarnings only supports filtering of ModuleNotFoundError-warnings.
            // This is some other kind of warning. Leave it in the list, so that it IS shown in Jester's output.
            return true;
        }

        for (var i = 0; i < webpackWarningFilters.length; i++) {
            var webpackWarningFilter = webpackWarningFilters[i];
            if (
                warning.origin.rawRequest === webpackWarningFilter["origin/rawRequest"]
                && warning.dependencies.length > 0
                && warning.dependencies[0].request === webpackWarningFilter["dependencies/0/request"]
            ) {
                // This warning matches one of the filters.
                // Remove it from the list, so that it is NOT shown in Jester's output.
                return false;
            }
        }

        // This warning matches none of the filters.
        // Leave it in the list, so that it IS shown in Jester's output.
        return true;
    });

    return filteredWarnings;
}
<commit_msg>The filter-config check now returns a boolean and logs to the console instead of throwing exceptions.<commit_after>module.exports = function handleWebpackResult(stats, webpackWarningFilters) {
    if (stats) {
        if (filterConfigIsValid(webpackWarningFilters)) {
            stats.compilation.warnings = filterWebpackWarnings(stats.compilation.warnings, webpackWarningFilters);
        }
        stats = stats.toJson();
    }
    var nonFatalErrors = (stats && stats.errors) || [];
    var warnings = (stats && stats.warnings) || [];

    if (nonFatalErrors.length > 0 || warnings.length > 0) {
        console.error("Something went wrong while generating the test file");

        var byLine = function(str, cb) {
            str.split("\n").forEach(cb);
        };

        var logErrorsByLine = function (errors) {
            byLine(errors, function (err) {
                console.log("    ", err, typeof err);
            });
        };

        var pluralizeIf = function(noun, count) {
            return count > 1 ? noun + "s" : noun;
        };

        if (nonFatalErrors.length > 0) {
            console.log(pluralizeIf("Non-fatal error", nonFatalErrors.length) + ":");
            nonFatalErrors.forEach(logErrorsByLine);
        }

        if (warnings.length > 0) {
            console.log(pluralizeIf("Warning", warnings.length) + ":");
            warnings.forEach(logErrorsByLine);
        }

        console.error("Building finished, but the result might not work!");
    } else {
        console.log("Building succeeded!");
    }

};

/**
 * Checks the sanity of the webpackWarningFilters-configuration.
 */
function filterConfigIsValid(webpackWarningFilters) {
    var filterConfigIsValid = true;
    if (webpackWarningFilters) {
        if (Array.isArray(webpackWarningFilters)) {
            for (var i = 0; i < webpackWarningFilters.length; i++) {
                var webpackWarningFilter = webpackWarningFilters[i];
                if ("name" in webpackWarningFilter
                    && "origin/rawRequest" in webpackWarningFilter
                    && "dependencies/0/request" in webpackWarningFilter
                    && Object.keys(webpackWarningFilter).length === 3 // That is, webpackWarningFilter contains no keys other than these three.
                    && webpackWarningFilter.name === "ModuleNotFoundError"
                ) {
                    // This filter config is supported. Proceed.
                } else {
                    // This filter config isn't supported. Abort.
                    console.warn(
                        "config.webpackWarningFilters[" + i + "] must be an object similar to {\n" +
                        "   'name': 'ModuleNotFoundError',\n" +
                        "   'origin/rawRequest': 'imports?process=>undefined!when',\n" +
                        "   'dependencies/0/request': 'vertx'\n" +
                        "}."
                    );
                    filterConfigIsValid = false;
                }
            }
        } else {
            console.warn("config.webpackWarningFilters must be an array.");
            filterConfigIsValid = false;
        }
    } else {
        // No filters are configured. That's okay.
        filterConfigIsValid = true;
    }
    return filterConfigIsValid;
}

function filterWebpackWarnings(unfilteredWarnings, webpackWarningFilters) {
    if (!webpackWarningFilters) {
        // No filters are configured. Use the complete, unfiltered list of warnings.
        return unfilteredWarnings;
    }

    // There's at least one filter and all filter configs are supported. Do the actual filtering.
    var filteredWarnings = unfilteredWarnings.filter(function filterWarning(warning, index, unfilteredWarnings) {
        if (warning.name !== "ModuleNotFoundError") {
            // filterWebpackWarnings only supports filtering of ModuleNotFoundError-warnings.
            // This is some other kind of warning. Leave it in the list, so that it IS shown in Jester's output.
            return true;
        }

        for (var i = 0; i < webpackWarningFilters.length; i++) {
            var webpackWarningFilter = webpackWarningFilters[i];
            if (
                warning.origin.rawRequest === webpackWarningFilter["origin/rawRequest"]
                && warning.dependencies.length > 0
                && warning.dependencies[0].request === webpackWarningFilter["dependencies/0/request"]
            ) {
                // This warning matches one of the filters.
                // Remove it from the list, so that it is NOT shown in Jester's output.
                return false;
            }
        }

        // This warning matches none of the filters.
        // Leave it in the list, so that it IS shown in Jester's output.
        return true;
    });

    return filteredWarnings;
}
<|endoftext|>


<commit_before>/*
 * jsTreeGrid 0.97
 * http://jsorm.com/
 *
 * This plugin handles adding a grid to a tree to display additional data
 *
 * Dual licensed under the MIT and GPL licenses (same as jQuery):
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Created for Tufin www.tufin.com
 * Contributed to public source through the good offices of Tufin
 *
 * $Date: 2010-10-28 $
 * $Revision:  $
 */

/*jslint nomen:false */
/*global window, document, jQuery*/

(function ($) {
	var renderAWidth, renderATitle, htmlstripre, SPECIAL_TITLE = "_DATA_";
	/*jslint regexp:false */
	htmlstripre = /<\/?[^>]+>/gi;
	/*jslint regexp:true */

	renderAWidth = function(node,tree) {
		var depth, a = node.get(0).tagName.toLowerCase() === "a" ? node : node.children("a"),
		width = tree.data.grid.columns[0].width;
		// need to use a selector in jquery 1.4.4+
		depth = a.parentsUntil(tree.get_container().get(0).tagName+".jstree").filter("li").length;
		width = width - depth*18;
		a.css({width: width, "vertical-align": "top", "overflow":"hidden"});
	};
	renderATitle = function(node,t,tree) {
		var a = node.get(0).tagName.toLowerCase() === "a" ? node : node.children("a"), title, col = tree.data.grid.columns[0];
		// get the title
		title = "";
		if (col.title) {
			if (col.title === SPECIAL_TITLE) {
				title = tree.get_text(t);
			} else if (t.attr(col.title)) {
				title = t.attr(col.title);
			}
		}
		// strip out HTML
		title = title.replace(htmlstripre, '');
		if (title) {
			a.attr("title",title);
		}
	};

	$.jstree.plugin("grid", {
		__init : function () {
			var s = this._get_settings().grid || {};
			this.data.grid.columns = s.columns || [];
			this.data.grid.treeClass = "jstree-grid";
			this.data.grid.columnWidth = s.width;
			this.data.grid.defaultConf = {display: "inline-block"};
			this.data.grid.source = s.source || "attr";
			this.data.grid.isThemeroller = !!this.data.themeroller;

			if ($.browser.msie && parseInt($.browser.version.substr(0,1),10) < 8) {
				this.data.grid.defaultConf.display = "inline";
				this.data.grid.defaultConf.zoom = "1";
			}

			// set up the classes we need
			if (this.data.grid.isThemeroller) {
				$('').appendTo($("head"));
			} else {
				$('').appendTo($("head"));
			}

			this.get_container().bind("open_node.jstree create_node.jstree clean_node.jstree change_node.jstree", $.proxy(function (e, data) {
					var target = data && data.rslt && data.rslt.obj ? data.rslt.obj : e.target;
					target = $(target);
					this._prepare_grid(target);
				}, this))
			.bind("loaded.jstree", $.proxy(function (e) {
				this._prepare_headers();
				this._prepare_grid();
				this.get_container().trigger("loaded_grid.jstree");
				}, this))
			.bind("move_node.jstree",$.proxy(function(e,data){
				var node = data.rslt.o;
				renderAWidth(node,this);
				// check all the children, because we could drag a tree over
				node.find("li > a").each($.proxy(function(i,elm){
					renderAWidth($(elm),this);
				},this));

			},this));
			if (this.data.grid.isThemeroller) {
				this.get_container()
					.bind("select_node.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").addClass("ui-state-active");
					},this))
					.bind("deselect_node.jstree deselect_all.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").removeClass("ui-state-active");
					},this))
					.bind("hover_node.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").addClass("ui-state-hover");
					},this))
					.bind("dehover_node.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").removeClass("ui-state-hover");
					},this));
			}

		},
		__destroy : function() {
			var parent = this.data.grid.parent, container = this.get_container();
			container.detach();
			$("div.jstree-grid-wrapper",parent).remove();
			parent.append(container);
		},
		defaults : {
			width: 25
		},
		_fn : {
			_prepare_headers : function() {
				var header, i, cols = this.data.grid.columns || [], width, defaultWidth = this.data.grid.columnWidth, cl, val, margin, last, tr = this.data.grid.isThemeroller,
				cHeight, hHeight, container = this.get_container(), parent = container.parent(), hasHeaders = 0,
				conf = this.data.grid.defaultConf;
				// save the original parent so we can reparent on destroy
				this.data.grid.parent = parent;

				// set up the wrapper, if not already done
				header = this.data.grid.header || $("

").addClass((tr?"ui-widget-header ":"")+"jstree-grid-header");

				// create the headers
				for (i=0;i<cols.length;i++) {
					cl = cols[i].headerClass || "";
					val = cols[i].header || "";
					if (val) {hasHeaders = true;}
					width = cols[i].width || defaultWidth;
					width -= 2+8; // account for the borders and padding
					margin = i === 0 ? 3 : 0;
					last = $("

").css(conf).css({"margin-left": margin,"width":width, "padding": "1 3 2 5"}).addClass((tr?"ui-widget-header ":"")+"jstree-grid-header "+cl).text(val).appendTo(header);
				}
				last.addClass((tr?"ui-widget-header ":"")+"jstree-grid-header");
				// did we have any real columns?
				if (hasHeaders) {
					$("

").addClass("jstree-grid-wrapper").appendTo(parent).append(header).append(container);
					// save the offset of the div from the body
					this.data.grid.divOffset = header.parent().offset().left;
					this.data.grid.header = header;
				}

			},
			_prepare_grid : function(obj) {
				var c = this.data.grid.treeClass, _this = this, t, cols = this.data.grid.columns || [], width, s = this.data.grid.source, tr = this.data.grid.isThemeroller,
				defaultWidth = this.data.grid.columnWidth, divOffset = this.data.grid.divOffset, conf = this.data.grid.defaultConf;
				obj = !obj || obj === -1 ? this.get_container() : this._get_node(obj);
				// get our column definition
				obj.each(function () {
					var i, val, cl, wcl, a, last, valClass, wideValClass, span, paddingleft, title, isAlreadyGrid;
					t = $(this);

					// find the a children
					a = t.children("a");
					isAlreadyGrid = a.hasClass(c);

					if (a.length === 1) {
						a.addClass(c);
						renderAWidth(a,_this);
						renderATitle(a,t,_this);
						last = a;
						for (i=1;i<cols.length;i++) {
							// get the cellClass and the wideCellClass
							cl = cols[i].cellClass || "";
							wcl = cols[i].wideCellClass || "";

							// get the contents of the cell
							if (s === "attr") { val = cols[i].value && t.attr(cols[i].value) ? t.attr(cols[i].value) : "";
							} else if (s === "metadata") { val = cols[i].value && t.data(cols[i].value) ? t.data(cols[i].value) : ""; }

							// get the valueClass
							valClass = cols[i].valueClass && t.attr(cols[i].valueClass) ? t.attr(cols[i].valueClass) : "";
							if (valClass && cols[i].valueClassPrefix && cols[i].valueClassPrefix !== "") {
								valClass = cols[i].valueClassPrefix + valClass;
							}
							// get the wideValueClass
							wideValClass = cols[i].wideValueClass && t.attr(cols[i].wideValueClass) ? t.attr(cols[i].wideValueClass) : "";
							if (wideValClass && cols[i].wideValueClassPrefix && cols[i].wideValueClassPrefix !== "") {
								wideValClass = cols[i].wideValueClassPrefix + wideValClass;
							}
							// get the title
							title = cols[i].title && t.attr(cols[i].title) ? t.attr(cols[i].title) : "";
							// strip out HTML
							title = title.replace(htmlstripre, '');

							// get the width
							paddingleft = 7;
							width = cols[i].width || defaultWidth;
							width = width - paddingleft;

							last = isAlreadyGrid ? a.nextAll("div:eq("+(i-1)+")") : $("

").insertAfter(last);
							span = isAlreadyGrid ? last.children("span") : $("").appendTo(last);

							// create a span inside the div, so we can control what happens in the whole div versus inside just the text/background
							span.addClass(cl+" "+valClass).css("display","inline-block").html(val);
							last = last.css(conf).css({width: width,"padding-left":paddingleft+"px"}).addClass("jstree-grid-cell "+wcl+ " " + wideValClass + (tr?" ui-state-default":""));

							if (title) {
								span.attr("title",title);
							}

						}
						last.addClass("jstree-grid-cell-last"+(tr?" ui-state-default":""));
					}
				});
				if(obj.is("li")) { this._repair_state(obj); }
				else { obj.find("> ul > li").each(function () { _this._repair_state(this); }); }
			}
		}
		// need to do alternating background colors or borders
	});
}(jQuery));
<commit_msg>Added option to replace text values with images<commit_after>/*
 * jsTreeGrid 0.97
 * http://jsorm.com/
 *
 * This plugin handles adding a grid to a tree to display additional data
 *
 * Dual licensed under the MIT and GPL licenses (same as jQuery):
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Created for Tufin www.tufin.com
 * Contributed to public source through the good offices of Tufin
 *
 * $Date: 2010-10-28 $
 * $Revision:  $
 */

/*jslint nomen:false */
/*global window, document, jQuery*/

(function ($) {
	var renderAWidth, renderATitle, htmlstripre, SPECIAL_TITLE = "_DATA_";
	/*jslint regexp:false */
	htmlstripre = /<\/?[^>]+>/gi;
	/*jslint regexp:true */

	renderAWidth = function(node,tree) {
		var depth, a = node.get(0).tagName.toLowerCase() === "a" ? node : node.children("a"),
		width = tree.data.grid.columns[0].width;
		// need to use a selector in jquery 1.4.4+
		depth = a.parentsUntil(tree.get_container().get(0).tagName+".jstree").filter("li").length;
		width = width - depth*18;
		a.css({width: width, "vertical-align": "top", "overflow":"hidden"});
	};
	renderATitle = function(node,t,tree) {
		var a = node.get(0).tagName.toLowerCase() === "a" ? node : node.children("a"), title, col = tree.data.grid.columns[0];
		// get the title
		title = "";
		if (col.title) {
			if (col.title === SPECIAL_TITLE) {
				title = tree.get_text(t);
			} else if (t.attr(col.title)) {
				title = t.attr(col.title);
			}
		}
		// strip out HTML
		title = title.replace(htmlstripre, '');
		if (title) {
			a.attr("title",title);
		}
	};

	$.jstree.plugin("grid", {
		__init : function () {
			var s = this._get_settings().grid || {};
			this.data.grid.columns = s.columns || [];
			this.data.grid.treeClass = "jstree-grid";
			this.data.grid.columnWidth = s.width;
			this.data.grid.defaultConf = {display: "inline-block"};
			this.data.grid.source = s.source || "attr";
			this.data.grid.isThemeroller = !!this.data.themeroller;

			if ($.browser.msie && parseInt($.browser.version.substr(0,1),10) < 8) {
				this.data.grid.defaultConf.display = "inline";
				this.data.grid.defaultConf.zoom = "1";
			}

			// set up the classes we need
			if (this.data.grid.isThemeroller) {
				$('').appendTo($("head"));
			} else {
				$('').appendTo($("head"));
			}

			this.get_container().bind("open_node.jstree create_node.jstree clean_node.jstree change_node.jstree", $.proxy(function (e, data) {
					var target = data && data.rslt && data.rslt.obj ? data.rslt.obj : e.target;
					target = $(target);
					this._prepare_grid(target);
				}, this))
			.bind("loaded.jstree", $.proxy(function (e) {
				this._prepare_headers();
				this._prepare_grid();
				this.get_container().trigger("loaded_grid.jstree");
				}, this))
			.bind("move_node.jstree",$.proxy(function(e,data){
				var node = data.rslt.o;
				renderAWidth(node,this);
				// check all the children, because we could drag a tree over
				node.find("li > a").each($.proxy(function(i,elm){
					renderAWidth($(elm),this);
				},this));

			},this));
			if (this.data.grid.isThemeroller) {
				this.get_container()
					.bind("select_node.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").addClass("ui-state-active");
					},this))
					.bind("deselect_node.jstree deselect_all.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").removeClass("ui-state-active");
					},this))
					.bind("hover_node.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").addClass("ui-state-hover");
					},this))
					.bind("dehover_node.jstree",$.proxy(function(e,data){
						data.rslt.obj.children("a").nextAll("div").removeClass("ui-state-hover");
					},this));
			}

		},
		__destroy : function() {
			var parent = this.data.grid.parent, container = this.get_container();
			container.detach();
			$("div.jstree-grid-wrapper",parent).remove();
			parent.append(container);
		},
		defaults : {
			width: 25
		},
		_fn : {
			_prepare_headers : function() {
				var header, i, cols = this.data.grid.columns || [], width, defaultWidth = this.data.grid.columnWidth, cl, val, margin, last, tr = this.data.grid.isThemeroller,
				cHeight, hHeight, container = this.get_container(), parent = container.parent(), hasHeaders = 0,
				conf = this.data.grid.defaultConf;
				// save the original parent so we can reparent on destroy
				this.data.grid.parent = parent;

				// set up the wrapper, if not already done
				header = this.data.grid.header || $("

").addClass((tr?"ui-widget-header ":"")+"jstree-grid-header");

				// create the headers
				for (i=0;i<cols.length;i++) {
					cl = cols[i].headerClass || "";
					val = cols[i].header || "";
					if (val) {hasHeaders = true;}
					width = cols[i].width || defaultWidth;
					width -= 2+8; // account for the borders and padding
					margin = i === 0 ? 3 : 0;
					last = $("

").css(conf).css({"margin-left": margin,"width":width, "padding": "1 3 2 5"}).addClass((tr?"ui-widget-header ":"")+"jstree-grid-header "+cl).text(val).appendTo(header);
				}
				last.addClass((tr?"ui-widget-header ":"")+"jstree-grid-header");
				// did we have any real columns?
				if (hasHeaders) {
					$("

").addClass("jstree-grid-wrapper").appendTo(parent).append(header).append(container);
					// save the offset of the div from the body
					this.data.grid.divOffset = header.parent().offset().left;
					this.data.grid.header = header;
				}

			},
			_prepare_grid : function(obj) {
				var c = this.data.grid.treeClass, _this = this, t, cols = this.data.grid.columns || [], width, s = this.data.grid.source, tr = this.data.grid.isThemeroller, img
				defaultWidth = this.data.grid.columnWidth, divOffset = this.data.grid.divOffset, conf = this.data.grid.defaultConf;
				obj = !obj || obj === -1 ? this.get_container() : this._get_node(obj);
				// get our column definition
				obj.each(function () {
					var i, val, cl, wcl, a, last, valClass, wideValClass, span, paddingleft, title, isAlreadyGrid;
					t = $(this);

					// find the a children
					a = t.children("a");
					isAlreadyGrid = a.hasClass(c);

					if (a.length === 1) {
						a.addClass(c);
						renderAWidth(a,_this);
						renderATitle(a,t,_this);
						last = a;
						for (i=1;i<cols.length;i++) {
							// get the cellClass and the wideCellClass
							cl = cols[i].cellClass || "";
							wcl = cols[i].wideCellClass || "";

							// get the contents of the cell
							if (s === "attr") { val = cols[i].value && t.attr(cols[i].value) ? t.attr(cols[i].value) : "";
							} else if (s === "metadata") { val = cols[i].value && t.data(cols[i].value) ? t.data(cols[i].value) : ""; }

							// put images instead of text if needed
							if (cols[i].images) {
								img = cols[i].images[val] || cols[i].images["default"];
								val = img[0] === "*" ? '' : '';
							}

							// get the valueClass
							valClass = cols[i].valueClass && t.attr(cols[i].valueClass) ? t.attr(cols[i].valueClass) : "";
							if (valClass && cols[i].valueClassPrefix && cols[i].valueClassPrefix !== "") {
								valClass = cols[i].valueClassPrefix + valClass;
							}
							// get the wideValueClass
							wideValClass = cols[i].wideValueClass && t.attr(cols[i].wideValueClass) ? t.attr(cols[i].wideValueClass) : "";
							if (wideValClass && cols[i].wideValueClassPrefix && cols[i].wideValueClassPrefix !== "") {
								wideValClass = cols[i].wideValueClassPrefix + wideValClass;
							}
							// get the title
							title = cols[i].title && t.attr(cols[i].title) ? t.attr(cols[i].title) : "";
							// strip out HTML
							title = title.replace(htmlstripre, '');

							// get the width
							paddingleft = 7;
							width = cols[i].width || defaultWidth;
							width = width - paddingleft;

							last = isAlreadyGrid ? a.nextAll("div:eq("+(i-1)+")") : $("

").insertAfter(last);
							span = isAlreadyGrid ? last.children("span") : $("").appendTo(last);

							// create a span inside the div, so we can control what happens in the whole div versus inside just the text/background
							span.addClass(cl+" "+valClass).css("display","inline-block").html(val);
							last = last.css(conf).css({width: width,"padding-left":paddingleft+"px"}).addClass("jstree-grid-cell "+wcl+ " " + wideValClass + (tr?" ui-state-default":""));

							if (title) {
								span.attr("title",title);
							}

						}
						last.addClass("jstree-grid-cell-last"+(tr?" ui-state-default":""));
					}
				});
				if(obj.is("li")) { this._repair_state(obj); }
				else { obj.find("> ul > li").each(function () { _this._repair_state(this); }); }
			}
		}
		// need to do alternating background colors or borders
	});
}(jQuery));
<|endoftext|>


<commit_before>var extend = require('extend'),
    fs = require('fs-extra'),
    cmd = require('child_process'),

    WORK_PATH = 'work';

function _cleanWorkspace() {
  if(fs.existsSync(WORK_PATH)) {
    console.log('remove path ' + WORK_PATH);
    fs.remove(WORK_PATH);
  }
}

function _error(error, callback) {
  console.error('\n\nERROR!\n');
  console.error(error);
  console.error('\n\n');

  _cleanWorkspace();

  if(callback) {
    callback(false);
  }

  return false;
}

module.exports = function release(config, callback) {
  config = extend(true, {
    USER_AGENT: '',
    SECRET: '',
    MEMORYOVERFLOW_REPO: '',
    MEMORYOVERFLOW_PATH: '',
    WEBSITE_REPO: '',
    WEBSITE_PATH: '',
    COMMIT_LABEL: '',
    commitID: '',
    commitUrl: ''
  }, config);

  _cleanWorkspace();

  config.MEMORYOVERFLOW_PATH = WORK_PATH + '/' + config.MEMORYOVERFLOW_PATH;
  config.WEBSITE_PATH = WORK_PATH + '/' + config.WEBSITE_PATH;

  console.log('\ngit clone ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH + '...');

  cmd.exec('git clone ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH, function(error, stdout, stderr) {
    if(error) {
      return _error(error, callback);
    }

    console.log('\ngit clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH + '...');

    cmd.exec('git clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH, function(error, stdout, stderr) {
      if(error) {
        return _error(error, callback);
      }

      console.log('\ncopy ' + config.MEMORYOVERFLOW_PATH + '/website' + ' ' + config.WEBSITE_PATH + '...');

      fs.copySync(config.MEMORYOVERFLOW_PATH + '/website', config.WEBSITE_PATH);

      console.log('\ngit config user.name "' + config.USER_AGENT + '"');

      cmd.exec('git config user.name "' + config.USER_AGENT + '"', {
        cwd: config.WEBSITE_PATH
      }, function(error, stdout, stderr) {
        if(error) {
          return _error(error, callback);
        }

        console.log('\ngit config user.email "' + config.USER_AGENT_EMAIL + '"');

        cmd.exec('git config user.email "' + config.USER_AGENT_EMAIL + '"', {
          cwd: config.WEBSITE_PATH
        }, function(error, stdout, stderr) {
          if(error) {
            return _error(error, callback);
          }

          console.log('\ngit add -A');

          cmd.exec('git add -A', {
            cwd: config.WEBSITE_PATH
          }, function(error, stdout, stderr) {
            if(error) {
              return _error(error, callback);
            }

            var commitAuthor = ' --author="' + config.USER_AGENT + ' <' + config.USER_AGENT_EMAIL + '>"',
                commitLabel = config.COMMIT_LABEL
                  .replace('{commitID}', config.commitID)
                  .replace('{commitUrl}', config.commitUrl)
                  .split('\n')
                  .map(function(line) {
                    return ' -m "' + line + '"';
                  })
                  .join('');

            console.log('\ngit commit' + commitAuthor + commitLabel);

            cmd.exec('git commit' + commitAuthor + commitLabel, {
              cwd: config.WEBSITE_PATH
            }, function(error, stdout, stderr) {
              if(error) {
                return _error(error, callback);
              }

              var pushRepo = config.WEBSITE_REPO.replace('https://', 'https://' + config.USER_AGENT + ':' + config.SECRET + '@');

              console.log('\ngit push ' + pushRepo + ' gh-pages');

              cmd.exec('git push ' + pushRepo + ' gh-pages', {
                cwd: config.WEBSITE_PATH
              }, function(error, stdout, stderr) {
                if(error) {
                  return _error(error, callback);
                }

                _cleanWorkspace();

                console.log('\n\nALL IS DONE!\n\n');

                callback(true);

              });
            });
          });
        });
      });
    });
  });
};<commit_msg>refactor(release): remove useless variables<commit_after>var extend = require('extend'),
    fs = require('fs-extra'),
    cmd = require('child_process'),

    WORK_PATH = 'work';

function _cleanWorkspace() {
  if(fs.existsSync(WORK_PATH)) {
    console.log('remove path ' + WORK_PATH);
    fs.remove(WORK_PATH);
  }
}

function _error(error, callback) {
  console.error('\n\nERROR!\n');
  console.error(error);
  console.error('\n\n');

  _cleanWorkspace();

  if(callback) {
    callback(false);
  }

  return false;
}

module.exports = function release(config, callback) {
  config = extend(true, {
    USER_AGENT: '',
    SECRET: '',
    MEMORYOVERFLOW_REPO: '',
    MEMORYOVERFLOW_PATH: '',
    WEBSITE_REPO: '',
    WEBSITE_PATH: '',
    COMMIT_LABEL: '',
    commitID: '',
    commitUrl: ''
  }, config);

  _cleanWorkspace();

  config.MEMORYOVERFLOW_PATH = WORK_PATH + '/' + config.MEMORYOVERFLOW_PATH;
  config.WEBSITE_PATH = WORK_PATH + '/' + config.WEBSITE_PATH;

  console.log('\ngit clone ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH + '...');

  cmd.exec('git clone ' + config.MEMORYOVERFLOW_REPO + ' ' + config.MEMORYOVERFLOW_PATH, function(error) {
    if(error) {
      return _error(error, callback);
    }

    console.log('\ngit clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH + '...');

    cmd.exec('git clone ' + config.WEBSITE_REPO + ' ' + config.WEBSITE_PATH, function(error) {
      if(error) {
        return _error(error, callback);
      }

      console.log('\ncopy ' + config.MEMORYOVERFLOW_PATH + '/website' + ' ' + config.WEBSITE_PATH + '...');

      fs.copySync(config.MEMORYOVERFLOW_PATH + '/website', config.WEBSITE_PATH);

      console.log('\ngit config user.name "' + config.USER_AGENT + '"');

      cmd.exec('git config user.name "' + config.USER_AGENT + '"', {
        cwd: config.WEBSITE_PATH
      }, function(error) {
        if(error) {
          return _error(error, callback);
        }

        console.log('\ngit config user.email "' + config.USER_AGENT_EMAIL + '"');

        cmd.exec('git config user.email "' + config.USER_AGENT_EMAIL + '"', {
          cwd: config.WEBSITE_PATH
        }, function(error) {
          if(error) {
            return _error(error, callback);
          }

          console.log('\ngit add -A');

          cmd.exec('git add -A', {
            cwd: config.WEBSITE_PATH
          }, function(error) {
            if(error) {
              return _error(error, callback);
            }

            var commitAuthor = ' --author="' + config.USER_AGENT + ' <' + config.USER_AGENT_EMAIL + '>"',
                commitLabel = config.COMMIT_LABEL
                  .replace('{commitID}', config.commitID)
                  .replace('{commitUrl}', config.commitUrl)
                  .split('\n')
                  .map(function(line) {
                    return ' -m "' + line + '"';
                  })
                  .join('');

            console.log('\ngit commit' + commitAuthor + commitLabel);

            cmd.exec('git commit' + commitAuthor + commitLabel, {
              cwd: config.WEBSITE_PATH
            }, function(error) {
              if(error) {
                return _error(error, callback);
              }

              var pushRepo = config.WEBSITE_REPO.replace('https://', 'https://' + config.USER_AGENT + ':' + config.SECRET + '@');

              console.log('\ngit push ' + pushRepo + ' gh-pages');

              cmd.exec('git push ' + pushRepo + ' gh-pages', {
                cwd: config.WEBSITE_PATH
              }, function(error) {
                if(error) {
                  return _error(error, callback);
                }

                _cleanWorkspace();

                console.log('\n\nALL IS DONE!\n\n');

                callback(true);

              });
            });
          });
        });
      });
    });
  });
};<|endoftext|>