<commit_before>
(function(){

    dw.visualization.register('bar-chart', 'raphael-chart', {

        render: function(el) {
            el = $(el);

            this.setRoot(el);

            var me = this, row = 0,
                dataset = me.dataset,
                sortBars = me.get('sort-values', false),
                reverse = me.get('reverse-order'),
                useNegativeColor = me.get('negative-color', false);

            me.axesDef = me.axes();
            if (!me.axesDef) return;

            if (!_.isUndefined(me.get('selected-row'))) {
                row = me.get('selected-row', 0);
                row = row > me.axesDef.bars.length ? 0 : row;
            }
            me.__lastRow = row;

            var sliceColumns = _.map(me.axesDef.bars, function(i) { return dataset.column(i); });
                filter = dw.utils.filter(dw.utils.columnNameColumn(sliceColumns), row),
                filterUI = filter.ui(me);

            if (filterUI) {
                $('#header').append(filterUI);
                filter.change(function(val, i) {
                    me.__lastRow = i;
                    me.update(i);
                });
            }
            var c = me.initCanvas({
                h: Math.max(
                    me.getSize()[1] - me.theme.padding.top - me.theme.padding.bottom - me.theme.vpadding,
                    18 * 1.35 * me.getBarColumn(row).length + 5
                )
            });

            var
                chart_width = c.w - c.lpad - c.rpad,
                series_gap = 0.05, // pull from theme
                row_gap = 0.01,
                labelsInsideBars = me.get('labels-inside-bars', false);

            me.init();
            me.initDimensions();

            $('.tooltip').hide();

            c.lastBarY = 0;

            _.each(me.getBarValues(sortBars, reverse), function(barv, s) {
                var d = me.barDimensions(barv, s, row),
                    lpos = me.labelPosition(barv, s, row),
                    fill = me.getKeyColor(barv.name, barv.value, useNegativeColor),
                    stroke = chroma.color(fill).darken(14).hex();

                if (labelsInsideBars) d.x -= 10;

                // draw bar
                var bar = me.registerElement(c.paper.rect(d.x, d.y, d.width, d.height).attr({
                    'stroke': stroke,
                    'fill': fill
                }).data('strokeCol', stroke), barv.name);
                if (me.theme.barChart.barAttrs) {
                    bar.attr(me.theme.barChart.barAttrs);
                }

                if (lpos.show_val) {
                    me.registerLabel(me.label(lpos.val_x, lpos.top, me.chart.formatValue(barv.value, true),{
                        // w: 40,
                        align: lpos.val_align,
                        cl: 'value' + lpos.lblClass
                    }), barv.name);
                }

                if (lpos.show_lbl) {
                    me.registerLabel(me.label(lpos.lbl_x , lpos.top, barv.name, {
                        w: 160,
                        align: lpos.lbl_align,
                        cl: 'series' + lpos.lblClass
                    }), barv.name);
                }

                c.lastBarY = Math.max(c.lastBarY, d.y + d.height);
            });

            if (me.__domain[0] < 0) {
                var x = c.lpad + c.zero ;
                if (labelsInsideBars) x -= 10;
                // add y-axis
                me.__yaxis = me.path('M' + [x, c.tpad] + 'V' + c.lastBarY, 'axis')
                    .attr(me.theme.yAxis);
            }

            // enable mouse events
            el.mousemove(_.bind(me.onMouseMove, me));
            me.renderingComplete();
        },

        getBarValues: function(sortBars, reverse) {
            var me = this,
                values = [],
                filter = me.__lastRow,
                labels = me.dataset.column(me.axesDef.labels),
                column = me.getBarColumn(filter),
                fmt = dw.utils.longDateFormat(column);

            column.each(function(val, i) {
                values.push({
                    name: fmt(labels.val(i)),
                    value: val
                });
            });
            if (sortBars) values.sort(function(a,b) { return b.value - a.value; });
            if (reverse) values.reverse();
            return values;
        },

        getBarColumn: function() {
            var me = this,
                filter = me.__lastRow;
            if (_.isUndefined(filter)) throw 'filter must not be undefined';
            return me.axes(true).bars[filter];
        },

        update: function(row) {
            var me = this;

            // update scales
            me.initDimensions();

            // update bar heights and labels
            _.each(me.getBarValues(me.get('sort-values', false)), function(bar, s) {
                _.each(me.__elements[bar.name], function(rect) {
                    var dim = me.barDimensions(bar, s, row);
                    rect.animate(dim, me.theme.duration, me.theme.easing);
                });

                _.each(me.__labels[bar.name], function(lbl) {
                    var pos = me.labelPosition(bar, s, row), lpos;
                    if (lbl.hasClass('value')) {
                        // update value
                        lbl.text(me.chart.formatValue(bar.value));
                        lpos = { halign: pos.val_align, left: pos.val_x, top: pos.top };
                    } else if (lbl.hasClass('series')) {
                        // update series label position
                        lpos = { halign: pos.lbl_align, left: pos.lbl_x, top: pos.top };
                    }
                    if (lpos) {
                        lbl.animate({
                            align: lpos.halign,
                            x: lpos.left,
                            y: lpos.top
                        }, me.theme.duration, me.theme.easing);
                    }
                });
            });
            if (me.__domain[0] < 0) {
                var c = me.__canvas,
                    x = c.lpad + c.zero - (me.get('labels-inside-bars', false) ? 10 : 0),
                    p = 'M' + [x, c.tpad] + 'V' + c.lastBarY;
                // add y-axis
                if (me.__yaxis) {
                    me.__yaxis.animate({ path: p, opacity: 1 }, me.theme.duration, me.theme.easing);
                } else {
                    me.__yaxis = me.path(p, 'axis').attr(me.theme.yAxis);
                }
            } else if (me.__yaxis) {
                me.__yaxis.animate({ opacity: 0 }, me.theme.duration * 0.5, me.theme.easing);
            }
        },

        initDimensions: function() {
            //
            var me = this, c = me.__canvas,
                w = c.w - c.lpad - c.rpad - 30,
                column = me.getBarColumn(),
                bars = me.getBarValues(),
                domain = me.get('absolute-scale', false) ? dw.utils.minMax(_.map(me.axesDef.bars, function(c) { return me.dataset.column(c); })) : column.range();

            if (domain[0] > 0) domain[0] = 0;
            if (domain[1] < 0) domain[1] = 0;

            me.__domain = domain;
            me.__scales = {
                y: d3.scale.linear().domain(domain)
            };
            /* how maxw works:
             *
             * maxw[0] .. max series name label width      \
             * maxw[1] .. max formatted value label width   } positive
             * largestVal[0] .. max absolute value               /
             * maxw[2] .. max series name label width      \
             * maxw[3] .. max formatted value label width   } negative
             * largestVal[1] .. max absolute value               /
             */
            var maxw = [0, 0, 0, 0], ratio, largestVal = [0, 0];
            _.each(bars, function(bar) {
                if (isNaN(bar.value)) return;
                var neg = bar.value < 0;
                largestVal[neg ? 1 : 0] = Math.max(largestVal[neg ? 1 : 0], Math.abs(bar.value));
            });
            _.each(bars, function(bar) {
                if (isNaN(bar.value)) return;
                var neg = bar.value < 0,
                    t = neg ? 2 : 0,
                    bw;
                bw = Math.abs(bar.value) / (largestVal[0] + largestVal[1]) * w;
                maxw[t] = Math.max(maxw[t], me.labelWidth(bar.name, 'series') + 20);
                maxw[t+1] = Math.max(maxw[t+1], me.labelWidth(me.chart.formatValue(bar.value, true), 'value') + 20 + bw);
            });

            c.left = 0;
            c.right = 0;
            c.zero = largestVal[1] / (largestVal[0] + largestVal[1]) * w;

            if (!me.get('labels-inside-bars', false) && w > 300) {
                var maxNegBar = c.zero;
                //console.log('c.left', );
                c.left = Math.max(maxw[0], maxw[3]) - c.zero;
                c.right = Math.max(maxw[1], maxw[2]) - (w - c.zero); // Math.max((maxw[2] + maxw[1]) - (w - c.zero), 0);

                w -= c.left + c.right;

                c.zero = c.left + largestVal[1] / (largestVal[0] + largestVal[1]) * w;
            } else {
                c.zero += 15;
            }

            c.maxSeriesLabelWidth = [maxw[0], maxw[2]];
            c.maxValueLabelWidth = [maxw[1], maxw[3]];

            me.__scales.y.rangeRound([c.lpad, w]);
        },

        barDimensions: function(bar, s, r) {
            var me = this, w, h, x, y, i, cw, n = me.getBarValues().length,
                sc = me.__scales, c = me.__canvas, bw, pad = 0.35, vspace = 0.1,
                val = bar.value;

            if (isNaN(val)) val = 0;
            //
            cw = c.h - c.bpad - c.tpad;
            //
            bw = Math.max(18, Math.min(50, cw / (n + (n-1) * pad)));
            w = sc.y(val) - sc.y(0);
            h = bw;
            if (w > 0) {
                x = c.lpad + c.zero;
            } else {
                x = c.lpad + c.zero + w;
                w *= -1;
            }
            if (val !== 0) w = Math.max(1, w);
            y = Math.round(c.tpad + s * (bw + bw * pad));
            return { width: w, height: h, x: x, y: y };
        },

        labelPosition: function(bar, s, r) {
            var me = this,
                d = me.barDimensions(bar, s, r),
                c = me.__canvas,
                val = bar.value,
                lbl_left = val >= 0 || isNaN(val),
                lbl_x = lbl_left ?
                    c.zero - 10
                    : c.zero + 10,
                lbl_align = lbl_left ? 'right' : 'left',
                val_x = lbl_left ?
                    d.x + d.width + 10
                    : d.x - 10,
                val_align = lbl_left ? 'left' : 'right',
                show_lbl = true,
                show_val = true,
                lblClass = me.chart.hasHighlight() && me.chart.isHighlighted(bar) ? ' highlighted' : '';

            if (me.get('labels-inside-bars', false)) {
                lbl_x = lbl_left ? d.x + 10 : d.x + d.width - 10;
                val_x = lbl_left ? d.x + d.width - 10 : d.x + 10;
                lbl_align = lbl_left ? 'left' : 'right';
                val_align = lbl_left ? 'right' : 'left';

                // check if the label is bigger than the bar
                var slblw = me.labelWidth(bar.name, 'series')+10,
                    vlblw = me.labelWidth(me.chart.formatValue(val, true), 'value')+20,
                    fill = me.getKeyColor(bar.name, bar.value, me.get('negative-color', false));
                if (slblw + vlblw > d.width) {
                    show_val = false;
                    if (slblw > d.width) show_lbl = false;
                }
                if (me.invertLabel(fill)) lblClass += ' inverted';
            }

            return { lblClass: lblClass, val_align: val_align, show_lbl: show_lbl,
                show_val: show_val, lbl_align: lbl_align, lbl_x: lbl_x, val_x: val_x,
                top: d.y + d.height * 0.5 };
        },

        getDataRowByPoint: function(x, y) {
            return 0;
        },

        showTooltip: function() {

        },

        hideTooltip: function() {

        },

        hover: function(hover_key) {
            var me = this;
            _.each(me.getBarValues(), function(bar) {
                _.each(me.__labels[bar.name], function(lbl) {
                    if (hover_key !== undefined && bar.name == hover_key) {
                        lbl.addClass('hover');
                    } else {
                        lbl.removeClass('hover');
                    }
                });
                 _.each(me.__elements[bar.name], function(el) {
                    var fill = me.getKeyColor(bar.name, bar.value, me.get('negative-color', false)), stroke;
                    if (hover_key !== undefined && bar.name == hover_key) fill = chroma.color(fill).darken(14).hex();
                    stroke = chroma.color(fill).darken(14).hex();
                    if (el.attrs.fill != fill || el.attrs.stroke != stroke)
                        el.animate({ fill: fill, stroke: stroke }, 50);
                });
            });
        },

        unhoverSeries: function() {
            this.hoverSeries();
        }

    });

}).call(this);<commit_msg>fixed max height in bar charts<commit_after>
(function(){

    dw.visualization.register('bar-chart', 'raphael-chart', {

        render: function(el) {
            el = $(el);

            this.setRoot(el);

            var me = this, row = 0,
                dataset = me.dataset,
                sortBars = me.get('sort-values', false),
                reverse = me.get('reverse-order'),
                useNegativeColor = me.get('negative-color', false);

            me.axesDef = me.axes();
            if (!me.axesDef) return;

            if (!_.isUndefined(me.get('selected-row'))) {
                row = me.get('selected-row', 0);
                row = row > me.axesDef.bars.length ? 0 : row;
            }
            me.__lastRow = row;

            var sliceColumns = _.map(me.axesDef.bars, function(i) { return dataset.column(i); });
                filter = dw.utils.filter(dw.utils.columnNameColumn(sliceColumns), row),
                filterUI = filter.ui(me);

            if (filterUI) {
                $('#header').append(filterUI);
                filter.change(function(val, i) {
                    me.__lastRow = i;
                    me.update(i);
                });
            }
            var c = me.initCanvas({
                h: Math.max(
                    me.getSize()[1],
                    18 * 1.35 * me.getBarColumn(row).length + 5
                )
            });

            var
                chart_width = c.w - c.lpad - c.rpad,
                series_gap = 0.05, // pull from theme
                row_gap = 0.01,
                labelsInsideBars = me.get('labels-inside-bars', false);

            me.init();
            me.initDimensions();

            $('.tooltip').hide();

            c.lastBarY = 0;

            _.each(me.getBarValues(sortBars, reverse), function(barv, s) {
                var d = me.barDimensions(barv, s, row),
                    lpos = me.labelPosition(barv, s, row),
                    fill = me.getKeyColor(barv.name, barv.value, useNegativeColor),
                    stroke = chroma.color(fill).darken(14).hex();

                if (labelsInsideBars) d.x -= 10;

                // draw bar
                var bar = me.registerElement(c.paper.rect(d.x, d.y, d.width, d.height).attr({
                    'stroke': stroke,
                    'fill': fill
                }).data('strokeCol', stroke), barv.name);
                if (me.theme.barChart.barAttrs) {
                    bar.attr(me.theme.barChart.barAttrs);
                }

                if (lpos.show_val) {
                    me.registerLabel(me.label(lpos.val_x, lpos.top, me.chart.formatValue(barv.value, true),{
                        // w: 40,
                        align: lpos.val_align,
                        cl: 'value' + lpos.lblClass
                    }), barv.name);
                }

                if (lpos.show_lbl) {
                    me.registerLabel(me.label(lpos.lbl_x , lpos.top, barv.name, {
                        w: 160,
                        align: lpos.lbl_align,
                        cl: 'series' + lpos.lblClass
                    }), barv.name);
                }

                c.lastBarY = Math.max(c.lastBarY, d.y + d.height);
            });

            if (me.__domain[0] < 0) {
                var x = c.lpad + c.zero ;
                if (labelsInsideBars) x -= 10;
                // add y-axis
                me.__yaxis = me.path('M' + [x, c.tpad] + 'V' + c.lastBarY, 'axis')
                    .attr(me.theme.yAxis);
            }

            // enable mouse events
            el.mousemove(_.bind(me.onMouseMove, me));
            me.renderingComplete();
        },

        getBarValues: function(sortBars, reverse) {
            var me = this,
                values = [],
                filter = me.__lastRow,
                labels = me.dataset.column(me.axesDef.labels),
                column = me.getBarColumn(filter),
                fmt = dw.utils.longDateFormat(column);

            column.each(function(val, i) {
                values.push({
                    name: fmt(labels.val(i)),
                    value: val
                });
            });
            if (sortBars) values.sort(function(a,b) { return b.value - a.value; });
            if (reverse) values.reverse();
            return values;
        },

        getBarColumn: function() {
            var me = this,
                filter = me.__lastRow;
            if (_.isUndefined(filter)) throw 'filter must not be undefined';
            return me.axes(true).bars[filter];
        },

        update: function(row) {
            var me = this;

            // update scales
            me.initDimensions();

            // update bar heights and labels
            _.each(me.getBarValues(me.get('sort-values', false)), function(bar, s) {
                _.each(me.__elements[bar.name], function(rect) {
                    var dim = me.barDimensions(bar, s, row);
                    rect.animate(dim, me.theme.duration, me.theme.easing);
                });

                _.each(me.__labels[bar.name], function(lbl) {
                    var pos = me.labelPosition(bar, s, row), lpos;
                    if (lbl.hasClass('value')) {
                        // update value
                        lbl.text(me.chart.formatValue(bar.value));
                        lpos = { halign: pos.val_align, left: pos.val_x, top: pos.top };
                    } else if (lbl.hasClass('series')) {
                        // update series label position
                        lpos = { halign: pos.lbl_align, left: pos.lbl_x, top: pos.top };
                    }
                    if (lpos) {
                        lbl.animate({
                            align: lpos.halign,
                            x: lpos.left,
                            y: lpos.top
                        }, me.theme.duration, me.theme.easing);
                    }
                });
            });
            if (me.__domain[0] < 0) {
                var c = me.__canvas,
                    x = c.lpad + c.zero - (me.get('labels-inside-bars', false) ? 10 : 0),
                    p = 'M' + [x, c.tpad] + 'V' + c.lastBarY;
                // add y-axis
                if (me.__yaxis) {
                    me.__yaxis.animate({ path: p, opacity: 1 }, me.theme.duration, me.theme.easing);
                } else {
                    me.__yaxis = me.path(p, 'axis').attr(me.theme.yAxis);
                }
            } else if (me.__yaxis) {
                me.__yaxis.animate({ opacity: 0 }, me.theme.duration * 0.5, me.theme.easing);
            }
        },

        initDimensions: function() {
            //
            var me = this, c = me.__canvas,
                w = c.w - c.lpad - c.rpad - 30,
                column = me.getBarColumn(),
                bars = me.getBarValues(),
                domain = me.get('absolute-scale', false) ? dw.utils.minMax(_.map(me.axesDef.bars, function(c) { return me.dataset.column(c); })) : column.range();

            if (domain[0] > 0) domain[0] = 0;
            if (domain[1] < 0) domain[1] = 0;

            me.__domain = domain;
            me.__scales = {
                y: d3.scale.linear().domain(domain)
            };
            /* how maxw works:
             *
             * maxw[0] .. max series name label width      \
             * maxw[1] .. max formatted value label width   } positive
             * largestVal[0] .. max absolute value               /
             * maxw[2] .. max series name label width      \
             * maxw[3] .. max formatted value label width   } negative
             * largestVal[1] .. max absolute value               /
             */
            var maxw = [0, 0, 0, 0], ratio, largestVal = [0, 0];
            _.each(bars, function(bar) {
                if (isNaN(bar.value)) return;
                var neg = bar.value < 0;
                largestVal[neg ? 1 : 0] = Math.max(largestVal[neg ? 1 : 0], Math.abs(bar.value));
            });
            _.each(bars, function(bar) {
                if (isNaN(bar.value)) return;
                var neg = bar.value < 0,
                    t = neg ? 2 : 0,
                    bw;
                bw = Math.abs(bar.value) / (largestVal[0] + largestVal[1]) * w;
                maxw[t] = Math.max(maxw[t], me.labelWidth(bar.name, 'series') + 20);
                maxw[t+1] = Math.max(maxw[t+1], me.labelWidth(me.chart.formatValue(bar.value, true), 'value') + 20 + bw);
            });

            c.left = 0;
            c.right = 0;
            c.zero = largestVal[1] / (largestVal[0] + largestVal[1]) * w;

            if (!me.get('labels-inside-bars', false) && w > 300) {
                var maxNegBar = c.zero;
                //console.log('c.left', );
                c.left = Math.max(maxw[0], maxw[3]) - c.zero;
                c.right = Math.max(maxw[1], maxw[2]) - (w - c.zero); // Math.max((maxw[2] + maxw[1]) - (w - c.zero), 0);

                w -= c.left + c.right;

                c.zero = c.left + largestVal[1] / (largestVal[0] + largestVal[1]) * w;
            } else {
                c.zero += 15;
            }

            c.maxSeriesLabelWidth = [maxw[0], maxw[2]];
            c.maxValueLabelWidth = [maxw[1], maxw[3]];

            me.__scales.y.rangeRound([c.lpad, w]);
        },

        barDimensions: function(bar, s, r) {
            var me = this, w, h, x, y, i, cw, n = me.getBarValues().length,
                sc = me.__scales, c = me.__canvas, bw, pad = 0.35, vspace = 0.1,
                val = bar.value;

            if (isNaN(val)) val = 0;
            //
            cw = c.h - c.bpad - c.tpad;
            //
            bw = Math.max(18, Math.min(50, cw / (n + (n-1) * pad)));
            w = sc.y(val) - sc.y(0);
            h = bw;
            if (w > 0) {
                x = c.lpad + c.zero;
            } else {
                x = c.lpad + c.zero + w;
                w *= -1;
            }
            if (val !== 0) w = Math.max(1, w);
            y = Math.round(c.tpad + s * (bw + bw * pad));
            return { width: w, height: h, x: x, y: y };
        },

        labelPosition: function(bar, s, r) {
            var me = this,
                d = me.barDimensions(bar, s, r),
                c = me.__canvas,
                val = bar.value,
                lbl_left = val >= 0 || isNaN(val),
                lbl_x = lbl_left ?
                    c.zero - 10
                    : c.zero + 10,
                lbl_align = lbl_left ? 'right' : 'left',
                val_x = lbl_left ?
                    d.x + d.width + 10
                    : d.x - 10,
                val_align = lbl_left ? 'left' : 'right',
                show_lbl = true,
                show_val = true,
                lblClass = me.chart.hasHighlight() && me.chart.isHighlighted(bar) ? ' highlighted' : '';

            if (me.get('labels-inside-bars', false)) {
                lbl_x = lbl_left ? d.x + 10 : d.x + d.width - 10;
                val_x = lbl_left ? d.x + d.width - 10 : d.x + 10;
                lbl_align = lbl_left ? 'left' : 'right';
                val_align = lbl_left ? 'right' : 'left';

                // check if the label is bigger than the bar
                var slblw = me.labelWidth(bar.name, 'series')+10,
                    vlblw = me.labelWidth(me.chart.formatValue(val, true), 'value')+20,
                    fill = me.getKeyColor(bar.name, bar.value, me.get('negative-color', false));
                if (slblw + vlblw > d.width) {
                    show_val = false;
                    if (slblw > d.width) show_lbl = false;
                }
                if (me.invertLabel(fill)) lblClass += ' inverted';
            }

            return { lblClass: lblClass, val_align: val_align, show_lbl: show_lbl,
                show_val: show_val, lbl_align: lbl_align, lbl_x: lbl_x, val_x: val_x,
                top: d.y + d.height * 0.5 };
        },

        getDataRowByPoint: function(x, y) {
            return 0;
        },

        showTooltip: function() {

        },

        hideTooltip: function() {

        },

        hover: function(hover_key) {
            var me = this;
            _.each(me.getBarValues(), function(bar) {
                _.each(me.__labels[bar.name], function(lbl) {
                    if (hover_key !== undefined && bar.name == hover_key) {
                        lbl.addClass('hover');
                    } else {
                        lbl.removeClass('hover');
                    }
                });
                 _.each(me.__elements[bar.name], function(el) {
                    var fill = me.getKeyColor(bar.name, bar.value, me.get('negative-color', false)), stroke;
                    if (hover_key !== undefined && bar.name == hover_key) fill = chroma.color(fill).darken(14).hex();
                    stroke = chroma.color(fill).darken(14).hex();
                    if (el.attrs.fill != fill || el.attrs.stroke != stroke)
                        el.animate({ fill: fill, stroke: stroke }, 50);
                });
            });
        },

        unhoverSeries: function() {
            this.hoverSeries();
        }

    });

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


<commit_before>define([ "Quaternion", "Vector", "ship/Device", "ship/SupplyType" ], function(Quaternion, Vector, Device, SupplyType)
{
    "use strict";
    function AlignmentAutopilot(name, environment, parentKey, position, orientation, consumePerTick)
    {
        var that = this;
        var isActive = false;
        var azimuthGroups = [ "portYaw", "starboardYaw" ];
        var elevationGroups = [ "dorsalPitch", "ventralPitch" ];
        var targetVector = Vector.X_AXIS;
        var b = 0.0;
        var d = 0.0;

        this.isActive = function(newValue)
        {
            if (newValue === false || newValue === true)
            {
                isActive = newValue;
                this.trigger("isActive", isActive);
                LOGGER.trace(this.name() + " isActive() ? " + isActive);

                if (!isActive)
                {
                    propulsionActive(azimuthGroups, false);
                    propulsionActive(elevationGroups, false);
                }
            }

            return isActive;
        };

        this.targetVector = function(newValue)
        {
            if (newValue)
            {
                targetVector = newValue;
                var state = this.state();
                var targetVectorLocal = state.orientation().postMultiply(targetVector);
                var initialAzimuth = Math.abs(Computer.convertAngle(targetVectorLocal.azimuth()));
                var initialElevation = Math.abs(Computer.convertAngle(targetVectorLocal.elevation()));
                // Equation derived from hand optimized data: f(10) = -4, f(90) = -11, f(180) = -16
                b = 0.000188 * initialAzimuth * initialAzimuth - 0.106291 * initialAzimuth - 2.955882;
                d = 0.000188 * initialElevation * initialElevation - 0.106291 * initialElevation - 2.955882;
            }

            return targetVector;
        };

        this.tick = function()
        {
            if (this.isActive())
            {
                if (this.isPowered())
                {
                    update();
                }
                else
                {
                    propulsionActive(azimuthGroups, false);
                    propulsionActive(elevationGroups, false);
                }
            }
        };

        Vizzini.extend(this, new Computer(name, environment, parentKey, position, orientation, consumePerTick));

        function propulsionActive(groupKeys, isActive)
        {
            var ship = that.environment().ship(that.parentKey());

            groupKeys.forEach(function(groupKey)
            {
                var propulsionGroup = ship.propulsionGroup(groupKey);

                if (propulsionGroup)
                {
                    propulsionGroup.forEach(function(engine)
                    {
                        engine.isActive(isActive);
                    });
                }
            });
        }

        function update()
        {
            LOGGER.trace("AlignmentAutopilot.update() start");

            // Compute target vector in ship coordinates.
            var state = that.state();
            var orientation = state.orientation();
            var targetVector = that.targetVector();
            LOGGER.debug("targetVector = " + targetVector.toHeadingString());
            var targetVectorLocal = state.orientation().postMultiply(targetVector);

            var error0 = Computer.convertAngle(targetVectorLocal.azimuth()); // azimuth
            var error1 = Computer.convertAngle(state.angularVelocity().azimuth()); // velocity azimuth
            var error2 = Computer.convertAngle(targetVectorLocal.elevation()); // elevation
            var error3 = Computer.convertAngle(state.angularVelocity().elevation()); // velocity elevation
            var ratio01 = (error1 === 0 ? 1.0e+20 * Math.sign(error0) : Math.vizziniRound(error0 / error1, 4));
            var ratio23 = (error3 === 0 ? 1.0e+20 * Math.sign(error2) : Math.vizziniRound(error2 / error3, 4));

            propulsionActive(azimuthGroups, false);
            propulsionActive(elevationGroups, false);

            // output: one of "portYaw", off, "starboardYaw"
            var azimuthGroup = controlProgram(error0, error1, b, azimuthGroups);

            // output: one of "dorsalPitch", off, "ventralPitch"
            var elevationGroup = controlProgram(error2, error3, d, elevationGroups);

            LOGGER.debug("errors = " + Math.vizziniRound(error0, 4) + " " + Math.vizziniRound(error1, 4) + " " +
                    Math.vizziniRound(error2, 4) + " " + Math.vizziniRound(error3, 4) + " ratios = " + ratio01 + " " +
                    ratio23 + " groupKeys = " + azimuthGroup + " " + elevationGroup);

            propulsionActive([ azimuthGroup ], true);
            propulsionActive([ elevationGroup ], true);

            LOGGER.trace("AlignmentAutopilot.update() end");
        }

        function controlProgram(error0, error1, b, groupKeys)
        {
            var answer = "off";
            var sum = error0 + b * error1;
            var threshold = 0.5;

            if (sum > threshold)
            {
                answer = groupKeys[0];
            }
            else if (sum < -threshold)
            {
                answer = groupKeys[1];
            }

            return answer;
        }
    }

    AlignmentAutopilot.prototype.toString = function()
    {
        return "AlignmentAutopilot " + this.name() + " consumePerTick=" + this.consumePerTick();
    };

    MicroEvent.mixin(AlignmentAutopilot);

    function FakeAlignmentAutopilot(name, environment, parentKey, position, orientation, consumePerTick)
    {
        var that = this;
        var isActive = false;
        var targetVector = Vector.X_AXIS;
        var myAngularAcceleration = Quaternion.ZERO;

        this.isActive = function(newValue)
        {
            if (newValue === false || newValue === true)
            {
                isActive = newValue;
                this.trigger("isActive", isActive);
            }

            return isActive;
        };

        this.targetVector = function(newValue)
        {
            if (newValue)
            {
                targetVector = newValue;
                var state = this.state();
                var headingVector = state.orientation().preMultiply(Vector.X_AXIS);
                var stepCount = 10;
                var angle = headingVector.angle(targetVector) / stepCount;
                var vector = headingVector.cross(targetVector).unit();
                LOGGER.debug("angle = " + angle + " vector = " + vector.toHeadingString());
                myAngularAcceleration = Quaternion.newInstance(angle, vector);
                LOGGER.debug("myAngularAcceleration = " + myAngularAcceleration.toHeadingString());
                state.addAngularAcceleration(myAngularAcceleration);
            }

            return targetVector;
        };

        this.tick = function()
        {
            if (this.isActive() && this.isPowered())
            {
                update();
            }
        };

        Vizzini.extend(this, new Computer(name, environment, parentKey, position, orientation, consumePerTick));

        function update()
        {
            var state = that.state();
            var targetVectorLocal = state.orientation().postMultiply(targetVector);
            var error0 = Computer.convertAngle(targetVectorLocal.azimuth()); // azimuth
            var error1 = Computer.convertAngle(state.angularVelocity().azimuth()); // velocity azimuth
            var error2 = Computer.convertAngle(targetVectorLocal.elevation()); // elevation
            var error3 = Computer.convertAngle(state.angularVelocity().elevation()); // velocity elevation

            LOGGER.debug("errors = " + Math.vizziniRound(error0, 4) + " " + Math.vizziniRound(error1, 4) + " " +
                    Math.vizziniRound(error2, 4) + " " + Math.vizziniRound(error3, 4));

            var threshold = 0.5;

            if (Math.abs(error0) < threshold && Math.abs(error2) < threshold)
            {
                // Done.
                state.zeroRotation();
                that.isActive(false);
            }
        }
    }

    FakeAlignmentAutopilot.prototype.toString = function()
    {
        return "FakeAlignmentAutopilot " + this.name() + " consumePerTick=" + this.consumePerTick();
    };

    MicroEvent.mixin(FakeAlignmentAutopilot);

    function Computer(name, environment, parentKey, position, orientation, consumePerTick)
    {
        InputValidator.validateNotNull("consumePerTick", consumePerTick);

        this.consumePerTick = function()
        {
            return consumePerTick;
        };

        this.consumeType = function()
        {
            return SupplyType.POWER;
        };

        this.isPowered = function()
        {
            return this.isSupplied(SupplyType.POWER, consumePerTick);
        };

        Vizzini.extend(this, new Device(name, environment, parentKey, position, orientation));
    }

    Computer.convertAngle = function(angle)
    {
        var answer = angle;

        if (180.0 < angle && angle < 360.0)
        {
            answer -= 360.0;
        }

        return answer;
    };

    return (
    {
        AlignmentAutopilot: AlignmentAutopilot,
        FakeAlignmentAutopilot: FakeAlignmentAutopilot,
    });
});
<commit_msg>Changed to use 2 steps for small angle changes.<commit_after>define([ "Quaternion", "Vector", "ship/Device", "ship/SupplyType" ], function(Quaternion, Vector, Device, SupplyType)
{
    "use strict";
    function AlignmentAutopilot(name, environment, parentKey, position, orientation, consumePerTick)
    {
        var that = this;
        var isActive = false;
        var azimuthGroups = [ "portYaw", "starboardYaw" ];
        var elevationGroups = [ "dorsalPitch", "ventralPitch" ];
        var targetVector = Vector.X_AXIS;
        var b = 0.0;
        var d = 0.0;

        this.isActive = function(newValue)
        {
            if (newValue === false || newValue === true)
            {
                isActive = newValue;
                this.trigger("isActive", isActive);
                LOGGER.trace(this.name() + " isActive() ? " + isActive);

                if (!isActive)
                {
                    propulsionActive(azimuthGroups, false);
                    propulsionActive(elevationGroups, false);
                }
            }

            return isActive;
        };

        this.targetVector = function(newValue)
        {
            if (newValue)
            {
                targetVector = newValue;
                var state = this.state();
                var targetVectorLocal = state.orientation().postMultiply(targetVector);
                var initialAzimuth = Math.abs(Computer.convertAngle(targetVectorLocal.azimuth()));
                var initialElevation = Math.abs(Computer.convertAngle(targetVectorLocal.elevation()));
                // Equation derived from hand optimized data: f(10) = -4, f(90) = -11, f(180) = -16
                b = 0.000188 * initialAzimuth * initialAzimuth - 0.106291 * initialAzimuth - 2.955882;
                d = 0.000188 * initialElevation * initialElevation - 0.106291 * initialElevation - 2.955882;
            }

            return targetVector;
        };

        this.tick = function()
        {
            if (this.isActive())
            {
                if (this.isPowered())
                {
                    update();
                }
                else
                {
                    propulsionActive(azimuthGroups, false);
                    propulsionActive(elevationGroups, false);
                }
            }
        };

        Vizzini.extend(this, new Computer(name, environment, parentKey, position, orientation, consumePerTick));

        function propulsionActive(groupKeys, isActive)
        {
            var ship = that.environment().ship(that.parentKey());

            groupKeys.forEach(function(groupKey)
            {
                var propulsionGroup = ship.propulsionGroup(groupKey);

                if (propulsionGroup)
                {
                    propulsionGroup.forEach(function(engine)
                    {
                        engine.isActive(isActive);
                    });
                }
            });
        }

        function update()
        {
            LOGGER.trace("AlignmentAutopilot.update() start");

            // Compute target vector in ship coordinates.
            var state = that.state();
            var orientation = state.orientation();
            var targetVector = that.targetVector();
            LOGGER.debug("targetVector = " + targetVector.toHeadingString());
            var targetVectorLocal = state.orientation().postMultiply(targetVector);

            var error0 = Computer.convertAngle(targetVectorLocal.azimuth()); // azimuth
            var error1 = Computer.convertAngle(state.angularVelocity().azimuth()); // velocity azimuth
            var error2 = Computer.convertAngle(targetVectorLocal.elevation()); // elevation
            var error3 = Computer.convertAngle(state.angularVelocity().elevation()); // velocity elevation
            var ratio01 = (error1 === 0 ? 1.0e+20 * Math.sign(error0) : Math.vizziniRound(error0 / error1, 4));
            var ratio23 = (error3 === 0 ? 1.0e+20 * Math.sign(error2) : Math.vizziniRound(error2 / error3, 4));

            propulsionActive(azimuthGroups, false);
            propulsionActive(elevationGroups, false);

            // output: one of "portYaw", off, "starboardYaw"
            var azimuthGroup = controlProgram(error0, error1, b, azimuthGroups);

            // output: one of "dorsalPitch", off, "ventralPitch"
            var elevationGroup = controlProgram(error2, error3, d, elevationGroups);

            LOGGER.debug("errors = " + Math.vizziniRound(error0, 4) + " " + Math.vizziniRound(error1, 4) + " " +
                    Math.vizziniRound(error2, 4) + " " + Math.vizziniRound(error3, 4) + " ratios = " + ratio01 + " " +
                    ratio23 + " groupKeys = " + azimuthGroup + " " + elevationGroup);

            propulsionActive([ azimuthGroup ], true);
            propulsionActive([ elevationGroup ], true);

            LOGGER.trace("AlignmentAutopilot.update() end");
        }

        function controlProgram(error0, error1, b, groupKeys)
        {
            var answer = "off";
            var sum = error0 + b * error1;
            var threshold = 0.5;

            if (sum > threshold)
            {
                answer = groupKeys[0];
            }
            else if (sum < -threshold)
            {
                answer = groupKeys[1];
            }

            return answer;
        }
    }

    AlignmentAutopilot.prototype.toString = function()
    {
        return "AlignmentAutopilot " + this.name() + " consumePerTick=" + this.consumePerTick();
    };

    MicroEvent.mixin(AlignmentAutopilot);

    function FakeAlignmentAutopilot(name, environment, parentKey, position, orientation, consumePerTick)
    {
        var that = this;
        var isActive = false;
        var targetVector = Vector.X_AXIS;
        var myAngularAcceleration = Quaternion.ZERO;

        this.isActive = function(newValue)
        {
            if (newValue === false || newValue === true)
            {
                isActive = newValue;
                this.trigger("isActive", isActive);
            }

            return isActive;
        };

        this.targetVector = function(newValue)
        {
            if (newValue)
            {
                targetVector = newValue;
                var state = this.state();
                var headingVector = state.orientation().preMultiply(Vector.X_AXIS);
                var angle = headingVector.angle(targetVector);
                var stepCount = (angle < 5.0 ? 2 : 10);
                var vector = headingVector.cross(targetVector).unit();
                LOGGER.debug("angle = " + angle + " vector = " + vector.toHeadingString());
                myAngularAcceleration = Quaternion.newInstance(angle / stepCount, vector);
                LOGGER.debug("myAngularAcceleration = " + myAngularAcceleration.toHeadingString());
                state.addAngularAcceleration(myAngularAcceleration);
            }

            return targetVector;
        };

        this.tick = function()
        {
            if (this.isActive() && this.isPowered())
            {
                update();
            }
        };

        Vizzini.extend(this, new Computer(name, environment, parentKey, position, orientation, consumePerTick));

        function update()
        {
            var state = that.state();
            var targetVectorLocal = state.orientation().postMultiply(targetVector);
            var error0 = Computer.convertAngle(targetVectorLocal.azimuth()); // azimuth
            var error1 = Computer.convertAngle(state.angularVelocity().azimuth()); // velocity azimuth
            var error2 = Computer.convertAngle(targetVectorLocal.elevation()); // elevation
            var error3 = Computer.convertAngle(state.angularVelocity().elevation()); // velocity elevation

            LOGGER.debug("errors = " + Math.vizziniRound(error0, 4) + " " + Math.vizziniRound(error1, 4) + " " +
                    Math.vizziniRound(error2, 4) + " " + Math.vizziniRound(error3, 4));

            var threshold = 0.5;

            if (Math.abs(error0) < threshold && Math.abs(error2) < threshold)
            {
                // Done.
                state.zeroRotation();
                that.isActive(false);
            }
        }
    }

    FakeAlignmentAutopilot.prototype.toString = function()
    {
        return "FakeAlignmentAutopilot " + this.name() + " consumePerTick=" + this.consumePerTick();
    };

    MicroEvent.mixin(FakeAlignmentAutopilot);

    function Computer(name, environment, parentKey, position, orientation, consumePerTick)
    {
        InputValidator.validateNotNull("consumePerTick", consumePerTick);

        this.consumePerTick = function()
        {
            return consumePerTick;
        };

        this.consumeType = function()
        {
            return SupplyType.POWER;
        };

        this.isPowered = function()
        {
            return this.isSupplied(SupplyType.POWER, consumePerTick);
        };

        Vizzini.extend(this, new Device(name, environment, parentKey, position, orientation));
    }

    Computer.convertAngle = function(angle)
    {
        var answer = angle;

        if (180.0 < angle && angle < 360.0)
        {
            answer -= 360.0;
        }

        return answer;
    };

    return (
    {
        AlignmentAutopilot: AlignmentAutopilot,
        FakeAlignmentAutopilot: FakeAlignmentAutopilot,
    });
});
<|endoftext|>


<commit_before>/**
 * @implements IIndex
 */
class PersistentIndex {
    /**
     * @param {ObjectStore} objectStore
     * @param {string} indexName
     * @param {string|Array.<string>} keyPath
     * @param {boolean} multiEntry
     */
    constructor(objectStore, indexName, keyPath, multiEntry=false) {
        this._objectStore = objectStore;
        this._indexName = indexName;
        this._keyPath = keyPath;
        this._multiEntry = multiEntry;
    }

    /**
     * @returns {Promise}
     */
    async truncate() {
        // Will automatically be truncated.
    }

    /**
     * @type {string}
     */
    get keyPath() {
        return this._keyPath;
    }

    /**
     * @type {boolean}
     */
    get multiEntry() {
        return this._multiEntry;
    }

    /**
     * @param {IDBDatabase} db
     * @private
     */
    _index(db) {
        return db.transaction([this._objectStore.tableName], 'readonly')
            .objectStore(this._objectStore.tableName)
            .index(this._indexName);
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Array.<*>>}
     */
    async values(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const getRequest = this._index(db).getAll(query);
            getRequest.onsuccess = () => resolve(getRequest.result);
            getRequest.onerror = () => reject(getRequest.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Set.<string>>}
     */
    async keys(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const getRequest = this._index(db).getAllKeys(query);
            getRequest.onsuccess = () => resolve(Set.from(getRequest.result));
            getRequest.onerror = () => reject(getRequest.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Array.<*>>}
     */
    async maxValues(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const openCursorRequest = this._index(db).openCursor(query, 'prev');
            openCursorRequest.onsuccess = () => resolve(openCursorRequest.result.value);
            openCursorRequest.onerror = () => reject(openCursorRequest.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Set.<string>>}
     */
    async maxKeys(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const openCursorRequest = this._index(db).openKeyCursor(query, 'prev');
            openCursorRequest.onsuccess = () => resolve(Set.from(openCursorRequest.result.primaryKey));
            openCursorRequest.onerror = () => reject(openCursorRequest.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Array.<*>>}
     */
    async minValues(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const openCursorRequest = this._index(db).openCursor(query, 'next');
            openCursorRequest.onsuccess = () => resolve(openCursorRequest.result.value);
            openCursorRequest.onerror = () => reject(openCursorRequest.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Set.<string>>}
     */
    async minKeys(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const openCursorRequest = this._index(db).openKeyCursor(query, 'next');
            openCursorRequest.onsuccess = () => resolve(Set.from(openCursorRequest.result.primaryKey));
            openCursorRequest.onerror = () => reject(openCursorRequest.error);
        });
    }

    /**
     * @abstract
     * @param {KeyRange|*} [query]
     * @returns {Promise.<number>}
     */
    async count(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const getRequest = this._index(db).count(query);
            getRequest.onsuccess = () => resolve(getRequest.result);
            getRequest.onerror = () => reject(getRequest.error);
        });
    }
}
Class.register(PersistentIndex);

<commit_msg>Fix null pointer when executing min/max queries on empty index<commit_after>/**
 * @implements IIndex
 */
class PersistentIndex {
    /**
     * @param {ObjectStore} objectStore
     * @param {string} indexName
     * @param {string|Array.<string>} keyPath
     * @param {boolean} multiEntry
     */
    constructor(objectStore, indexName, keyPath, multiEntry=false) {
        this._objectStore = objectStore;
        this._indexName = indexName;
        this._keyPath = keyPath;
        this._multiEntry = multiEntry;
    }

    /**
     * @returns {Promise}
     */
    async truncate() {
        // Will automatically be truncated.
    }

    /**
     * @type {string}
     */
    get keyPath() {
        return this._keyPath;
    }

    /**
     * @type {boolean}
     */
    get multiEntry() {
        return this._multiEntry;
    }

    /**
     * @param {IDBDatabase} db
     * @private
     */
    _index(db) {
        return db.transaction([this._objectStore.tableName], 'readonly')
            .objectStore(this._objectStore.tableName)
            .index(this._indexName);
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Array.<*>>}
     */
    async values(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const getRequest = this._index(db).getAll(query);
            getRequest.onsuccess = () => resolve(getRequest.result);
            getRequest.onerror = () => reject(getRequest.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Set.<string>>}
     */
    async keys(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const getRequest = this._index(db).getAllKeys(query);
            getRequest.onsuccess = () => resolve(Set.from(getRequest.result));
            getRequest.onerror = () => reject(getRequest.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Array.<*>>}
     */
    async maxValues(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const request = this._index(db).openCursor(query, 'prev');
            request.onsuccess = () => resolve(request.result ? request.result.value : []);
            request.onerror = () => reject(request.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Set.<string>>}
     */
    async maxKeys(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const request = this._index(db).openKeyCursor(query, 'prev');
            request.onsuccess = () => resolve(Set.from(request.result ? request.result.primaryKey : []));
            request.onerror = () => reject(request.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Array.<*>>}
     */
    async minValues(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const request = this._index(db).openCursor(query, 'next');
            request.onsuccess = () => resolve(request.result ? request.result.value : []);
            request.onerror = () => reject(request.error);
        });
    }

    /**
     * @param {KeyRange|*} [query]
     * @returns {Promise.<Set.<string>>}
     */
    async minKeys(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const request = this._index(db).openKeyCursor(query, 'next');
            request.onsuccess = () => resolve(Set.from(request.result ? request.result.primaryKey : []));
            request.onerror = () => reject(request.error);
        });
    }

    /**
     * @abstract
     * @param {KeyRange|*} [query]
     * @returns {Promise.<number>}
     */
    async count(query=null) {
        query = IDBTools.convertKeyRange(query);
        const db = await this._objectStore.backend;
        return new Promise((resolve, reject) => {
            const request = this._index(db).count(query);
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }
}
Class.register(PersistentIndex);

<|endoftext|>


<commit_before>//__BEGIN_LICENSE__
// Copyright (c) 2015, United States Government, as represented by the
// Administrator of the National Aeronautics and Space Administration.
// All rights reserved.
//
// The xGDS platform is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//__END_LICENSE__

var xgds_image = xgds_image || {};
$.extend(xgds_image,{
	hookDelete: function() {
		// Add a click handler for the delete row
		$('#delete_images').click( function() {
		    var selectedRows = getSelectedRows( theDataTable );
		    // get a list containing just the id's of selected images
		    var selectedImageIdsList = jQuery.map(selectedRows, function(element) { return jQuery(element).attr('pk'); });
		    var selectedImageIdsJson = {"pk": selectedImageIdsList};
			//delete selected images from db
		    var url = deleteImagesUrl;
		    $.ajax({
				url: url,
				type: "POST",
				data: selectedImageIdsJson, // serializes the form's elements.
				success: function(data) {
					console.log("images successfully deleted");
				},
				error: function(request, status, error) {
					console.log("error! ", error);
				}
			})

			// delete selected rows from datatable.
		    for (var i = 0; i < selectedRows.length; i++) {
		        theDataTable.fnDeleteRow(selectedRows[i]);
		    }
		    // re-render the map icons with only the non-deleted images.
		    showOnMap(theDataTable.fnGetData());
		} );
	},
	activateButtons: function(el) {
		// Hook up edit info and add note buttons
		el.find("#edit_info_button").click(function(event) {
		    event.preventDefault();
		    el.find("#image_overview").hide();
		    el.find("#more_info_view").show();
		});
		el.find("#cancel_edit").click(function(event) {
		    event.preventDefault();
		    el.find("#more_info_view").hide();
		    el.find("#image_overview").show();
		});
		el.find("#add_note_button").click(function(event) {
		    event.preventDefault();
		    el.find("#notes_input").show();
		});
	},
	onImageNextOrPrev: function(el){
		el.find('.next-button').click(function(event) {
			// set the img src
			var index = xgds_image.getCurrentImageAndIndex(el);
			if (index != null) {
			    index = index + 1;
			    if (index == imageSetsArray.length){
			    	index = 0;
			    }
			    clearTableSelection(theDataTable);
			    xgds_image.updateImageView(el, null, index, false, false);
			}
		});
		el.find(".prev-button").click(function(event) {
			var index = xgds_image.getCurrentImageAndIndex(el);
			if (index != null) {
			    index = index - 1;
			    if (index < 0){
				index = imageSetsArray.length - 1;
			    }
			    clearTableSelection(theDataTable);
			    xgds_image.updateImageView(el, null, index, false, false);
			}
		});
	},
	setChangedPosition: function(value, el){
		$(el).find('#id_changed_position').attr('value', value);
	},
	hookEditingPosition: function(el){
		// Set the changed_position to true if the user edits any of the position fields.
		$(el).find("#id_latitude").change(function() {setChangedPosition(1, el)});
		$(el).find("#id_longitude").change(function() {setChangedPosition(1, el)});
		$(el).find("#id_altitude").change(function() {setChangedPosition(1, el)});
		$(el).find("#id_heading").change(function() {setChangedPosition(1, el)});
	},
	onUpdateImageInfo: function(el) {
		// Saves image info to the db when user updates it and submits.
		$(el).find("#updateInfoSubmit").on('click', function(event) {
			event.preventDefault(); 	// avoid to execute the actual submit of the form.
			var url = updateImageUrl; // the script where you handle the form input.
			var postData = $("#more-info-form").serialize();
			$.ajax({
				url: url,
				type: "POST",
				data: postData, // serializes the form's elements.
				success: function(data)
				{
				    xgds_image.setSaveStatusMessage($('#message'), 'Saved','');
				    xgds_image.updateImageView(el, data[0], undefined, true, true);
				    el.find("#more_info_view").hide();
				    el.find("#image_overview").show();
				},
				error: function(request, status, error) {
				    xgds_image.setSaveStatusMessage($('#message'), status, error);
				}
			});
			return false;
		});
	},
	getCurrentImageAndIndex: function(el){
		// Helper that finds index in imageSetsArray
		// of the currently displayed image in the imageView.
		var currentImageName = el.attr('pk');
		var currentImageIndex = null;
		for (var i=0; i< imageSetsArray.length; i++) {
			if (imageSetsArray[i]['name'] == currentImageName) {
				currentImageIndex = i;
				break;
			}
		}
		return currentImageIndex;
	},
	updateImageView: function(el, imageJson, index, keepingImage, keepingNotes) {
		if (imageJson == null){
			if (index != null) {
				imageJson = imageSetsArray[index];
			} else {
				return;
			}
		}

		if (!keepingNotes){
			var tbl = el.find('table#notes_list');
			initializeNotesReference(el, imageJson['app_label'], imageJson['model_type'], imageJson['pk'], imageJson['acquisition_time'], imageJson['acquisition_timezone']);
			getNotesForObject(imageJson['app_label'], imageJson['model_type'], imageJson['pk'], 'notes_content', tbl);
		}

		var newContent = this.compiledTemplate(imageJson);
		$(el).html(newContent);

		if (!keepingImage){
//			el.attr('id', imageJson['name']);
			this.loadImageInViewer(imageJson);
	    }
//		el.find(".image-name strong").text(imageJson['name']);
//
//		// update values
//		el.find('a#new-window-target').attr('href',imageJson['view_url']);
//		el.find('#id_pk').attr('value', imageJson['pk']);
//		el.find('#overview_description').text(imageJson['description']);
//		el.find('textarea[name="description"]').attr('value', imageJson['description']);
//		el.find('input[name="name"]').attr('value', imageJson['name']);
//		el.find('input[name="latitude"]').attr('value', imageJson['lat']);
//		el.find('input[name="longitude"]').attr('value', imageJson['lon']);
//		el.find('input[name="altitude"]').attr('value', imageJson['altitude']);
//		el.find('input[name="heading"]').attr('value', imageJson['heading']);
//		el.find('#id_changed_position').attr('value', 0);
	},
	hideImageNextPrev: function() {
		$('.prev-button').hide();
		$('.next-button').hide();
		$('#new-window-btn').hide();
	},
	constructImageView: function(imageJson, viewPage){
		var imageViewWidget = $("#image_view");
		if (imageViewWidget.length) {
			this.updateImageView(imageViewWidget, imageJson, null, false, false);
		} else {
	        Handlebars.registerHelper('prettyTime', function( sourceTime, timeZone ){
	        	return getLocalTimeString(sourceTime, timeZone);
	        });

			viewPage = typeof viewPage !== 'undefined' ? viewPage : false;
			var rawTemplate = $('#template-image-view').html();
			this.compiledTemplate = Handlebars.compile(rawTemplate);

			var newDiv = this.compiledTemplate(imageJson);
			var imageViewTemplate = $(newDiv);

			// callbacks
			this.hookEditingPosition(imageViewTemplate);
			this.onUpdateImageInfo(imageViewTemplate);
			this.activateButtons(imageViewTemplate);
			this.hookDelete();

			if (!viewPage){
				this.onImageNextOrPrev(imageViewTemplate);
			}

			// append the div to the container and gridstack.
			var newEl;
			var container = $('#container');

			if (!viewPage){
				newEl = container.append(imageViewTemplate);
			} else {
				newEl = container.prepend(imageViewTemplate);
			}
			// add the element to the dashboard
			if (!viewPage){
				addItem(imageViewTemplate, 3, 3, 3, 2);
			}
			//	// set the loading image to be displayed when main img is loading
			//	imageViewTemplate.find(".display-image").load(function() {
			//	// set dimensions of loading image
			//	var width = imageViewTemplate.find(".display-image").width();
			//	var height = imageViewTemplate.find(".display-image").height();
			//	imageViewTemplate.find(".loading-image").width(width);
			//	imageViewTemplate.find(".loading-image").height(height);
			//	imageViewTemplate.find(".loading-image").hide();
			//	});
			//	setChangedPosition(0, imageViewTemplate);

			//add the notes if it does not exist
			var notes_content_div = imageViewTemplate.find("#notes_content");
			var notes_table = undefined;
			if ($(notes_content_div).is(':empty')){
				// the first time we want to fill it in
				notes_table = $.find("table#notes_list");
				var notes_input_div = $.find("#notes_input");

				var new_input_div = $(notes_input_div).hide();
				$(notes_content_div).append(new_input_div);

				var new_table_div = $(notes_table);
				$(notes_content_div).append(new_table_div);
				$(new_table_div).removeAttr('hidden');
				$(new_table_div).show();

				var taginput = $(new_input_div).find('.taginput');
				initializeInput(taginput);
				hookNoteSubmit();

			} else {
				notes_table = imageViewTemplate.find("table#notes_list");
			}

			initializeNotesReference(imageViewTemplate, imageJson['app_label'], imageJson['model_type'], imageJson['id'], imageJson['creation_time']);
			getNotesForObject(imageJson['app_label'], imageJson['model_type'], imageJson['id'], 'notes_content', $(notes_table));

			// set the gridstack image height
			$('.image_view_outer').attr('data-gs-height', '4');

			this.setupImageViewer(imageJson);
		}
	},
	setupImageViewer: function(imageJson){

		if (this.viewer != undefined){
			this.viewer.destroy();
			this.viewer = null;
		}
		// build tile sources for openseadragon image viewer
		var prefixUrl = '/static/openseadragon/built-openseadragon/openseadragon/images/';
		this.viewer = OpenSeadragon({
			id: "display-image",
			prefixUrl: prefixUrl,
			tileSources: {
				type: 'image',
				url: imageJson.raw_image_url
			}
		});

	},
	loadImageInViewer: function(imageJson){
		this.setupImageViewer(imageJson);
		return;
    	// load new image into OpenSeadragon viewer
		if (this.viewer == undefined){
			this.setupImageViewer(imageJson);
		} else {
			this.viewer.open({type: 'image',
							  url: imageJson.raw_image_url});
		}
	},
	setSaveStatusMessage: function(handler, status, msg){
		if (status == 'success') {
			handler.attr('class', 'success-message');
		} else {
			handler.attr('class', 'error-message');
		}
		handler.html(msg);
		setTimeout(function() { // messages fades out.
			handler.fadeOut().empty();
		}, 5000);
	}
});

<commit_msg>using xgds_image namespace<commit_after>//__BEGIN_LICENSE__
// Copyright (c) 2015, United States Government, as represented by the
// Administrator of the National Aeronautics and Space Administration.
// All rights reserved.
//
// The xGDS platform is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0.
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//__END_LICENSE__

var xgds_image = xgds_image || {};
$.extend(xgds_image,{
	hookDelete: function() {
		// Add a click handler for the delete row
		$('#delete_images').click( function() {
		    var selectedRows = getSelectedRows( theDataTable );
		    // get a list containing just the id's of selected images
		    var selectedImageIdsList = jQuery.map(selectedRows, function(element) { return jQuery(element).attr('pk'); });
		    var selectedImageIdsJson = {"pk": selectedImageIdsList};
			//delete selected images from db
		    var url = deleteImagesUrl;
		    $.ajax({
				url: url,
				type: "POST",
				data: selectedImageIdsJson, // serializes the form's elements.
				success: function(data) {
					console.log("images successfully deleted");
				},
				error: function(request, status, error) {
					console.log("error! ", error);
				}
			})

			// delete selected rows from datatable.
		    for (var i = 0; i < selectedRows.length; i++) {
		        theDataTable.fnDeleteRow(selectedRows[i]);
		    }
		    // re-render the map icons with only the non-deleted images.
		    showOnMap(theDataTable.fnGetData());
		} );
	},
	activateButtons: function(el) {
		// Hook up edit info and add note buttons
		el.find("#edit_info_button").click(function(event) {
		    event.preventDefault();
		    el.find("#image_overview").hide();
		    el.find("#more_info_view").show();
		});
		el.find("#cancel_edit").click(function(event) {
		    event.preventDefault();
		    el.find("#more_info_view").hide();
		    el.find("#image_overview").show();
		});
		el.find("#add_note_button").click(function(event) {
		    event.preventDefault();
		    el.find("#notes_input").show();
		});
	},
	onImageNextOrPrev: function(el){
		el.find('.next-button').click(function(event) {
			// set the img src
			var index = xgds_image.getCurrentImageAndIndex(el);
			if (index != null) {
			    index = index + 1;
			    if (index == imageSetsArray.length){
			    	index = 0;
			    }
			    clearTableSelection(theDataTable);
			    xgds_image.updateImageView(el, null, index, false, false);
			}
		});
		el.find(".prev-button").click(function(event) {
			var index = xgds_image.getCurrentImageAndIndex(el);
			if (index != null) {
			    index = index - 1;
			    if (index < 0){
				index = imageSetsArray.length - 1;
			    }
			    clearTableSelection(theDataTable);
			    xgds_image.updateImageView(el, null, index, false, false);
			}
		});
	},
	setChangedPosition: function(value, el){
		$(el).find('#id_changed_position').attr('value', value);
	},
	hookEditingPosition: function(el){
		// Set the changed_position to true if the user edits any of the position fields.
		$(el).find("#id_latitude").change(function() {setChangedPosition(1, el)});
		$(el).find("#id_longitude").change(function() {setChangedPosition(1, el)});
		$(el).find("#id_altitude").change(function() {setChangedPosition(1, el)});
		$(el).find("#id_heading").change(function() {setChangedPosition(1, el)});
	},
	onUpdateImageInfo: function(el) {
		// Saves image info to the db when user updates it and submits.
		$(el).find("#updateInfoSubmit").on('click', function(event) {
			event.preventDefault(); 	// avoid to execute the actual submit of the form.
			var url = updateImageUrl; // the script where you handle the form input.
			var postData = $("#more-info-form").serialize();
			$.ajax({
				url: url,
				type: "POST",
				data: postData, // serializes the form's elements.
				success: function(data)
				{
				    xgds_image.setSaveStatusMessage($('#message'), 'Saved','');
				    xgds_image.updateImageView(el, data[0], undefined, true, true);
				    el.find("#more_info_view").hide();
				    el.find("#image_overview").show();
				},
				error: function(request, status, error) {
				    xgds_image.setSaveStatusMessage($('#message'), status, error);
				}
			});
			return false;
		});
	},
	getCurrentImageAndIndex: function(el){
		// Helper that finds index in imageSetsArray
		// of the currently displayed image in the imageView.
		var currentImageName = el.attr('pk');
		var currentImageIndex = null;
		for (var i=0; i< imageSetsArray.length; i++) {
			if (imageSetsArray[i]['name'] == currentImageName) {
				currentImageIndex = i;
				break;
			}
		}
		return currentImageIndex;
	},
	updateImageView: function(el, imageJson, index, keepingImage, keepingNotes) {
		if (imageJson == null){
			if (index != null) {
				imageJson = imageSetsArray[index];
			} else {
				return;
			}
		}

		if (!keepingNotes){
			var tbl = el.find('table#notes_list');
			initializeNotesReference(el, imageJson['app_label'], imageJson['model_type'], imageJson['pk'], imageJson['acquisition_time'], imageJson['acquisition_timezone']);
			getNotesForObject(imageJson['app_label'], imageJson['model_type'], imageJson['pk'], 'notes_content', tbl);
		}

		var newContent = this.compiledTemplate(imageJson);
		$(el).html(newContent);

		if (!keepingImage){
//			el.attr('id', imageJson['name']);
			this.loadImageInViewer(imageJson);
	    }
//		el.find(".image-name strong").text(imageJson['name']);
//
//		// update values
//		el.find('a#new-window-target').attr('href',imageJson['view_url']);
//		el.find('#id_pk').attr('value', imageJson['pk']);
//		el.find('#overview_description').text(imageJson['description']);
//		el.find('textarea[name="description"]').attr('value', imageJson['description']);
//		el.find('input[name="name"]').attr('value', imageJson['name']);
//		el.find('input[name="latitude"]').attr('value', imageJson['lat']);
//		el.find('input[name="longitude"]').attr('value', imageJson['lon']);
//		el.find('input[name="altitude"]').attr('value', imageJson['altitude']);
//		el.find('input[name="heading"]').attr('value', imageJson['heading']);
//		el.find('#id_changed_position').attr('value', 0);
	},
	hideImageNextPrev: function() {
		$('.prev-button').hide();
		$('.next-button').hide();
		$('#new-window-btn').hide();
	},
	constructImageView: function(imageJson, viewPage){
		var imageViewWidget = $("#image_view");
		if (imageViewWidget.length) {
			this.updateImageView(imageViewWidget, imageJson, null, false, false);
		} else {
	        Handlebars.registerHelper('prettyTime', function( sourceTime, timeZone ){
	        	return getLocalTimeString(sourceTime, timeZone);
	        });

			viewPage = typeof viewPage !== 'undefined' ? viewPage : false;
			var rawTemplate = $('#template-image-view').html();
			this.compiledTemplate = Handlebars.compile(rawTemplate);

			var newDiv = this.compiledTemplate(imageJson);
			var imageViewTemplate = $(newDiv);

			// callbacks
			this.hookEditingPosition(imageViewTemplate);
			this.onUpdateImageInfo(imageViewTemplate);
			this.activateButtons(imageViewTemplate);
			this.hookDelete();

			if (!viewPage){
				this.onImageNextOrPrev(imageViewTemplate);
			}

			// append the div to the container and gridstack.
			var newEl;
			var container = $('#container');

			if (!viewPage){
				newEl = container.append(imageViewTemplate);
			} else {
				newEl = container.prepend(imageViewTemplate);
			}
			// add the element to the dashboard
			if (!viewPage){
				addItem(imageViewTemplate, 3, 3, 3, 2);
			}
			//	// set the loading image to be displayed when main img is loading
			//	imageViewTemplate.find(".display-image").load(function() {
			//	// set dimensions of loading image
			//	var width = imageViewTemplate.find(".display-image").width();
			//	var height = imageViewTemplate.find(".display-image").height();
			//	imageViewTemplate.find(".loading-image").width(width);
			//	imageViewTemplate.find(".loading-image").height(height);
			//	imageViewTemplate.find(".loading-image").hide();
			//	});
			//	setChangedPosition(0, imageViewTemplate);

			//add the notes if it does not exist
			var notes_content_div = imageViewTemplate.find("#notes_content");
			var notes_table = undefined;
			if ($(notes_content_div).is(':empty')){
				// the first time we want to fill it in
				notes_table = $.find("table#notes_list");
				var notes_input_div = $.find("#notes_input");

				var new_input_div = $(notes_input_div).hide();
				$(notes_content_div).append(new_input_div);

				var new_table_div = $(notes_table);
				$(notes_content_div).append(new_table_div);
				$(new_table_div).removeAttr('hidden');
				$(new_table_div).show();

				var taginput = $(new_input_div).find('.taginput');
				initializeInput(taginput);
				hookNoteSubmit();

			} else {
				notes_table = imageViewTemplate.find("table#notes_list");
			}

			initializeNotesReference(imageViewTemplate, imageJson['app_label'], imageJson['model_type'], imageJson['id'], imageJson['creation_time']);
			getNotesForObject(imageJson['app_label'], imageJson['model_type'], imageJson['id'], 'notes_content', $(notes_table));

			// set the gridstack image height
			$('.image_view_outer').attr('data-gs-height', '4');

			this.setupImageViewer(imageJson);
		}
	},
	setupImageViewer: function(imageJson){

		if (this.viewer != undefined){
			this.viewer.destroy();
			this.viewer = null;
		}
		// build tile sources for openseadragon image viewer
		var prefixUrl = '/static/openseadragon/built-openseadragon/openseadragon/images/';
		this.viewer = OpenSeadragon({
			id: "display-image",
			prefixUrl: prefixUrl,
			tileSources: {
				type: 'image',
				url: imageJson.raw_image_url
			}
		});

	},
	loadImageInViewer: function(imageJson){
		this.setupImageViewer(imageJson);
		return;
    	// load new image into OpenSeadragon viewer
		if (this.viewer == undefined){
			this.setupImageViewer(imageJson);
		} else {
			this.viewer.open({type: 'image',
							  url: imageJson.raw_image_url});
		}
	},
	setSaveStatusMessage: function(handler, status, msg){
		if (status == 'success') {
			handler.attr('class', 'success-message');
		} else {
			handler.attr('class', 'error-message');
		}
		handler.html(msg);
		setTimeout(function() { // messages fades out.
			handler.fadeOut().empty();
		}, 5000);
	}
});
<|endoftext|>


<commit_before>
maxId = 0;
maxColorId = 0;

mainFoodChain= JSON.parse('[ { "type": "family", "id": 1, "gators": [ { "type": "gator", "id": 2, "colorId": 1 } ], "foodChain": [ { "type": "egg", "id": 3, "colorId": 1 }, { "type": "family", "id": 4, "gators": [ { "type": "gator", "id": 6, "colorId": 2 }, { "type": "gator", "id": 7, "colorId": 3 } ], "foodChain": [ { "type": "egg", "id": 8, "colorId": 3 } ] }, { "type": "family", "id": 5, "gators": [ { "type": "gator", "id": 9, "colorId": 4 }, { "type": "gator", "id": 10, "colorId": 5 } ], "foodChain": [ { "type": "egg", "id": 11, "colorId": 4 } ] } ] } ]');

function getMaxIds(foodChain){
  for (var i = 0; i < foodChain.length; i++) {
    if(foodChain[i].id>maxId){
      maxId = foodChain[i].id;
    }

    if(foodChain[i].type=="family"){
      getMaxIds(foodChain[i].foodChain);

      for (var j = 0; j < foodChain[i].gators.length; j++) {
        if(foodChain[i].gators[j].colorId > maxColorId){
          maxColorId = foodChain[i].gators[j].colorId;
        }

        if(foodChain[i].gators[j].id>maxId){
          maxId = foodChain[i].id;
        }
      }
    }else{
      if(foodChain[i].colorId > maxColorId){
        maxColorId = foodChain[i].colorId;
      }
    }
  }
}

function reduce(foodChain){
  if(len(foodChain[0].gators)>0){//alpha reduce or done
    if(len())
    gator = foodChain[0].gators[0];
    food = families[1];
    //search through eater's foodChain recursively and replace eggs with a matching
  }else{//eta reduce

  }
}

function isEqual(foodChain1, foodChain2) {
	if (foodChain1.length != foodChain2.length) return 0;
	for (var i = 0; i < foodChain1.length; i++)
		if (foodChain1[i].type != foodChain2[i].type) return 0;
	var eggMap = {};
	var gatorMap = {};
	// Iterate over families and eggs (elements)
	for (var i = 0; i < foodChain1.length; i++) {
		var element1 = foodChain1[i];
		var element2 = foodChain2[i];
		if (element1.type == "egg") {
			// if we already have a mapping for this color but it's different now, the food-chains are different
			if (element1.colorId in eggMap && eggMap[element1.colorId] != element2.colorId)
				return 0;
			// if we haven't seen this color yet, add this mapping
			else if (!(element1 in eggMap))
				eggMap[element1.colorId] = element2.colorId;
		}
		else if (element1.type == "family") {
			if (element1.gators.length != element2.gators.length) return 0;
			if (element1.foodChain.length != element2.foodChain.length) return 0;
			// Iterate over gators
			var gators1 = element1.gators;
			var gators2 = element2.gators;
			for (var i = 0; i < gators1.length(); i++) {
			// if we already have a mapping for this color but it's different now, the food-chains are different
			if (element1.colorId in eggMap && eggMap[element1.colorId] != element2.colorId)
				if (gators1[i].colorId in gatorMap) && gatorMap[gators1[i].colorId] != gators2[i].colorId)
					return 0;
				// if we haven't seen this color yet, add this mapping
				else if (!(gators1[i] in gatorMap))
					gatorMap[gators1[i].colorId] = gators2[i].colorId;
			}
			// Recurse for sub-foodChains
			if (!isEqual(element1.foodChain, element2.foodChain)) return 0;
		}
	}
	return 1;
}

function replaceEggs(family, colorId, newFamily){
  for (var i = 0; i >= family.foodChain.length; i++) {
    if(family.foodChain[i].type=="family"){
      replaceEggs(family.foodChain[i], colorId, newFamily);
    }
  }
}

getMaxIds(mainFoodChain);
console.log(maxId);
console.log(maxColorId);

/*
family:
  type="family"
  id
  gators = []//gators
  foodChain = []//eggs or families

gator:
  type="gator"
  id
  colorId

egg:
  type="egg"
  id
  colorId
<<<<<<< HEAD

*/
<commit_msg>Switched color mapping in equality checker to bidirectional. One direction won't work.<commit_after>maxId = 0;
maxColorId = 0;

mainFoodChain1 = JSON.parse('[ { "type": "family", "id": 1, "gators": [ { "type": "gator", "id": 2, "colorId": 1 } ], "foodChain": [ { "type": "egg", "id": 3, "colorId": 1 }, { "type": "family", "id": 4, "gators": [ { "type": "gator", "id": 6, "colorId": 2 }, { "type": "gator", "id": 7, "colorId": 3 } ], "foodChain": [ { "type": "egg", "id": 8, "colorId": 3 } ] }, { "type": "family", "id": 5, "gators": [ { "type": "gator", "id": 9, "colorId": 4 }, { "type": "gator", "id": 10, "colorId": 5 } ], "foodChain": [ { "type": "egg", "id": 11, "colorId": 4 } ] } ] } ]');
mainFoodChain2 = JSON.parse('[ { "type": "family", "id": 1, "gators": [ { "type": "gator", "id": 2, "colorId": 1 } ], "foodChain": [ { "type": "egg", "id": 3, "colorId": 1 }, { "type": "family", "id": 4, "gators": [ { "type": "gator", "id": 6, "colorId": 2 }, { "type": "gator", "id": 7, "colorId": 3 } ], "foodChain": [ { "type": "egg", "id": 8, "colorId": 3 } ] }, { "type": "family", "id": 5, "gators": [ { "type": "gator", "id": 9, "colorId": 4 }, { "type": "gator", "id": 10, "colorId": 5 } ], "foodChain": [ { "type": "egg", "id": 11, "colorId": 4 } ] } ] } ]');

function getMaxIds(foodChain){
  for (var i = 0; i < foodChain.length; i++) {
    if(foodChain[i].id>maxId){
      maxId = foodChain[i].id;
    }

    if(foodChain[i].type=="family"){
      getMaxIds(foodChain[i].foodChain);

      for (var j = 0; j < foodChain[i].gators.length; j++) {
        if(foodChain[i].gators[j].colorId > maxColorId){
          maxColorId = foodChain[i].gators[j].colorId;
        }

        if(foodChain[i].gators[j].id>maxId){
          maxId = foodChain[i].id;
        }
      }
    }else{
      if(foodChain[i].colorId > maxColorId){
        maxColorId = foodChain[i].colorId;
      }
    }
  }
}

function reduce(foodChain){
  if(len(foodChain[0].gators)>0){//alpha reduce or done
    if(len())
    gator = foodChain[0].gators[0];
    food = families[1];
    //search through eater's foodChain recursively and replace eggs with a matching
  }else{//eta reduce

  }
}

function isEqual(foodChain1, foodChain2) {
	if (foodChain1.length != foodChain2.length) return 0;
	for (var i = 0; i < foodChain1.length; i++)
		if (foodChain1[i].type != foodChain2[i].type) return 0;
	var eggMap1 = {};
	var eggMap2 = {};
	var gatorMap1 = {};
	var gatorMap2 = {};
	// Iterate over families and eggs (elements)
	for (var i = 0; i < foodChain1.length; i++) {
		var element1 = foodChain1[i];
		var element2 = foodChain2[i];
		if (element1.type == "egg") {
			// if we already have a mapping for this color but it's different now, the food-chains are different
			if ((element1.colorId in eggMap1) && eggMap1[element1.colorId] != element2.colorId)
				return 0;
			if ((element2.colorId in eggMap2) && eggMap2[element2.colorId] != element1.colorId)
				return 0;
			// if we haven't seen this color yet, add this mapping
			if (!(element1 in eggMap1))
				eggMap1[element1.colorId] = element2.colorId;
			if (!(element2 in eggMap2))
				eggMap2[element2.colorId] = element1.colorId;
			//console.log(eggMap);
		}
		else if (element1.type == "family") {
			if (element1.gators.length != element2.gators.length) return 0;
			if (element1.foodChain.length != element2.foodChain.length) return 0;
			// Iterate over gators
			var gators1 = element1.gators;
			var gators2 = element2.gators;
			for (var i = 0; i < gators1.length; i++) {
				console.log("1: ", gators1[i]);
				console.log("2: ", gators2[i]);
			}
			for (var i = 0; i < gators1.length; i++) {
				// if we already have a mapping for this color but it's different now, the food-chains are different
				if ((gators1[i].colorId in gatorMap1) && gatorMap1[gators1[i].colorId] != gators2[i].colorId)
					return 0;
				if ((gators2[i].colorId in gatorMap2) && gatorMap2[gators2[i].colorId] != gators1[i].colorId)
					return 0;
				// if we haven't seen this color yet, add this mapping
				if (!(gators1[i] in gatorMap1))
					gatorMap1[gators1[i].colorId] = gators2[i].colorId;
				if (!(gators2[i] in gatorMap2))
					gatorMap2[gators2[i].colorId] = gators1[i].colorId;
				//console.log(gatorMap);
			}
			// Recurse for sub-foodChains
			if (!isEqual(element1.foodChain, element2.foodChain)) return 0;
		}
	}
	return 1;
}

function replaceEggs(family, colorId, newFamily){
  for (var i = 0; i >= family.foodChain.length; i++) {
    if(family.foodChain[i].type=="family"){
      replaceEggs(family.foodChain[i], colorId, newFamily);
    }
  }
}

getMaxIds(mainFoodChain1);
//console.log(maxId);
//console.log(maxColorId);
console.log(isEqual(mainFoodChain1, mainFoodChain2));

/*
family:
  type="family"
  id
  gators = []//gators
  foodChain = []//eggs or families

gator:
  type="gator"
  id
  colorId

egg:
  type="egg"
  id
  colorId

*/
<|endoftext|>


<commit_before>import { promise, arr } from "lively.lang";
import { Rectangle } from "lively.graphics";

function createIFrame(parentElement, url = "about:blank", bounds = new Rectangle(0,0, 700,700)) {
  return new Promise((resolve, reject) => {
    if (!parentElement) throw new Error("Need parent element!")
    var iframe = document.createElement('iframe');
    iframe.style.position = "absolute";
    iframe.style.top = bounds.top() + 'px';
    iframe.style.left = bounds.left() + 'px';
    iframe.style.width = bounds.width + 'px';
    iframe.style.height = bounds.height + 'px';
    iframe.src = url;
    var loaded = false;
    iframe.onload = function(evt) { loaded = true; iframe.onload = null; resolve(iframe); };
    promise.waitFor(1000, () => !!loaded).catch((err) => reject(new Error("iframe load timeout")))
    parentElement.appendChild(iframe);
  })
};

function requestAnimationFramePolyfill(window) {
  // based on requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
  // MIT license
  // https://gist.github.com/paulirish/1579671
  var lastTime = 0,
      vendors = ['ms', 'moz', 'webkit', 'o'];
  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
      window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
                                 || window[vendors[x]+'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame)
    window.requestAnimationFrame = function(callback, element) {
      var currTime = Date.now(),
          timeToCall = Math.max(0, 16 - (currTime - lastTime)),
          id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };

  if (!window.cancelAnimationFrame)
      window.cancelAnimationFrame = function(id) { clearTimeout(id); };
}

async function createDOMEnvironment_browser() {
  var iframe = await createIFrame(document.body)
  requestAnimationFramePolyfill(iframe.contentWindow);
  return {
    iframe: iframe,
    window: iframe.contentWindow,
    document: iframe.contentWindow.document,
    destroy() {
      iframe.contentWindow && iframe.contentWindow.close();
      iframe.parentNode && iframe.parentNode.removeChild(iframe);
    }
  }
}

function createDOMEnvironment_node() {
  var morphicDir = System.decanonicalize("lively.morphic/").replace(/file:\/\//, ""),
      jsdom = System._nodeRequire(morphicDir + "node_modules/jsdom"),
      virtualConsole = jsdom.createVirtualConsole().sendTo(console);
  return new Promise((resolve, reject) => {
    jsdom.env(
      '

',
      ["https://code.jquery.com/pep/0.4.1/pep.js"],
      { virtualConsole },
      function (err, window) {
        requestAnimationFramePolyfill(window);
        err ? reject(err) : resolve({
          window: window,
          document: window.document,
          destroy() { window.close(); }
        })
      });
  })
}

export function createDOMEnvironment() {
  return System.get("@system-env").browser ?
    createDOMEnvironment_browser() :
    createDOMEnvironment_node();
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

function setCSSDef(node, cssDefString, doc) {
  arr.from(node.childNodes).forEach(c => node.removeChild(c));
  var rules = doc.createTextNode(cssDefString);
  if (node.styleSheet) node.styleSheet.cssText = rules.nodeValue
  else node.appendChild(rules);
  return node;
}

function addCSSDef(id, cssString, doc) {
  var style = doc.createElement('style');
  style.type = 'text/css';
  if (id) style.setAttribute('id', id);
  setCSSDef(style, cssString, doc);
  doc.head.appendChild(style);
  return style;
}

export function addOrChangeCSSDeclaration(id = "lively-css", cssString, doc = document) {
  var node = doc.getElementById(id);
  return node ?
    setCSSDef(node, cssString, doc) :
    addCSSDef(id, cssString, doc);
}

export function addOrChangeLinkedCSS(id, url, doc = document) {
  var link = doc.getElementById(id);
  if (!link) {
    link = doc.createElement('link');
    link.type = 'text/css';
    link.rel = "stylesheet";
    link.setAttribute('id', id);
    doc.head.appendChild(link);
  }
  link.setAttribute('href', url);
  return link;
}
<commit_msg>browserDOMEnvironment<commit_after>import { promise, arr } from "lively.lang";
import { Rectangle } from "lively.graphics";

function createIFrame(parentElement, url = "about:blank", bounds = new Rectangle(0,0, 700,700)) {
  return new Promise((resolve, reject) => {
    if (!parentElement) throw new Error("Need parent element!")
    var iframe = document.createElement('iframe');
    iframe.style.position = "absolute";
    iframe.style.top = bounds.top() + 'px';
    iframe.style.left = bounds.left() + 'px';
    iframe.style.width = bounds.width + 'px';
    iframe.style.height = bounds.height + 'px';
    iframe.src = url;
    var loaded = false;
    iframe.onload = function(evt) { loaded = true; iframe.onload = null; resolve(iframe); };
    promise.waitFor(1000, () => !!loaded).catch((err) => reject(new Error("iframe load timeout")))
    parentElement.appendChild(iframe);
  })
};

function requestAnimationFramePolyfill(window) {
  // based on requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
  // MIT license
  // https://gist.github.com/paulirish/1579671
  var lastTime = 0,
      vendors = ['ms', 'moz', 'webkit', 'o'];
  for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
      window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
                                 || window[vendors[x]+'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame)
    window.requestAnimationFrame = function(callback, element) {
      var currTime = Date.now(),
          timeToCall = Math.max(0, 16 - (currTime - lastTime)),
          id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };

  if (!window.cancelAnimationFrame)
      window.cancelAnimationFrame = function(id) { clearTimeout(id); };
}

async function createDOMEnvironment_browser() {
  var iframe = await createIFrame(document.body)
  requestAnimationFramePolyfill(iframe.contentWindow);
  return {
    iframe: iframe,
    window: iframe.contentWindow,
    document: iframe.contentWindow.document,
    destroy() {
      iframe.contentWindow && iframe.contentWindow.close();
      iframe.parentNode && iframe.parentNode.removeChild(iframe);
    }
  }
}

function createDOMEnvironment_node() {
  var morphicDir = System.decanonicalize("lively.morphic/").replace(/file:\/\//, ""),
      jsdom = System._nodeRequire(morphicDir + "node_modules/jsdom"),
      virtualConsole = jsdom.createVirtualConsole().sendTo(console);
  return new Promise((resolve, reject) => {
    jsdom.env(
      '

',
      ["https://code.jquery.com/pep/0.4.1/pep.js"],
      { virtualConsole },
      function (err, window) {
        requestAnimationFramePolyfill(window);
        err ? reject(err) : resolve({
          window: window,
          document: window.document,
          destroy() { window.close(); }
        })
      });
  })
}

export function createDOMEnvironment() {
  return System.get("@system-env").browser ?
    createDOMEnvironment_browser() :
    createDOMEnvironment_node();
}

export function browserDOMEnvironment() {
  // sync alternative
  if (!System.get("@system-env").browser)
    throw new Error("browserEnvironment() only works in browsers!");
  requestAnimationFramePolyfill(window);
  return {
    destroy() {},
    window: window,
    document: document
  }
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

function setCSSDef(node, cssDefString, doc) {
  arr.from(node.childNodes).forEach(c => node.removeChild(c));
  var rules = doc.createTextNode(cssDefString);
  if (node.styleSheet) node.styleSheet.cssText = rules.nodeValue
  else node.appendChild(rules);
  return node;
}

function addCSSDef(id, cssString, doc) {
  var style = doc.createElement('style');
  style.type = 'text/css';
  if (id) style.setAttribute('id', id);
  setCSSDef(style, cssString, doc);
  doc.head.appendChild(style);
  return style;
}

export function addOrChangeCSSDeclaration(id = "lively-css", cssString, doc = document) {
  var node = doc.getElementById(id);
  return node ?
    setCSSDef(node, cssString, doc) :
    addCSSDef(id, cssString, doc);
}

export function addOrChangeLinkedCSS(id, url, doc = document) {
  var link = doc.getElementById(id);
  if (!link) {
    link = doc.createElement('link');
    link.type = 'text/css';
    link.rel = "stylesheet";
    link.setAttribute('id', id);
    doc.head.appendChild(link);
  }
  link.setAttribute('href', url);
  return link;
}
<|endoftext|>