<commit_before>define([
    'uijet_dir/uijet',
    'resources',
    'api',
    'd3'
], function (uijet, resources, api) {

    var Y_TICKS = 5;

    var d3 = window.d3,
        period = uijet.utils.prop('period'),
        amount = uijet.utils.prop('amount'),
        dateParser = d3.time.format('%Y').parse,
        commas = d3.format(','),
        periodParser = function (d) {
            d.period = dateParser(d.period);
        };

    uijet.Widget('TimelineChart', {
        options         : {
            type_class  : ['uijet_chart', 'uijet_timelinechart']
        },
        init            : function () {
            this._super.apply(this, arguments);
            this.colors = d3.scale.category10();
            uijet.publish('chart_colors', this.colors.range());
        },
        prepareElement  : function () {
            this._super();
            var element = this.$element[0],
                padding = 20,
                left_sidebar_width = 100,
                width = element.offsetWidth,
                // need to expand the width of the SVG element to be able to draw y axis labels outside the chart area
                svg_width = width + padding + left_sidebar_width,
                height = element.offsetHeight,
//                x = d3.time.scale()
                x = d3.time.scale()
                    .range([padding, width - padding]),
                y = d3.scale.linear()
                    .range([height, padding]),
                x_axis = d3.svg.axis()
                    .scale(x)
                    .orient('bottom')
                    .ticks(d3.time.years),
                y_axis = d3.svg.axis()
                    .scale(y)
                    .orient('left')
                    .ticks(Y_TICKS);

            this.padding = padding;
            this.left_sidebar_width = left_sidebar_width;
            this.svg_width = svg_width;
            this.width = width;
            this.height = height;
            this.x_axis = x_axis;
            this.y_axis = y_axis;
            this.x_scale = x;
            this.y_scale = y;

            this.line = d3.svg.line()
                .x( function(d) { return x(d.period); } )
                .y( function(d) { return y(d.amount); } );

            this.svg_element = d3.select(this.$element[0]).append('svg')
                .attr('width', svg_width)
                .attr('height', height + 70);
            this.svg = this.svg_element.append('g')
                .attr('transform', 'translate(' + (svg_width - width) + ',0)')
                .append('svg')
                    .attr('width', width)
                    .attr('height', height + 70);
            this.mouse_target = this.svg.append('rect')
                .attr('height', height)
                .attr('width', width)
                .attr('id', 'mouse_target');
            return this;
        },
        _draw           : function () {
            return this.publish('fetched', this.resource)
                .draw(this.resource.models);
        },
        render          : function () {
            this._super();

            if ( this.context && this.context.state_loaded ) {
                this._draw();
                delete this.context.state_loaded;
            }
            else {
                this.set(uijet.Resource('LegendItems').models).then(this._draw.bind(this));
            }
            return this;
        },
        draw            : function (series) {
            var line = this.line,
                colors = this.colors,
                ids = [],
                data = [],
                y_max;

            series.forEach(function (item) {
                var id = item.id + '-' + item.get('updated'),
                    title = item.get('title'),
                    muni = item.get('muni'),
                    type = item.get('amount_type'),
                    series_type_index = type === 'actual' ? 0 : 1,
                    item_series = item.toSeries();
                item_series[series_type_index].forEach(periodParser);
                ids.push(id);
                data.push({
                    id      : id,
                    title   : title,
                    type    : type,
                    muni    : muni,
                    values  : item_series[series_type_index]
                });
            });

            y_max = d3.max(data, function (d) { return d3.max(d.values, amount); });

            this.colors.domain(ids);

            this.x_scale.domain([
                d3.min(data, function (d) { return d3.min(d.values, period); }),
                d3.max(data, function (d) { return d3.max(d.values, period); })
            ]);
            this.y_scale.domain([
                0,
                y_max
            ]);

            // clean axes
            this.svg_element.selectAll('.axis')
                .remove();

            this.svg.insert('g', '#mouse_target')
                .attr('class', 'axis x_axis')
                .attr('transform', 'translate(0,' + (this.height + this.padding) + ')')
                .call(this.x_axis);

            this.svg_element.insert('g', ':first-child')
                .attr('class', 'axis y_axis')
                .attr('transform', 'translate(' + this.left_sidebar_width + ',0)')
                .call(this.y_axis)
                .selectAll('line')
                    .attr('x2', this.padding)
                    .attr('x1', this.width + this.padding);

            this.svg.selectAll('.timeline').remove();

            var timelines = this.svg.selectAll('.timeline')
                .data(data, function (d) {
                    return d.id;
                });

            timelines.enter()
                .insert('g', '#mouse_target')
                    .attr('class', 'timeline')
                    .append('path')
                        .attr('class', 'line')
                        .attr('d', function(d) {
                            return line(d.values);
                        })
                        .style('stroke', function(d) { return colors(d.id); });

            this.mouse_target.on('mouseover', this.hoverOn.bind(this));
            this.mouse_target.on('mouseout', this.hoverOff.bind(this));
        },
        timeContext     : function (from, to) {
            var domain = this.x_scale.domain(),
                line = this.line;
            if ( ! from ) {
                from = domain[0];
            }
            else {
                from = dateParser(from);
            }
            if ( ! to ) {
                to = domain[1];
            }
            else {
                to = dateParser(to);
            }
            this.x_scale.domain([from, to]);
            this.svg.select('.x_axis')
                .call(this.x_axis)
                .selectAll('line')
                    .attr('x1', 0)
                    .attr('y2', -this.padding)
                    .attr('y1', -(this.height));
            this.svg.selectAll('.line').attr('d', function (d) {
                return line(d.values);
            });

            // reset mouseover handler
            this.hoverOff();
            this.mouse_target.on('mouseover', this.hoverOn.bind(this));

            return this;
        },
        hoverOn         : function () {
            this.mouse_target.on('mousemove', this.mousemove());
        },
        hoverOff        : function () {
            this.mouse_target.on('mousemove', null);
            this.hoverMark();
            this.current_hovered_index = null;
        },
        mousemove       : function () {
            var x = this.x_scale,
                ticks_values = x.ticks(d3.time.years),
                x_ticks_coords = ticks_values.map(function (value) {
                    return x(value);
                }),
                that = this;

            return function () {
                var mouse_x = d3.mouse(this)[0],
                    right_index = d3.bisectLeft(x_ticks_coords, mouse_x),
                    left_index = right_index ? right_index - 1 : right_index,
                    new_index;
                if ( right_index !== left_index ) {
                    if ( mouse_x - x_ticks_coords[left_index] < x_ticks_coords[right_index] - mouse_x ) {
                        new_index = left_index;
                    }
                    else {
                        new_index = right_index;
                    }
                }
                else {
                    new_index = right_index;
                }
                if ( that.current_hovered_index !== new_index ) {
                    that.current_hovered_index = new_index;
                    that.hoverMark(ticks_values[new_index]);
                }
            };
        },
        hoverMark       : function (value) {
            if ( value ) {
                //TODO: this is based on assumption that data is yearly
                var year = value.getFullYear();
                d3.select('.x_axis').selectAll('.tick').classed('mark', function (d) {
                    return d.getFullYear() === year;
                });
                this.markValues(value);
            }
            else {
                d3.select('.tick.mark').classed('mark', false);
                d3.selectAll('.value_circle').remove();
            }
        },
        markValues      : function (x_value) {
            var labels,
                datums = [],
                x = this.x_scale,
                y = this.y_scale,
                width = this.width,
                color = this.colors,
                added_label, added_label_texts;
            d3.selectAll('.timeline').each(function (d, i) {
                d.values.some(function (point_datum) {
                    if ( point_datum.period.valueOf() === x_value.valueOf() ) {
                        datums.push({
                            period  : point_datum.period,
                            amount  : point_datum.amount,
                            id      : d.id,
                            title   : d.title,
                            muni    : d.muni
                        });
                        return true;
                    }
                });
            });
            datums.sort(function (a, b) {
                return a.amount - b.amount;
            });
            datums.forEach(function (d, i) {
                d.x = x(d.period);
                d.y = y(d.amount);
                d.color = color(d.id);
            });

            labels = this.svg.selectAll('.value_circle').data(datums, function (d) { return d.id + d.period; });

            labels.exit().remove();

            added_label = labels.enter().append('g')
                .attr('class', 'value_circle');

            added_label_texts = added_label.append('g')
                .attr('class', 'value_label')
                .attr('transform', 'translate(0,-10)');

//            added_label_texts.append('text')
//                .attr('class', 'title');

            added_label_texts.append('text')
                .attr('class', 'amount');

            added_label.append('circle');

            labels.sort(function (a, b) { return a.amount - b.amount; });

            labels.attr('transform', function (d) {
                return 'translate(' + d.x + ',' + d.y + ')';
            });

            labels.selectAll('text')
                .attr('fill', function (d) { return d.color; });
            labels.selectAll('.amount')
                .text(function (d) { return commas(d.amount); })
                .attr('x', function () {
                    return - (this.getBBox().width + 10);
                });
//            labels.selectAll('.title')
//                .text(function (d) { return d.muni + ': ' + d.title; });

            d3.selectAll('.value_label')
                .attr('transform', function (d, i) {
                    var y_pos = -10,
                        x_pos = 0,
                        matrix = this.getCTM(),
                        bbox = this.getBBox(),
                        prev, prev_y, dy;
                    if ( i ) {
                        prev = datums[i - 1];
                        prev_y = prev.title_y || prev.y;
                        dy = prev_y - (d.y - 10);
                        // make sure we have a margin of 20px between labels' texts
                        if ( dy <= 0 ) {
                            y_pos = prev_y - d.y - 30
                        }
                        else if ( dy < 20 ) {
                            y_pos = -30;
                        }
                        else {
                            y_pos = -10;
                        }
                    }
                    // cache title position
                    d.title_y = d.y + y_pos;

                    // check if the label is exceeding the size of the canvas
                    x_pos = bbox.x + matrix.e;
                    if ( x_pos < 0 ) {
                        x_pos = -x_pos;
                    }
                    else if ( x_pos + bbox.width > width ) {
                        x_pos = width - (x_pos + bbox.width);
                    }
                    else {
                        x_pos = 0;
                    }

                    return 'translate(' + x_pos + ',' + y_pos + ')';
                });

            labels.select('circle')
                .attr('r', 5)
                .style('fill', function (d) { return d.color; });
        },
        setTitle        : function (id, title) {
            if ( uijet.utils.isObj(id) ) {
                title = id.title;
                id = id.id;
            }
            d3.selectAll('.timeline').filter(function (d) {
                return d.id.indexOf(id) === 0;
            }).datum(function (d) {
                d.title = title + ' ' + d.type;
                return d;
            });
            return this;
        }
    });

});
<commit_msg>Hide start/end x axis tick labels<commit_after>define([
    'uijet_dir/uijet',
    'resources',
    'api',
    'd3'
], function (uijet, resources, api) {

    var Y_TICKS = 5;

    var d3 = window.d3,
        period = uijet.utils.prop('period'),
        amount = uijet.utils.prop('amount'),
        dateParser = d3.time.format('%Y').parse,
        commas = d3.format(','),
        periodParser = function (d) {
            d.period = dateParser(d.period);
        };

    uijet.Widget('TimelineChart', {
        options         : {
            type_class  : ['uijet_chart', 'uijet_timelinechart']
        },
        init            : function () {
            this._super.apply(this, arguments);
            this.colors = d3.scale.category10();
            uijet.publish('chart_colors', this.colors.range());
        },
        prepareElement  : function () {
            this._super();
            var element = this.$element[0],
                padding = 20,
                left_sidebar_width = 100,
                width = element.offsetWidth,
                // need to expand the width of the SVG element to be able to draw y axis labels outside the chart area
                svg_width = width + padding + left_sidebar_width,
                height = element.offsetHeight,
//                x = d3.time.scale()
                x = d3.time.scale()
                    .range([padding, width - padding]),
                y = d3.scale.linear()
                    .range([height, padding]),
                x_axis = d3.svg.axis()
                    .scale(x)
                    .orient('bottom')
                    .ticks(d3.time.years),
                y_axis = d3.svg.axis()
                    .scale(y)
                    .orient('left')
                    .ticks(Y_TICKS);

            this.padding = padding;
            this.left_sidebar_width = left_sidebar_width;
            this.svg_width = svg_width;
            this.width = width;
            this.height = height;
            this.x_axis = x_axis;
            this.y_axis = y_axis;
            this.x_scale = x;
            this.y_scale = y;

            this.line = d3.svg.line()
                .x( function(d) { return x(d.period); } )
                .y( function(d) { return y(d.amount); } );

            this.svg_element = d3.select(this.$element[0]).append('svg')
                .attr('width', svg_width)
                .attr('height', height + 70);
            this.svg = this.svg_element.append('g')
                .attr('transform', 'translate(' + (svg_width - width) + ',0)')
                .append('svg')
                    .attr('width', width)
                    .attr('height', height + 70);
            this.mouse_target = this.svg.append('rect')
                .attr('height', height)
                .attr('width', width)
                .attr('id', 'mouse_target');
            return this;
        },
        _draw           : function () {
            return this.publish('fetched', this.resource)
                .draw(this.resource.models);
        },
        render          : function () {
            this._super();

            if ( this.context && this.context.state_loaded ) {
                this._draw();
                delete this.context.state_loaded;
            }
            else {
                this.set(uijet.Resource('LegendItems').models).then(this._draw.bind(this));
            }
            return this;
        },
        draw            : function (series) {
            var line = this.line,
                colors = this.colors,
                ids = [],
                data = [],
                y_max;

            series.forEach(function (item) {
                var id = item.id + '-' + item.get('updated'),
                    title = item.get('title'),
                    muni = item.get('muni'),
                    type = item.get('amount_type'),
                    series_type_index = type === 'actual' ? 0 : 1,
                    item_series = item.toSeries();
                item_series[series_type_index].forEach(periodParser);
                ids.push(id);
                data.push({
                    id      : id,
                    title   : title,
                    type    : type,
                    muni    : muni,
                    values  : item_series[series_type_index]
                });
            });

            y_max = d3.max(data, function (d) { return d3.max(d.values, amount); });

            this.colors.domain(ids);

            this.x_scale.domain([
                d3.min(data, function (d) { return d3.min(d.values, period); }),
                d3.max(data, function (d) { return d3.max(d.values, period); })
            ]);
            this.y_scale.domain([
                0,
                y_max
            ]);

            // clean axes
            this.svg_element.selectAll('.axis')
                .remove();

            this.svg.insert('g', '#mouse_target')
                .attr('class', 'axis x_axis')
                .attr('transform', 'translate(0,' + (this.height + this.padding) + ')')
                .call(this.x_axis);

            this.svg_element.insert('g', ':first-child')
                .attr('class', 'axis y_axis')
                .attr('transform', 'translate(' + this.left_sidebar_width + ',0)')
                .call(this.y_axis)
                .selectAll('line')
                    .attr('x2', this.padding)
                    .attr('x1', this.width + this.padding);

            this.svg.selectAll('.timeline').remove();

            var timelines = this.svg.selectAll('.timeline')
                .data(data, function (d) {
                    return d.id;
                });

            timelines.enter()
                .insert('g', '#mouse_target')
                    .attr('class', 'timeline')
                    .append('path')
                        .attr('class', 'line')
                        .attr('d', function(d) {
                            return line(d.values);
                        })
                        .style('stroke', function(d) { return colors(d.id); });

            this.mouse_target.on('mouseover', this.hoverOn.bind(this));
            this.mouse_target.on('mouseout', this.hoverOff.bind(this));
        },
        timeContext     : function (from, to) {
            var domain = this.x_scale.domain(),
                line = this.line,
                from_value, to_value, x_axis;
            if ( ! from ) {
                from = domain[0];
            }
            else {
                from = dateParser(from);
            }
            if ( ! to ) {
                to = domain[1];
            }
            else {
                to = dateParser(to);
            }

            from_value = from.valueOf();
            to_value = to.valueOf();

            this.x_scale.domain([from, to]);

            x_axis = this.svg.select('.x_axis');
            x_axis.call(this.x_axis)
                .selectAll('line')
                    .attr('x1', 0)
                    .attr('y2', -this.padding)
                    .attr('y1', -(this.height));
            x_axis.selectAll('text')
                .each(function (d) {
                    var value = d.valueOf(),
                        hide = value === to_value || value === from_value;
                    d3.select(this).classed('hide', hide);
                });

            this.svg.selectAll('.line').attr('d', function (d) {
                return line(d.values);
            });

            // reset mouseover handler
            this.hoverOff();
            this.mouse_target.on('mouseover', this.hoverOn.bind(this));

            return this;
        },
        hoverOn         : function () {
            this.mouse_target.on('mousemove', this.mousemove());
        },
        hoverOff        : function () {
            this.mouse_target.on('mousemove', null);
            this.hoverMark();
            this.current_hovered_index = null;
        },
        mousemove       : function () {
            var x = this.x_scale,
                ticks_values = x.ticks(d3.time.years),
                x_ticks_coords = ticks_values.map(function (value) {
                    return x(value);
                }),
                that = this;

            return function () {
                var mouse_x = d3.mouse(this)[0],
                    right_index = d3.bisectLeft(x_ticks_coords, mouse_x),
                    left_index = right_index ? right_index - 1 : right_index,
                    new_index;
                if ( right_index !== left_index ) {
                    if ( mouse_x - x_ticks_coords[left_index] < x_ticks_coords[right_index] - mouse_x ) {
                        new_index = left_index;
                    }
                    else {
                        new_index = right_index;
                    }
                }
                else {
                    new_index = right_index;
                }
                if ( that.current_hovered_index !== new_index ) {
                    that.current_hovered_index = new_index;
                    that.hoverMark(ticks_values[new_index]);
                }
            };
        },
        hoverMark       : function (value) {
            if ( value ) {
                //TODO: this is based on assumption that data is yearly
                var year = value.getFullYear();
                d3.select('.x_axis').selectAll('.tick').classed('mark', function (d) {
                    return d.getFullYear() === year;
                });
                this.markValues(value);
            }
            else {
                d3.select('.tick.mark').classed('mark', false);
                d3.selectAll('.value_circle').remove();
            }
        },
        markValues      : function (x_value) {
            var labels,
                datums = [],
                x = this.x_scale,
                y = this.y_scale,
                width = this.width,
                color = this.colors,
                added_label, added_label_texts;
            d3.selectAll('.timeline').each(function (d, i) {
                d.values.some(function (point_datum) {
                    if ( point_datum.period.valueOf() === x_value.valueOf() ) {
                        datums.push({
                            period  : point_datum.period,
                            amount  : point_datum.amount,
                            id      : d.id,
                            title   : d.title,
                            muni    : d.muni
                        });
                        return true;
                    }
                });
            });
            datums.sort(function (a, b) {
                return a.amount - b.amount;
            });
            datums.forEach(function (d, i) {
                d.x = x(d.period);
                d.y = y(d.amount);
                d.color = color(d.id);
            });

            labels = this.svg.selectAll('.value_circle').data(datums, function (d) { return d.id + d.period; });

            labels.exit().remove();

            added_label = labels.enter().append('g')
                .attr('class', 'value_circle');

            added_label_texts = added_label.append('g')
                .attr('class', 'value_label')
                .attr('transform', 'translate(0,-10)');

//            added_label_texts.append('text')
//                .attr('class', 'title');

            added_label_texts.append('text')
                .attr('class', 'amount');

            added_label.append('circle');

            labels.sort(function (a, b) { return a.amount - b.amount; });

            labels.attr('transform', function (d) {
                return 'translate(' + d.x + ',' + d.y + ')';
            });

            labels.selectAll('text')
                .attr('fill', function (d) { return d.color; });
            labels.selectAll('.amount')
                .text(function (d) { return commas(d.amount); })
                .attr('x', function () {
                    return - (this.getBBox().width + 10);
                });
//            labels.selectAll('.title')
//                .text(function (d) { return d.muni + ': ' + d.title; });

            d3.selectAll('.value_label')
                .attr('transform', function (d, i) {
                    var y_pos = -10,
                        x_pos = 0,
                        matrix = this.getCTM(),
                        bbox = this.getBBox(),
                        prev, prev_y, dy;
                    if ( i ) {
                        prev = datums[i - 1];
                        prev_y = prev.title_y || prev.y;
                        dy = prev_y - (d.y - 10);
                        // make sure we have a margin of 20px between labels' texts
                        if ( dy <= 0 ) {
                            y_pos = prev_y - d.y - 30
                        }
                        else if ( dy < 20 ) {
                            y_pos = -30;
                        }
                        else {
                            y_pos = -10;
                        }
                    }
                    // cache title position
                    d.title_y = d.y + y_pos;

                    // check if the label is exceeding the size of the canvas
                    x_pos = bbox.x + matrix.e;
                    if ( x_pos < 0 ) {
                        x_pos = -x_pos;
                    }
                    else if ( x_pos + bbox.width > width ) {
                        x_pos = width - (x_pos + bbox.width);
                    }
                    else {
                        x_pos = 0;
                    }

                    return 'translate(' + x_pos + ',' + y_pos + ')';
                });

            labels.select('circle')
                .attr('r', 5)
                .style('fill', function (d) { return d.color; });
        },
        setTitle        : function (id, title) {
            if ( uijet.utils.isObj(id) ) {
                title = id.title;
                id = id.id;
            }
            d3.selectAll('.timeline').filter(function (d) {
                return d.id.indexOf(id) === 0;
            }).datum(function (d) {
                d.title = title + ' ' + d.type;
                return d;
            });
            return this;
        }
    });

});
<|endoftext|>


<commit_before>import Ember from 'ember';
import EmptyObject from './util/empty-object';
import { isClass_, isOneOf } from './util/assert';

const {
  computed,
  copy,
  get,
  A
} = Ember;

export function create() {
  return computed(function() {
    return new Definition(this);
  }).readOnly();
}

const sofaKey = '__sofa';

function lookupProperties(modelClass) {
  let all = A();
  let byName = new EmptyObject();

  let store = get(modelClass, 'store');
  modelClass.eachComputedProperty((name, meta) => {
    if(!meta || meta[sofaKey] !== true) {
      return;
    }

    let property = meta.property;
    property.prepareModelClass(name, modelClass, store);

    all.push(property);
    byName[name] = property;
  });

  all.reverse();

  return { all, byName };
}

export default class Definition {

  constructor(modelClass) {
    this.modelClass = modelClass;
    this.properties = lookupProperties(modelClass);
  }

  get modelName() {
    let modelName = this._modelName;
    if(!modelName) {
      modelName = get(this.modelClass, 'modelName');
      this._modelName = modelName;
    }
    return modelName;
  }

  get type() {
    let property = this.property('type');
    return property.getDocumentKeyValue(this.modelClass);
  }

  eachProperty(fn) {
    this.properties.all.forEach(fn);
  }

  prepareInternalModel(internalModel, opts) {
    opts = copy(opts || {});
    internalModel.withPropertyChanges(changed => {
      this.eachProperty(property => {
        let key = property.prepareInternalModel(internalModel, opts, changed);
        if(key) {
          delete opts[key];
        }
      });
    }, false);
    return opts;
  }

  property(name) {
    return this.properties.byName[name];
  }

  matchesDocument(doc) {
    return this.property('type').matchesDocument(this.modelClass, doc);
  }

  willSerialize(internal, type='document') {
    this.eachProperty(property => {
      property.willSerialize(internal, type);
    });
  }

  serialize(internal, type='document') {
    isOneOf('type', type, [ 'preview', 'document', 'shoebox' ]);
    let doc = copy(internal.raw || {});
    this.eachProperty(property => {
      property.serialize(internal, doc, type);
    });
    return doc;
  }

  deserialize(internal, doc, changed, type=null) {
    let raw = copy(doc);
    this.eachProperty(property => {
      let key = property.deserialize(internal, doc, changed, type);
      if(key) {
        delete raw[key];
      }
    });
    internal.raw = raw;
  }

  deserializeProperty(internal, name, value, changed) {
    let property = this.property(name);
    let key = property.opts.key;
    let doc = { [key]: value };
    property.deserialize(internal, doc, changed);
  }

  deserializeSaveUpdateOrDelete(internal, json, changed) {
    this.deserializeProperty(internal, 'id', json.id, changed);
    this.deserializeProperty(internal, 'rev', json.rev, changed);
  }

  deserializeSaveOrUpdate(internal, json, changed) {
    this.deserializeSaveUpdateOrDelete(internal, json, changed);
  }

  deserializeDelete(internal, json, changed) {
    if(!json) {
      return;
    }
    this.deserializeSaveOrUpdate(internal, json, changed);
  }

  deserializeAttachments(internal, doc, changed) {
    this.property('attachments').deserialize(internal, doc, changed);
  }

  docId(modelId) {
    return this.property('id').docId(this.modelClass, modelId);
  }

  modelId(docId) {
    return this.property('id').modelId(this.modelClass, docId);
  }

  is(parent) {
    isClass_(`parent must be class not ${parent}`, parent);
    let arg = parent.superclass;
    let self = this.modelClass.superclass;
    while(self) {
      if(arg === self) {
        return true;
      }
      self = self.superclass;
      if(self === Ember.Object) {
        return false;
      }
    }
    return false;
  }

  onLoaded(internal, doc, changed, type=null) {
    this.deserialize(internal, doc, changed, type);
    internal.onLoaded(changed);
  }

  onSaved(internal, json, changed) {
    this.deserializeSaveOrUpdate(internal, json, changed);
    internal.onSaved(changed);
  }

  onDeleted(internal, json, changed) {
    this.deserializeDelete(internal, json, changed);
    internal.onDeleted(changed);
  }

}
<commit_msg>cleanup<commit_after>import Ember from 'ember';
import EmptyObject from './util/empty-object';
import { isClass_, isOneOf } from './util/assert';

const {
  computed,
  copy,
  get,
  A
} = Ember;

export function create() {
  return computed(function() {
    return new Definition(this);
  }).readOnly();
}

const sofaKey = '__sofa';

function lookupProperties(modelClass) {
  let all = A();
  let byName = new EmptyObject();

  let store = get(modelClass, 'store');
  modelClass.eachComputedProperty((name, meta) => {
    if(!meta || meta[sofaKey] !== true) {
      return;
    }

    let property = meta.property;
    property.prepareModelClass(name, modelClass, store);

    all.push(property);
    byName[name] = property;
  });

  all.reverse();

  return { all, byName };
}

export default class Definition {

  constructor(modelClass) {
    this.modelClass = modelClass;
    this.properties = lookupProperties(modelClass);
  }

  get modelName() {
    let modelName = this._modelName;
    if(!modelName) {
      modelName = get(this.modelClass, 'modelName');
      this._modelName = modelName;
    }
    return modelName;
  }

  get type() {
    let property = this.property('type');
    return property.getDocumentKeyValue(this.modelClass);
  }

  eachProperty(fn) {
    this.properties.all.forEach(fn);
  }

  prepareInternalModel(internalModel, opts) {
    opts = copy(opts || {});
    internalModel.withPropertyChanges(changed => {
      this.eachProperty(property => {
        let key = property.prepareInternalModel(internalModel, opts, changed);
        if(key) {
          delete opts[key];
        }
      });
    }, false);
    return opts;
  }

  property(name) {
    return this.properties.byName[name];
  }

  matchesDocument(doc) {
    return this.property('type').matchesDocument(this.modelClass, doc);
  }

  willSerialize(internal, type='document') {
    this.eachProperty(property => {
      property.willSerialize(internal, type);
    });
  }

  serialize(internal, type='document') {
    isOneOf('type', type, [ 'preview', 'document', 'shoebox' ]);
    let doc = copy(internal.raw || {});
    this.eachProperty(property => {
      property.serialize(internal, doc, type);
    });
    return doc;
  }

  deserialize(internal, doc, changed, type=null) {
    let raw = copy(doc);
    this.eachProperty(property => {
      let key = property.deserialize(internal, doc, changed, type);
      if(key) {
        delete raw[key];
      }
    });
    internal.raw = raw;
  }

  deserializeProperty(internal, name, value, changed) {
    let property = this.property(name);
    let key = property.opts.key;
    let doc = { [key]: value };
    property.deserialize(internal, doc, changed);
  }

  deserializeSaveUpdateOrDelete(internal, json, changed) {
    this.deserializeProperty(internal, 'id', json.id, changed);
    this.deserializeProperty(internal, 'rev', json.rev, changed);
  }

  deserializeSaveOrUpdate(internal, json, changed) {
    this.deserializeSaveUpdateOrDelete(internal, json, changed);
  }

  deserializeDelete(internal, json, changed) {
    if(!json) {
      return;
    }
    this.deserializeSaveUpdateOrDelete(internal, json, changed);
  }

  deserializeAttachments(internal, doc, changed) {
    this.property('attachments').deserialize(internal, doc, changed);
  }

  docId(modelId) {
    return this.property('id').docId(this.modelClass, modelId);
  }

  modelId(docId) {
    return this.property('id').modelId(this.modelClass, docId);
  }

  is(parent) {
    isClass_(`parent must be class not ${parent}`, parent);
    let arg = parent.superclass;
    let self = this.modelClass.superclass;
    while(self) {
      if(arg === self) {
        return true;
      }
      self = self.superclass;
      if(self === Ember.Object) {
        return false;
      }
    }
    return false;
  }

  onLoaded(internal, doc, changed, type=null) {
    this.deserialize(internal, doc, changed, type);
    internal.onLoaded(changed);
  }

  onSaved(internal, json, changed) {
    this.deserializeSaveOrUpdate(internal, json, changed);
    internal.onSaved(changed);
  }

  onDeleted(internal, json, changed) {
    this.deserializeDelete(internal, json, changed);
    internal.onDeleted(changed);
  }

}
<|endoftext|>


<commit_before>var GraphQL = require('graphql');
var keystoneTypes = require('./keystoneTypes');

var keystone = require('keystone');
var Meetup = keystone.list('Meetup');
var Talk = keystone.list('Talk');
var User = keystone.list('User');

function getMeetup (id) {
	if (id === 'next') {
		return Meetup.model.findOne().sort('-startDate')
						.where('state', 'active').exec();
	} else if (id === 'last') {
		return Meetup.model.findOne().sort('-startDate')
						.where('state', 'past').exec();
	} else {
		return Meetup.model.findById(id).exec();
	}
}

var meetupStateEnum = new GraphQL.GraphQLEnumType({
	name: 'MeetupState',
	description: 'The state of the meetup',
	values: {
		draft: {
			description: "No published date, it's a draft meetup",
		},
		scheduled: {
			description: "Publish date is before today, it's a scheduled meetup",
		},
		active: {
			description: "Publish date is after today, it's an active meetup",
		},
		past: {
			description: "Meetup date plus one day is after today, it's a past meetup",
		},
	},
});

var meetupType = new GraphQL.GraphQLObjectType({
	name: 'Meetup',
	fields: () => ({
		id: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The id of the meetup.',
		},
		name: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The name of the meetup.',
		},
		publishedDate: {
			type: GraphQL.GraphQLString,
		},
		state: {
			type: new GraphQL.GraphQLNonNull(meetupStateEnum),
		},
		startDate: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
		endDate: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
		place: {
			type: GraphQL.GraphQLString,
		},
		map: {
			type: GraphQL.GraphQLString,
		},
		description: {
			type: GraphQL.GraphQLString,
		},
		maxRSVPs: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLInt),
		},
		totalRSVPs: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLInt),
		},
		url: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
		remainingRSVPs: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLInt),
		},
		rsvpsAvailable: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLBoolean),
		},
		talks: {
			type: new GraphQL.GraphQLList(talkType),
			resolve: (source) => Talk.model.find().where('meetup', source.id).exec(),
		},
	}),
});

var userType = new GraphQL.GraphQLObjectType({
	name: 'User',
	fields: {
		name: {
			type: new GraphQL.GraphQLNonNull(keystoneTypes.name),
		},
		email: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
	},
});

var talkType = new GraphQL.GraphQLObjectType({
	name: 'Talk',
	fields: () => ({
		id: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The id of the talk.',
		},
		name: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The title of the talk.',
		},
		isLightningTalk: {
			type: GraphQL.GraphQLBoolean,
			description: 'Whether the talk is a Lightning talk',
		},
		meetup: {
			type: new GraphQL.GraphQLNonNull(meetupType),
			description: 'The Meetup the talk is scheduled for',
			resolve: (source, args, info) =>
				Meetup.model.findById(source.meetup).exec(),
		},
		who: {
			type: new GraphQL.GraphQLList(userType),
			description: 'A list of at least one User running the talk',
			resolve: (source, args, info) =>
				User.model.find().where('_id').in(source.who).exec(),
		},
		description: {
			type: GraphQL.GraphQLString,
		},
		slides: {
			type: GraphQL.GraphQLString,
		},
		link: {
			type: GraphQL.GraphQLString,
		},
	}),
});

var queryRootType = new GraphQL.GraphQLObjectType({
	name: 'Query',
	fields: {
		meetup: {
			type: meetupType,
			args: {
				id: {
					description: 'id of the meetup, can be "next" or "last"',
					type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
				},
			},
			resolve: (_, args) => getMeetup(args.id),
		},
		talk: {
			type: talkType,
			args: {
				id: {
					description: 'id of the talk',
					type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
				},
			},
			resolve: (_, args) => Talk.model.findById(args.id).exec(),
		},
	},
});

module.exports = new GraphQL.GraphQLSchema({
	query: queryRootType,
});
<commit_msg>Add RSVP type and added `rsvps` field to Meetup<commit_after>var GraphQL = require('graphql');
var keystoneTypes = require('./keystoneTypes');

var keystone = require('keystone');
var Meetup = keystone.list('Meetup');
var Talk = keystone.list('Talk');
var User = keystone.list('User');
var RSVP = keystone.list('RSVP');

function getMeetup (id) {
	if (id === 'next') {
		return Meetup.model.findOne().sort('-startDate')
						.where('state', 'active').exec();
	} else if (id === 'last') {
		return Meetup.model.findOne().sort('-startDate')
						.where('state', 'past').exec();
	} else {
		return Meetup.model.findById(id).exec();
	}
}

var meetupStateEnum = new GraphQL.GraphQLEnumType({
	name: 'MeetupState',
	description: 'The state of the meetup',
	values: {
		draft: {
			description: "No published date, it's a draft meetup",
		},
		scheduled: {
			description: "Publish date is before today, it's a scheduled meetup",
		},
		active: {
			description: "Publish date is after today, it's an active meetup",
		},
		past: {
			description: "Meetup date plus one day is after today, it's a past meetup",
		},
	},
});

var meetupType = new GraphQL.GraphQLObjectType({
	name: 'Meetup',
	fields: () => ({
		id: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The id of the meetup.',
		},
		name: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The name of the meetup.',
		},
		publishedDate: {
			type: GraphQL.GraphQLString,
		},
		state: {
			type: new GraphQL.GraphQLNonNull(meetupStateEnum),
		},
		startDate: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
		endDate: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
		place: {
			type: GraphQL.GraphQLString,
		},
		map: {
			type: GraphQL.GraphQLString,
		},
		description: {
			type: GraphQL.GraphQLString,
		},
		maxRSVPs: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLInt),
		},
		totalRSVPs: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLInt),
		},
		url: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
		remainingRSVPs: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLInt),
		},
		rsvpsAvailable: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLBoolean),
		},
		talks: {
			type: new GraphQL.GraphQLList(talkType),
			resolve: (source) => Talk.model.find().where('meetup', source.id).exec(),
		},
		rsvps: {
			type: new GraphQL.GraphQLList(rsvpType),
			resolve: (source) => RSVP.model.find().where('meetup', source.id).exec(),
		},
	}),
});

var talkType = new GraphQL.GraphQLObjectType({
	name: 'Talk',
	fields: () => ({
		id: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The id of the talk.',
		},
		name: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
			description: 'The title of the talk.',
		},
		isLightningTalk: {
			type: GraphQL.GraphQLBoolean,
			description: 'Whether the talk is a Lightning talk',
		},
		meetup: {
			type: new GraphQL.GraphQLNonNull(meetupType),
			description: 'The Meetup the talk is scheduled for',
			resolve: (source, args, info) =>
				Meetup.model.findById(source.meetup).exec(),
		},
		who: {
			type: new GraphQL.GraphQLList(userType),
			description: 'A list of at least one User running the talk',
			resolve: (source, args, info) =>
				User.model.find().where('_id').in(source.who).exec(),
		},
		description: {
			type: GraphQL.GraphQLString,
		},
		slides: {
			type: GraphQL.GraphQLString,
		},
		link: {
			type: GraphQL.GraphQLString,
		},
	}),
});

var userType = new GraphQL.GraphQLObjectType({
	name: 'User',
	fields: {
		name: {
			type: new GraphQL.GraphQLNonNull(keystoneTypes.name),
		},
		email: {
			type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
		},
	},
});

var rsvpType = new GraphQL.GraphQLObjectType({
	name: 'RSVP',
	fields: {
		meetup: {
			type: new GraphQL.GraphQLNonNull(meetupType),
			resolve: (source) => Meetup.model.findById(source.meetup).exec(),
		},
		who: {
			type: new GraphQL.GraphQLNonNull(userType),
			resolve: (source) => User.model.findById(source.who).exec(),
		},
		attending: { type: GraphQL.GraphQLBoolean },
	},
});

var queryRootType = new GraphQL.GraphQLObjectType({
	name: 'Query',
	fields: {
		meetup: {
			type: meetupType,
			args: {
				id: {
					description: 'id of the meetup, can be "next" or "last"',
					type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
				},
			},
			resolve: (_, args) => getMeetup(args.id),
		},
		talk: {
			type: talkType,
			args: {
				id: {
					description: 'id of the talk',
					type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString),
				},
			},
			resolve: (_, args) => Talk.model.findById(args.id).exec(),
		},
	},
});

module.exports = new GraphQL.GraphQLSchema({
	query: queryRootType,
});
<|endoftext|>


<commit_before>define(["Tone/core/Tone", "Tone/component/Volume"], function(Tone){

	"use strict";

	/**
	 *  @class  A single master output which is connected to the
	 *          AudioDestinationNode (aka your speakers).
	 *          It provides useful conveniences such as the ability
	 *          to set the volume and mute the entire application.
	 *          It also gives you the ability to apply master effects to your application.
	 *

	 *          Like Tone.Transport, A single Tone.Master is created
	 *          on initialization and you do not need to explicitly construct one.
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @singleton
	 *  @example
	 * //the audio will go from the oscillator to the speakers
	 * oscillator.connect(Tone.Master);
	 * //a convenience for connecting to the master output is also provided:
	 * oscillator.toMaster();
	 * //the above two examples are equivalent.
	 */
	Tone.Master = function(){
		Tone.call(this);

		/**
		 * the unmuted volume
		 * @type {number}
		 * @private
		 */
		this._unmutedVolume = 1;

		/**
		 *  if the master is muted
		 *  @type {boolean}
		 *  @private
		 */
		this._muted = false;

		/**
		 * The volume of the master output.
		 * @type {Decibels}
		 * @signal
		 */
		this.volume = this.output = new Tone.Volume();

		this._readOnly("volume");
		//connections
		this.input.chain(this.output, this.context.destination);
	};

	Tone.extend(Tone.Master);

	/**
	 *  @type {Object}
	 *  @const
	 */
	Tone.Master.defaults = {
		"volume" : 0,
		"mute" : false
	};

	/**
	 * Mute the output.
	 * @memberOf Tone.Master#
	 * @type {boolean}
	 * @name mute
	 * @example
	 * //mute the output
	 * Tone.Master.mute = true;
	 */
	Object.defineProperty(Tone.Master.prototype, "mute", {
		get : function(){
			return this._muted;
		},
		set : function(mute){
			if (!this._muted && mute){
				this._unmutedVolume = this.volume.value;
				//maybe it should ramp here?
				this.volume.value = -Infinity;
			} else if (this._muted && !mute){
				this.volume.value = this._unmutedVolume;
			}
			this._muted = mute;
		}
	});

	/**
	 *  Add a master effects chain. NOTE: this will disconnect any nodes which were previously
	 *  chained in the master effects chain.
	 *  @param {AudioNode|Tone...} args All arguments will be connected in a row
	 *                                  and the Master will be routed through it.
	 *  @return  {Tone.Master}  this
	 *  @example
	 * //some overall compression to keep the levels in check
	 * var masterCompressor = new Tone.Compressor({
	 * 	"threshold" : -6,
	 * 	"ratio" : 3,
	 * 	"attack" : 0.5,
	 * 	"release" : 0.1
	 * });
	 * //give a little boost to the lows
	 * var lowBump = new Tone.Filter(200, "lowshelf");
	 * //route everything through the filter
	 * //and compressor before going to the speakers
	 * Tone.Master.chain(lowBump, masterCompressor);
	 */
	Tone.Master.prototype.chain = function(){
		this.input.disconnect();
		this.input.chain.apply(this.input, arguments);
		arguments[arguments.length - 1].connect(this.output);
	};

	/**
	 *  Clean up
	 *  @return  {Tone.Master}  this
	 */
	Tone.Master.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this._writable("volume");
		this.volume.dispose();
		this.volume = null;
	};

	///////////////////////////////////////////////////////////////////////////
	//	AUGMENT TONE's PROTOTYPE
	///////////////////////////////////////////////////////////////////////////

	/**
	 *  Connect 'this' to the master output. Shorthand for this.connect(Tone.Master)
	 *  @returns {Tone} this
	 *  @example
	 * //connect an oscillator to the master output
	 * var osc = new Tone.Oscillator().toMaster();
	 */
	Tone.prototype.toMaster = function(){
		this.connect(Tone.Master);
		return this;
	};

	/**
	 *  Also augment AudioNode's prototype to include toMaster
	 *  as a convenience
	 *  @returns {AudioNode} this
	 */
	AudioNode.prototype.toMaster = function(){
		this.connect(Tone.Master);
		return this;
	};

	var MasterConstructor = Tone.Master;

	/**
	 *  initialize the module and listen for new audio contexts
	 */
	Tone._initAudioContext(function(){
		//a single master output
		if (!Tone.prototype.isUndef(Tone.Master)){
			Tone.Master = new MasterConstructor();
		} else {
			MasterConstructor.prototype.dispose.call(Tone.Master);
			MasterConstructor.call(Tone.Master);
		}
	});

	return Tone.Master;
});<commit_msg>reverting to old volume implementation in Master<commit_after>define(["Tone/core/Tone", "Tone/component/Volume"], function(Tone){

	"use strict";

	/**
	 *  @class  A single master output which is connected to the
	 *          AudioDestinationNode (aka your speakers).
	 *          It provides useful conveniences such as the ability
	 *          to set the volume and mute the entire application.
	 *          It also gives you the ability to apply master effects to your application.
	 *

	 *          Like Tone.Transport, A single Tone.Master is created
	 *          on initialization and you do not need to explicitly construct one.
	 *
	 *  @constructor
	 *  @extends {Tone}
	 *  @singleton
	 *  @example
	 * //the audio will go from the oscillator to the speakers
	 * oscillator.connect(Tone.Master);
	 * //a convenience for connecting to the master output is also provided:
	 * oscillator.toMaster();
	 * //the above two examples are equivalent.
	 */
	Tone.Master = function(){
		Tone.call(this);

		/**
		 * the unmuted volume
		 * @type {number}
		 * @private
		 */
		this._unmutedVolume = 1;

		/**
		 *  if the master is muted
		 *  @type {boolean}
		 *  @private
		 */
		this._muted = false;

		/**
		 *  The private volume node
		 *  @type  {Tone.Volume}
		 *  @private
		 */
		this._volume = this.output = new Tone.Volume();

		/**
		 * The volume of the master output.
		 * @type {Decibels}
		 * @signal
		 */
		this.volume = this._volume.volume;

		this._readOnly("volume");
		//connections
		this.input.chain(this.output, this.context.destination);
	};

	Tone.extend(Tone.Master);

	/**
	 *  @type {Object}
	 *  @const
	 */
	Tone.Master.defaults = {
		"volume" : 0,
		"mute" : false
	};

	/**
	 * Mute the output.
	 * @memberOf Tone.Master#
	 * @type {boolean}
	 * @name mute
	 * @example
	 * //mute the output
	 * Tone.Master.mute = true;
	 */
	Object.defineProperty(Tone.Master.prototype, "mute", {
		get : function(){
			return this._muted;
		},
		set : function(mute){
			if (!this._muted && mute){
				this._unmutedVolume = this.volume.value;
				//maybe it should ramp here?
				this.volume.value = -Infinity;
			} else if (this._muted && !mute){
				this.volume.value = this._unmutedVolume;
			}
			this._muted = mute;
		}
	});

	/**
	 *  Add a master effects chain. NOTE: this will disconnect any nodes which were previously
	 *  chained in the master effects chain.
	 *  @param {AudioNode|Tone...} args All arguments will be connected in a row
	 *                                  and the Master will be routed through it.
	 *  @return  {Tone.Master}  this
	 *  @example
	 * //some overall compression to keep the levels in check
	 * var masterCompressor = new Tone.Compressor({
	 * 	"threshold" : -6,
	 * 	"ratio" : 3,
	 * 	"attack" : 0.5,
	 * 	"release" : 0.1
	 * });
	 * //give a little boost to the lows
	 * var lowBump = new Tone.Filter(200, "lowshelf");
	 * //route everything through the filter
	 * //and compressor before going to the speakers
	 * Tone.Master.chain(lowBump, masterCompressor);
	 */
	Tone.Master.prototype.chain = function(){
		this.input.disconnect();
		this.input.chain.apply(this.input, arguments);
		arguments[arguments.length - 1].connect(this.output);
	};

	/**
	 *  Clean up
	 *  @return  {Tone.Master}  this
	 */
	Tone.Master.prototype.dispose = function(){
		Tone.prototype.dispose.call(this);
		this._writable("volume");
		this._volume.dispose();
		this._volume = null;
		this.volume = null;
	};

	///////////////////////////////////////////////////////////////////////////
	//	AUGMENT TONE's PROTOTYPE
	///////////////////////////////////////////////////////////////////////////

	/**
	 *  Connect 'this' to the master output. Shorthand for this.connect(Tone.Master)
	 *  @returns {Tone} this
	 *  @example
	 * //connect an oscillator to the master output
	 * var osc = new Tone.Oscillator().toMaster();
	 */
	Tone.prototype.toMaster = function(){
		this.connect(Tone.Master);
		return this;
	};

	/**
	 *  Also augment AudioNode's prototype to include toMaster
	 *  as a convenience
	 *  @returns {AudioNode} this
	 */
	AudioNode.prototype.toMaster = function(){
		this.connect(Tone.Master);
		return this;
	};

	var MasterConstructor = Tone.Master;

	/**
	 *  initialize the module and listen for new audio contexts
	 */
	Tone._initAudioContext(function(){
		//a single master output
		if (!Tone.prototype.isUndef(Tone.Master)){
			Tone.Master = new MasterConstructor();
		} else {
			MasterConstructor.prototype.dispose.call(Tone.Master);
			MasterConstructor.call(Tone.Master);
		}
	});

	return Tone.Master;
});<|endoftext|>


<commit_before>'use strict';

(function (angular, buildfire) {
    angular.module('socialPluginWidget', ['ngRoute', 'infinite-scroll', 'ngAnimate','socialModals', 'ngFileUpload', 'socialPluginFilters'])
        .config(['$routeProvider', '$compileProvider', '$httpProvider', function ($routeProvider, $compileProvider, $httpProvider) {

            /**
             * To make href urls safe on mobile
             */
            $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|cdvfile|file):/);

            /**
             *To disable pull down to refresh functionality
             * */

//            buildfire.datastore.disableRefresh();

            $routeProvider
                .when('/', {
                    template: '

'
                })
                .when('/thread/:threadId', {
                    templateUrl: 'templates/thread.html',
                    controllerAs: 'Thread',
                    controller: 'ThreadCtrl'
                })
                .otherwise('/');

            var interceptor = ['$q', function ($q) {
                var counter = 0;
                return {

                    request: function (config) {
                        buildfire.spinner.show();
                        counter++;
                        return config;
                    },
                    response: function (response) {
                        counter--;
                        if (counter === 0) {
                            buildfire.spinner.hide();
                        }
                        return response;
                    },
                    responseError: function (rejection) {
                        counter--;
                        if (counter === 0) {
                            buildfire.spinner.hide();
                        }

                        return $q.reject(rejection);
                    }
                };
            }];

            $httpProvider.interceptors.push(interceptor);
        }])
        .run(['$location', '$rootScope','Location','Buildfire', function ( $location, $rootScope,Location,Buildfire) {
            Buildfire.navigation.onBackButtonClick = function () {
                var path = $location.path();
                if (path.indexOf('/thread') == 0) {
                    $rootScope.showThread = true;
                    $rootScope.$digest();
                }
                else
                    Buildfire.navigation.navigateHome();
            }
        }]);
})(window.angular, window.buildfire);<commit_msg>Removed spinner from liking/unliking posts<commit_after>'use strict';

(function (angular, buildfire) {
    angular.module('socialPluginWidget', ['ngRoute', 'infinite-scroll', 'ngAnimate','socialModals', 'ngFileUpload', 'socialPluginFilters'])
        .config(['$routeProvider', '$compileProvider', '$httpProvider', function ($routeProvider, $compileProvider, $httpProvider) {

            /**
             * To make href urls safe on mobile
             */
            $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|cdvfile|file):/);

            /**
             *To disable pull down to refresh functionality
             * */

//            buildfire.datastore.disableRefresh();

            $routeProvider
                .when('/', {
                    template: '

'
                })
                .when('/thread/:threadId', {
                    templateUrl: 'templates/thread.html',
                    controllerAs: 'Thread',
                    controller: 'ThreadCtrl'
                })
                .otherwise('/');

            var interceptor = ['$q', function ($q) {
                var counter = 0;
                return {

                    request: function (config) {
                        if(config.url.indexOf('threadLikes') != -1) {
                            buildfire.spinner.show();
                            counter++;
                        }
                        return config;
                    },
                    response: function (response) {
                        counter = counter > 0 ? counter-- : 0;
                        if (counter === 0) {
                            buildfire.spinner.hide();
                        }
                        return response;
                    },
                    responseError: function (rejection) {
                        counter--;
                        if (counter === 0) {
                            buildfire.spinner.hide();
                        }

                        return $q.reject(rejection);
                    }
                };
            }];

            $httpProvider.interceptors.push(interceptor);
        }])
        .run(['$location', '$rootScope','Location','Buildfire', function ( $location, $rootScope,Location,Buildfire) {
            Buildfire.navigation.onBackButtonClick = function () {
                var path = $location.path();
                if (path.indexOf('/thread') == 0) {
                    $rootScope.showThread = true;
                    $rootScope.$digest();
                }
                else
                    Buildfire.navigation.navigateHome();
            }
        }]);
})(window.angular, window.buildfire);<|endoftext|>


<commit_before>/**
*	//this object is dependent on the DynamicListHelper, included in the sidebar tile
*	// src='dwr/interface/DynamicListHelper.js'
*	// also dependent on browserSniff.js, SidebarHelper.js, and prototype.js 1.5.x
*/
	var ManageListHelper = {
		'getGenericLists' : function(listType)	{
			DynamicListHelper.getGenericLists(listType, ManageListHelper.getGenericLists_cb);
		},
		'getAllLists' : function()	{
			//assumes browserSniff.js has already been included and declared the browser specific vars
			DynamicListHelper.getAllLists(ManageListHelper.getGenericLists_cb);
		},
		'getGenericLists_cb' : function(txt)	{
			//accepts a JSON object	<- now accepts a json array
			// String listType : patient | gene | defaultPatient
			// Array<Object> listItems : { listName, listDate, itemCount, invalidItems }
			try	{
				var listContainerArray = eval('(' + txt + ')');
				for(var i=0; i<listContainerArray.length; i++)	{
					var listContainer = listContainerArray[i];
					var listType = listContainer.listType ? listContainer.listType : "none";
					if(listType == "none") {
						continue;
					}

					var lists = listContainer.listItems;

					//Note:  $('..'+"ListDiv") needs be be defined in the HTML src
					// ^ now, should be auto generated

					if(lists.length == 0)	{
						if($(listType+'ListDiv')){
							$(listType+'ListDiv').innerHTML = "No "+ listType + " lists currently saved

";
					    }
					    if($(listType+'UniteDiv')){
					    	$(listType+'UniteDiv').style.display = "none";
					    }
						continue;
					}

					if($(listType+'ListDiv'))
						$(listType+'ListDiv').innerHTML = "";  //clear it, and repopulate

					var tst = "";
					for(var t=0; t<lists.length; t++)	{

						var status = "";
						var shortName = lists[t].listName.length>25 ? lists[t].listName.substring(0,23) + "..." : lists[t].listName;
						var theName = lists[t].listName;
						var itemCount = lists[t].itemCount;

						var listSubTypes = (lists[t].listSubTypes && lists[t].listSubTypes.length > 0) ? lists[t].listSubTypes.join(",") : "";
						var lstyle = listSubTypes.indexOf(listContainer.highlightType)!= -1 ? "color:#000000;" : "";
						// += or =
						tst +=  "
"
		                    + "<input type='checkbox' style='border:0px;' id='' name='" + listType + "' value='" +theName+ "'/>"
		                    + ""
		                    + shortName + "

 Type: " + listSubTypes + " - "
		                    + "" + itemCount + " item(s)"
		                    + "
"
		                    + "details" + status + "
"
		                    + "
"
		                    + "delete
"
		                    + "

\n
\n";
					}
					if($(listType+'ListDiv'))
						$(listType+'ListDiv').innerHTML = tst;
				}
			}
			catch(err)	{
				//alert("ERR: " + err);
			}

		},
		'groupSelectedLists' : function(groupType, listGroup, groupName, action)	{
			//params:  String listType, DOM where the selected boxes are, new list name, action to perform
			var sLists = Array();
			try	{
				if(groupName == "")	{
					alert("Please Enter a name for the new group");
					throw("no name error");
				}
				var ls = $(listGroup) ? $(listGroup).getElementsByTagName('input') : Array();
				for(var i=0; i<ls.length; i++)	{
					if(ls[i].type=='checkbox' && (ls[i].selected || ls[i].checked))
						sLists.push(ls[i].value);
				}

				if(sLists.length < 1)	{
					alert("Please select some lists to " + action);
					throw("no lists selected");
				}
				if(sLists.length!=2 && action == "difference")	{
					alert("Please select only 2 groups");
					throw("incorrect amount of lists");
				}

				DynamicListHelper.uniteLists(sLists, groupName, groupType, action, ManageListHelper.groupSelectedLists_cb );
			}
			catch(err) {}
		},
		'groupSelectedLists_cb' : function(txt)	{
			//var res = txt.split(",");
			//accepts a comma sep string, should/could be json!
			//String results : pass | fail
			//String action
			//String groupType
			try	{
				var res = eval('(' + txt + ')');

				if(res.results.indexOf("pass")!=-1)	{
					$(res.groupType+'GroupName').value = "";

					var st = res.groupType+"GroupStatus";
					$(st).innerHTML = "Group Saved";
					setTimeout(function()	{ $(st).innerHTML = ""; }, 2000);
				}
				else {
					if(res.action == "join"){
						alert("List did not save, please try again.");
					}
					else{
						alert("The selected lists do not have any common entries.");
					}
				}

				ManageListHelper.getAllLists();
			   	SidebarHelper.loadSidebar();
			}
			catch(err)	{
				//alert(err);
			}
		},
		// ************* upload related **************** //
		//this function is called by upload.jsp sitting in an iframe
		//it is given an associative array (much like a paramMap) from
		// the validated list. The groupString variable(below) is inserted into the page
		// at the appropriate place.-KR...dont need the main argument anymore

		'handleResponse' : function(msg) {
			//optional arguments: 1-upload form id, 2-upload status id
			try	{
				ManageListHelper.getAllLists();
			   	SidebarHelper.loadSidebar();
			}
			catch(err)	{}

		   var uform = arguments.length > 1 && arguments[1]!= "" ? arguments[0] : "uploadForm";
		   var ustat = arguments.length > 2 ? arguments[2] : "uploadStatus";
			//reset the form
			Form.reset($(uform));
			//hide the indicator
			setTimeout(function(){$(ustat).style.display = "none";}, 500);

		},

		// this functions as a toggle for the details link next to a
		// list. If details are currently viewable, it removes the element
		// from the DOM. If the "list name + detailsDiv" element doesn't
		// exist, the AJAX call is made to retrieve the most current details
		// from the userListBean sitting in the session. -KR
		// requires name+"detailsDiv" and name+"status" elements(ids) to be in the HTML
		'getDetails' : function(name){

			if(document.getElementById(name+ "detailsDiv")==null){
				if($(name+"status"))
					$(name+"status").style.display = "";
		    	UserListHelper.getDetailsFromList(name,ManageListHelper.putDetailsIHTML);
		   	}
		   	else	{
		    	Element.remove(name+"detailsDiv");
		    }
		},
		 //function to make an AJAX call to the userListBean in the session
		 // and delete the list under the name it passes as a param. Then finds
		 // the div with an id matching this name and removes it from the DOM. -KR
		'deleteList' : function(name){
			if(confirm("Delete this List?"))
				UserListHelper.removeListFromAjax(name, ManageListHelper.generic_cb);
		},
		'generic_cb' : function(name)	{
			try	{
				SidebarHelper.loadSidebar();
			}
			catch(err)	{}
			ManageListHelper.getAllLists()
		},
		//function to make an AJAX call to the userListBean in the session
		// and delete the list item under the list name it passes as params. Then finds
		// the div with an id matching this item name and removes it from the DOM. -KR
		'deleteItem' : function (name, itemId){
			UserListHelper.removeItemFromList(name,itemId);
			Element.remove(name + itemId + "_div");
			try	{
				SidebarHelper.loadSidebar();
				//update the count
				if($(name+"_count"))	{
					var tmp = $(name+"_count").innerHTML;
					$(name+"_count").innerHTML = parseInt(tmp) - 1;
				}
			}
			catch(err){}
		},
		//this is the callback function from the getDetails AJAX function (above). This
		//function recieves a Document object back from the getDetails call and parses
		// the Document object. As it parses the Document (which is a UserList), it
		// dynamically creates DOM elements for each item in a particular list and adds
		// them to the page. The dynamically created nodes are created/deleted depending
		// on when the user toggles the "details" link next to a link. -KR

		// modded to use IHTML and JSON- rcl
		'putDetailsIHTML' : function(userList){
			//JSON: String listName
			// String listType
			// Array validItems
			// Array invalidItems
		 	try	{
				var list = eval('(' + userList + ')');
				listName = list.listName;
				listType = list.listType;
				listType = ""; //clear this for now

				var items = list.validItems ? list.validItems : Array();
				var invalidItems = list.invalidItems ? list.invalidItems : Array();

				var itemId;
		 		var value;
		 		var dDIV = document.createElement("div");
		 		dDIV.setAttribute("id",listName + "detailsDiv");
		 		dDIV.setAttribute("class", "listItemsDiv");

		 		//rcl - create and append our container
		 		 document.getElementById(listName + "details").appendChild(dDIV);
		 		//setup a handle to the working container
		 		var wDiv = $(listName + "detailsDiv");
		 		wDiv.style.borderLeft = "1px dashed red";
		 		wDiv.style.marginLeft = "20px";
				wDiv.style.width="300px";
		 		if(items.length > 0)	{
		 			var tmp = "";
					for(var i=0; i<items.length; i++)	{
						itemId = items[i];
						tmp += "
"+(i+1) +") " +listType + " " + itemId;
						var oc = new Function("deleteItem('"+listName+ "','" + itemId + "');return false;");
						tmp += "[delete]
";
					}
					wDiv.innerHTML += tmp;

					var eid = encodeURIComponent(listName);
					wDiv.innerHTML += "
export list
";
				}
				else{
			    	document.getElementById(listName + "details").appendChild(dDIV);
			    	wDiv.innerHTML = "No valid items found
";
				}

			    if(!invalidItems.length < 1)	{
					var intmp = "

Invalid or does not exist in the database:
 ";
					//wDiv.innerHTML += "
Invalid or does not exist in the database:
 ";
					for(var i=0; i<invalidItems.length; i++){
						invalidItemId = invalidItems[i];
						if((i+1) == invalidItems.length){
							//wDiv.innerHTML += invalidItemId;
							intmp += invalidItemId;
						}
						else{
							intmp += invalidItemId + ", ";
							//wDiv.innerHTML += invalidItemId + ", ";
						}
					}
					wDiv.innerHTML +=intmp;
					wDiv.innerHTML += "
";

				}
			}
			catch(err)	{
				if($('details'))	{
					$("details").innerHTML = err;
					$("details").style.display = "";
				}
			}

			if($(listName+"status"))	{
				setTimeout(function()	{$(listName+"status").style.display = "none";}, 500);
			}
		},
		//all form validation is done client side. The name and file fields are checked for null
		// and list name collisions are checked. If there is a collision the user can either
		//overwrite the stored list with the current list or cancel the action and rename it. -KR

		//never gets called if you click <enter> on the keyboard, hence the onSubmit = return false; - RL
		//moved here from inline - RL
     	'validateForm' : function()	{
			//optional args: listName, upload, uploadStatus
			//loosely checking for presence
			var listName = arguments.length>0 ? arguments[0] : 'listName';
			var upload = arguments.length>1 ? arguments[1] : 'upload';
			var uploadStatus = arguments.length> 2 ? arguments[2] : 'uploadStatus';
			var uploadForm = arguments.length>3 ? arguments[3] : 'uploadForm';

			$(uploadStatus).style.display = "";
	     	var thisListName = $(listName).value;

			var errors = "";
	     	if(thisListName == ""){
	     		errors += "please enter a name for this list. \n";
	     	}
	     	if($(upload).value==""){
	     		errors += "please enter a file for this list.";
			}
	     	if(errors != ""){
	     	    alert(errors);
	     	    $(uploadStatus).style.display = "none";
	     	    return false;
	     	}
	     	else {
		    	$(uploadForm).submit();
			}
	     }
	};
	<commit_msg>list management<commit_after>/**
*	//this object is dependent on the DynamicListHelper, included in the sidebar tile
*	// src='dwr/interface/DynamicListHelper.js'
*	// also dependent on browserSniff.js, SidebarHelper.js, and prototype.js 1.5.x
*/
	var ManageListHelper = {
		'getGenericLists' : function(listType)	{
			DynamicListHelper.getGenericLists(listType, ManageListHelper.getGenericLists_cb);
		},
		'getAllLists' : function()	{
			//assumes browserSniff.js has already been included and declared the browser specific vars
			DynamicListHelper.getAllLists(ManageListHelper.getGenericLists_cb);
		},
		'getGenericLists_cb' : function(txt)	{
			//accepts a JSON object	<- now accepts a json array
			// String listType : patient | gene | defaultPatient
			// Array<Object> listItems : { listName, listDate, itemCount, invalidItems }
			try	{
				var listContainerArray = eval('(' + txt + ')');
				for(var i=0; i<listContainerArray.length; i++)	{
					var listContainer = listContainerArray[i];
					var listType = listContainer.listType ? listContainer.listType : "none";
					if(listType == "none") {
						continue;
					}

					var lists = listContainer.listItems;

					//Note:  $('..'+"ListDiv") needs be be defined in the HTML src
					// ^ now, should be auto generated

					if(lists.length == 0)	{
						if($(listType+'ListDiv')){
							$(listType+'ListDiv').innerHTML = "No "+ listType + " lists currently saved

";
					    }
					    if($(listType+'UniteDiv')){
					    	$(listType+'UniteDiv').style.display = "none";
					    }
						continue;
					}

					if($(listType+'ListDiv'))
						$(listType+'ListDiv').innerHTML = "";  //clear it, and repopulate

					var tst = "";
					for(var t=0; t<lists.length; t++)	{

						var status = "";
						var shortName = lists[t].listName.length>25 ? lists[t].listName.substring(0,23) + "..." : lists[t].listName;
						var theName = lists[t].listName;
						var itemCount = lists[t].itemCount;
						var author = lists[t].author;
						var date = lists[t].listDate;
						var notes = lists[t].notes;
						var lib = "Author: " + lists[t].author + "
" +
								  "Date Created: " + lists[t].listDate 	+ "
" +
								  "Notes:
"  + lists[t].notes;

						var listSubTypes = (lists[t].listSubTypes && lists[t].listSubTypes.length > 0) ? lists[t].listSubTypes.join(",") : "";
						var lstyle = listSubTypes.indexOf(listContainer.highlightType)!= -1 ? "color:#000000;" : "";
						// += or =
						tst +=  "
"
		                    + "<input type='checkbox' style='border:0px;' id='' name='" + listType + "' value='" +theName+ "'/>"
		                    + ""
		                    + shortName + "

 Type: " + listSubTypes + " - "
		                    + "" + itemCount + " item(s)"
		                    + "
"
		                    + "details" + status + "
"
		                    + "
"
		                    + "delete
"
		                    + "

\n
\n";
					}
					if($(listType+'ListDiv'))
						$(listType+'ListDiv').innerHTML = tst;
				}
			}
			catch(err)	{
				//alert("ERR: " + err);
			}

		},
		'groupSelectedLists' : function(groupType, listGroup, groupName, action)	{
			//params:  String listType, DOM where the selected boxes are, new list name, action to perform
			var sLists = Array();
			try	{
				if(groupName == "")	{
					alert("Please Enter a name for the new group");
					throw("no name error");
				}
				var ls = $(listGroup) ? $(listGroup).getElementsByTagName('input') : Array();
				for(var i=0; i<ls.length; i++)	{
					if(ls[i].type=='checkbox' && (ls[i].selected || ls[i].checked))
						sLists.push(ls[i].value);
				}

				if(sLists.length < 1)	{
					alert("Please select some lists to " + action);
					throw("no lists selected");
				}
				if(sLists.length!=2 && action == "difference")	{
					alert("Please select only 2 groups");
					throw("incorrect amount of lists");
				}

				DynamicListHelper.uniteLists(sLists, groupName, groupType, action, ManageListHelper.groupSelectedLists_cb );
			}
			catch(err) {}
		},
		'groupSelectedLists_cb' : function(txt)	{
			//var res = txt.split(",");
			//accepts a comma sep string, should/could be json!
			//String results : pass | fail
			//String action
			//String groupType
			try	{
				var res = eval('(' + txt + ')');

				if(res.results.indexOf("pass")!=-1)	{
					$(res.groupType+'GroupName').value = "";

					var st = res.groupType+"GroupStatus";
					$(st).innerHTML = "Group Saved";
					setTimeout(function()	{ $(st).innerHTML = ""; }, 2000);
				}
				else {
					if(res.action == "join"){
						alert("List did not save, please try again.");
					}
					else{
						alert("The selected lists do not have any common entries.");
					}
				}

				ManageListHelper.getAllLists();
			   	SidebarHelper.loadSidebar();
			}
			catch(err)	{
				//alert(err);
			}
		},
		// ************* upload related **************** //
		//this function is called by upload.jsp sitting in an iframe
		//it is given an associative array (much like a paramMap) from
		// the validated list. The groupString variable(below) is inserted into the page
		// at the appropriate place.-KR...dont need the main argument anymore

		'handleResponse' : function(msg) {
			//optional arguments: 1-upload form id, 2-upload status id
			try	{
				ManageListHelper.getAllLists();
			   	SidebarHelper.loadSidebar();
			}
			catch(err)	{}

		   var uform = arguments.length > 1 && arguments[1]!= "" ? arguments[0] : "uploadForm";
		   var ustat = arguments.length > 2 ? arguments[2] : "uploadStatus";
			//reset the form
			Form.reset($(uform));
			//hide the indicator
			setTimeout(function(){$(ustat).style.display = "none";}, 500);

		},

		// this functions as a toggle for the details link next to a
		// list. If details are currently viewable, it removes the element
		// from the DOM. If the "list name + detailsDiv" element doesn't
		// exist, the AJAX call is made to retrieve the most current details
		// from the userListBean sitting in the session. -KR
		// requires name+"detailsDiv" and name+"status" elements(ids) to be in the HTML
		'getDetails' : function(name){

			if(document.getElementById(name+ "detailsDiv")==null){
				if($(name+"status"))
					$(name+"status").style.display = "";
		    	UserListHelper.getDetailsFromList(name,ManageListHelper.putDetailsIHTML);
		   	}
		   	else	{
		    	Element.remove(name+"detailsDiv");
		    }
		},
		 //function to make an AJAX call to the userListBean in the session
		 // and delete the list under the name it passes as a param. Then finds
		 // the div with an id matching this name and removes it from the DOM. -KR
		'deleteList' : function(name){
			if(confirm("Delete this List?"))
				UserListHelper.removeListFromAjax(name, ManageListHelper.generic_cb);
		},
		'generic_cb' : function(name)	{
			try	{
				SidebarHelper.loadSidebar();
			}
			catch(err)	{}
			ManageListHelper.getAllLists()
		},
		//function to make an AJAX call to the userListBean in the session
		// and delete the list item under the list name it passes as params. Then finds
		// the div with an id matching this item name and removes it from the DOM. -KR
		'deleteItem' : function (name, itemId){
			UserListHelper.removeItemFromList(name,itemId);
			Element.remove(name + itemId + "_div");
			try	{
				SidebarHelper.loadSidebar();
				//update the count
				if($(name+"_count"))	{
					var tmp = $(name+"_count").innerHTML;
					$(name+"_count").innerHTML = parseInt(tmp) - 1;
				}
			}
			catch(err){}
		},
		//this is the callback function from the getDetails AJAX function (above). This
		//function recieves a Document object back from the getDetails call and parses
		// the Document object. As it parses the Document (which is a UserList), it
		// dynamically creates DOM elements for each item in a particular list and adds
		// them to the page. The dynamically created nodes are created/deleted depending
		// on when the user toggles the "details" link next to a link. -KR

		// modded to use IHTML and JSON- rcl
		'putDetailsIHTML' : function(userList){
			//JSON: String listName
			// String listType
			// Array validItems
			// Array invalidItems
		 	try	{
				var list = eval('(' + userList + ')');
				listName = list.listName;
				listType = list.listType;
				listType = ""; //clear this for now

				var items = list.validItems ? list.validItems : Array();
				var invalidItems = list.invalidItems ? list.invalidItems : Array();

				var itemId;
				var itemRank = "";
				var itemNotes = "";
		 		var value;
		 		var dDIV = document.createElement("div");
		 		dDIV.setAttribute("id",listName + "detailsDiv");
		 		dDIV.setAttribute("class", "listItemsDiv");

		 		//rcl - create and append our container
		 		 document.getElementById(listName + "details").appendChild(dDIV);
		 		//setup a handle to the working container
		 		var wDiv = $(listName + "detailsDiv");
		 		wDiv.style.borderLeft = "1px dashed red";
		 		wDiv.style.marginLeft = "20px";
				wDiv.style.width="300px";
		 		if(items.length > 0)	{
		 			var tmp = "";
					for(var i=0; i<items.length; i++)	{
						itemId = items[i].name;
						itemRank = "";
						itemNotes = "";
							if(items[i].rank!=null){
								itemRank = " rank: " + items[i].rank;
							}
							if(items[i].notes!=null){
								itemNotes = " notes: " + items[i].notes;
							}
						tmp += "
"+(i+1) +") " +listType + " " + itemId + itemRank + itemNotes;
						var oc = new Function("deleteItem('"+listName+ "','" + itemId + "');return false;");
						tmp += "[delete]
";
					}
					wDiv.innerHTML += tmp;

					var eid = encodeURIComponent(listName);
					wDiv.innerHTML += "
export list
";
				}
				else{
			    	document.getElementById(listName + "details").appendChild(dDIV);
			    	wDiv.innerHTML = "No valid items found
";
				}

			    if(!invalidItems.length < 1)	{
					var intmp = "

Invalid or does not exist in the database:
 ";
					//wDiv.innerHTML += "
Invalid or does not exist in the database:
 ";
					for(var i=0; i<invalidItems.length; i++){
						invalidItemId = invalidItems[i];
						if((i+1) == invalidItems.length){
							//wDiv.innerHTML += invalidItemId;
							intmp += invalidItemId;
						}
						else{
							intmp += invalidItemId + ", ";
							//wDiv.innerHTML += invalidItemId + ", ";
						}
					}
					wDiv.innerHTML +=intmp;
					wDiv.innerHTML += "
";

				}
			}
			catch(err)	{
				if($('details'))	{
					$("details").innerHTML = err;
					$("details").style.display = "";
				}
			}

			if($(listName+"status"))	{
				setTimeout(function()	{$(listName+"status").style.display = "none";}, 500);
			}
		},
		//all form validation is done client side. The name and file fields are checked for null
		// and list name collisions are checked. If there is a collision the user can either
		//overwrite the stored list with the current list or cancel the action and rename it. -KR

		//never gets called if you click <enter> on the keyboard, hence the onSubmit = return false; - RL
		//moved here from inline - RL
     	'validateForm' : function()	{
			//optional args: listName, upload, uploadStatus
			//loosely checking for presence
			var listName = arguments.length>0 ? arguments[0] : 'listName';
			var upload = arguments.length>1 ? arguments[1] : 'upload';
			var uploadStatus = arguments.length> 2 ? arguments[2] : 'uploadStatus';
			var uploadForm = arguments.length>3 ? arguments[3] : 'uploadForm';

			$(uploadStatus).style.display = "";
	     	var thisListName = $(listName).value;

			var errors = "";
	     	if(thisListName == ""){
	     		errors += "please enter a name for this list. \n";
	     	}
	     	if($(upload).value==""){
	     		errors += "please enter a file for this list.";
			}
	     	if(errors != ""){
	     	    alert(errors);
	     	    $(uploadStatus).style.display = "none";
	     	    return false;
	     	}
	     	else {
		    	$(uploadForm).submit();
			}
	     }
	};
	<|endoftext|>


<commit_before>import Ember from 'ember';
import _ from 'lodash';
import layout from './template';

/**
* @module ember-osf
* @submodule components
*/

/**
* Widget to select a license on a project or preprint with the ability
* to only allow a subset of licenses and to autosave or save explictly
* ```handlebars
* {{license-picker
*   licenses=availableLicenses
*   currentValues=basicsLicense
*   showCategories=false
*   editLicense=(action 'editLicense')
*   allowDismiss=false
*   autosave=true
*   showBorder=false
*   pressSubmit=(action 'saveBasics')}}
* ```
* @class license-picker
* @param {DS.Model} licenses Which Licenses are available to be selected
* @param {Object} currentValues The values that are currently on the model, as strings (copyrightHolders joined as a string)
* @param {boolean} showCategories whether the licenses available in the dropdown are separated by categories
* @param {action} editLicense function to be called when the license details are changed (either on submit or autosaving)
* @param {boolean} autosave whether the component should call the save function on every change or offer an explicit save button
* @param {boolean} showBorder frame the widget in a border
* @param {action} pressSubmit what should be called if Enter is pressed
*/
export default Ember.Component.extend({
    layout,
    store: Ember.inject.service(),
    licensesAvailable: Ember.A(),
    showBorder: true,
    showYear: true,
    showText: false,
    toggleText: true,
    showCopyrightHolders: true,
    showCategories: true,
    allowDismiss: false,
    init() {
        this._super(...arguments);
        // Debouncing autosave prevents a request per keystroke, only sending it
        // when the user is done typing (trailing=true), debounce timer can be tweaked.
        this.set('debouncedAutosave', _.debounce(() => {
            if (this.get('autosave')) {
                this.get('actions.save').bind(this)();
            }
        }, 500, {trailing: true}));
    },
    didReceiveAttrs() {
        if (!this.get('currentValues.year') && !this.get('year')) {
            let date = new Date();
            this.set('year', String(date.getUTCFullYear()));
        }
        if (!this.get('licenses') && this.get('licensesAvailable').length === 0) {
            this.get('store').query('license', { 'page[size]': 20 }).then(ret => this.set('licensesAvailable', ret));
        } else if (this.get('licenses')) {
            this.set('licensesAvailable', this.get('licenses'));
        }
    },
    showOtherFields: Ember.observer('nodeLicense', 'nodeLicense.text', function() {
        let text = this.get('nodeLicense.text');
        if (!text) {
            return;
        }
        this.set('showYear', text.indexOf('{{year}}') !== -1);
        this.set('showCopyrightHolders', text.indexOf('{{copyrightHolders}}') !== -1);
    }),
    yearRequired: Ember.computed('nodeLicense', function() {
        return this.get('nodeLicense.requiredFields') && this.get('nodeLicense.requiredFields').indexOf('year') !== -1;
    }),
    copyrightHoldersRequired: Ember.computed('nodeLicense', function() {
        return this.get('nodeLicense.requiredFields') && this.get('nodeLicense.requiredFields').indexOf('copyrightHolders') !== -1;
    }),
    nodeLicense: Ember.computed('licensesAvailable', 'currentValues.licenseType', function() {
        if (this.get('currentValues.licenseType.id')) {
            return this.get('currentValues.licenseType');
        }
        return this.get('licensesAvailable.firstObject');
    }),
    year: Ember.computed('currentValues.year', function() {
        return this.get('currentValues.year');
    }),
    copyrightHolders: Ember.computed('currentValues.copyrightHolders', function() {
        return this.get('currentValues.copyrightHolders');
    }),
    nodeLicenseText: Ember.computed('nodeLicense', 'nodeLicense.text', 'year', 'copyrightHolders', function() {
        let text = this.get('nodeLicense.text');
        if (text) {
            text = text.replace(/({{year}})/g, this.get('year') || '');
            text = text.replace(/({{copyrightHolders}})/g, this.get('copyrightHolders') || '');
        }
        return text;
    }),
    licenseEdited: Ember.observer('copyrightHolders', 'nodeLicense', 'year', function() {
        if (
            (this.get('copyrightHolders') && (this.get('copyrightHolders') !== this.get('currentValues.copyrightHolders'))) ||
            (this.get('year') && (this.get('year') !== this.get('currentValues.year'))) ||
            (this.get('currentValues.licenseType.id') && this.get('nodeLicense.id') !== this.get('currentValues.licenseType.id'))
        ) {
            this.get('debouncedAutosave')();
        }
    }),
    actions: {
        selectLicense(license) {
            this.set('nodeLicense', license);
        },
        toggleFullText() {
            this.set('showText', !this.get('showText'));
        },
        save() {
            let values = {
                licenseType: this.get('nodeLicense'),
                year: this.get('year'),
                copyrightHolders: this.get('copyrightHolders') ? this.get('copyrightHolders') : ''
            };
            this.attrs.editLicense(
                values,
                !((this.get('yearRequired') && !values.year) || (this.get('copyrightHoldersRequired') && values.copyrightHolders.length === 0))
            );
        },
        sendSubmit() {
            this.sendAction('pressSubmit');
        },
        dismiss() {
            this.attrs.dismiss();
        }
    }
});
<commit_msg>Reverse debouncing logic on license widget<commit_after>import Ember from 'ember';
import _ from 'lodash';
import layout from './template';

/**
* @module ember-osf
* @submodule components
*/

/**
* Widget to select a license on a project or preprint with the ability
* to only allow a subset of licenses and to autosave or save explictly
* ```handlebars
* {{license-picker
*   licenses=availableLicenses
*   currentValues=basicsLicense
*   showCategories=false
*   editLicense=(action 'editLicense')
*   allowDismiss=false
*   autosave=true
*   showBorder=false
*   pressSubmit=(action 'saveBasics')}}
* ```
* @class license-picker
* @param {DS.Model} licenses Which Licenses are available to be selected
* @param {Object} currentValues The values that are currently on the model, as strings (copyrightHolders joined as a string)
* @param {boolean} showCategories whether the licenses available in the dropdown are separated by categories
* @param {action} editLicense function to be called when the license details are changed (either on submit or autosaving)
* @param {boolean} autosave whether the component should call the save function on every change or offer an explicit save button
* @param {boolean} showBorder frame the widget in a border
* @param {action} pressSubmit what should be called if Enter is pressed
*/
export default Ember.Component.extend({
    layout,
    store: Ember.inject.service(),
    licensesAvailable: Ember.A(),
    showBorder: true,
    showYear: true,
    showText: false,
    toggleText: true,
    showCopyrightHolders: true,
    showCategories: true,
    allowDismiss: false,
    didReceiveAttrs() {
        if (!this.get('licenses')) {
            this.get('store').query('license', { 'page[size]': 20 }).then(ret => {
                this.set('licensesAvailable', ret);
            });
        } else {
            this.set('licensesAvailable', this.get('licenses'));
        }
        if (!this.get('currentValues.year')) {
            let date = new Date();
            this.set('year', String(date.getUTCFullYear()));
        } else {
            this.set('year', this.get('currentValues.year'));
        }
        if (this.get('currentValues.copyrightHolders')) {
            this.set('copyrightHolders', this.get('currentValues.copyrightHolders'));
        }
    },
    _setNodeLicense: Ember.observer('licensesAvailable', 'currentValues.licenseType', function() {
        if (!this.get('currentValues.licenseType.id')) { //if not resolved properly
            this.set('nodeLicense', this.get('licensesAvailable.firstObject'));
        } else {
            this.set('nodeLicense', this.get('currentValues.licenseType'));
        }
    }),
    nodeLicenseText: Ember.computed('nodeLicense', 'nodeLicense.text', 'year', 'copyrightHolders', function() {
        let text = this.get('nodeLicense.text');
        if (text) {
            text = text.replace(/({{year}})/g, this.get('year') || '');
            text = text.replace(/({{copyrightHolders}})/g, this.get('copyrightHolders') || '');
        }
        return text;
    }),
    licenseEdited: Ember.observer('copyrightHolders', 'nodeLicense', 'year', function() {
        Ember.run.debounce(this, function() {
            if (this.get('autosave')) {
                this.get('actions.save').bind(this)();
            }
        }, 250);
    }),
    year: null,
    copyrightHolders: null,
    showOtherFields: Ember.observer('nodeLicense', 'nodeLicense.text', function() {
        let text = this.get('nodeLicense.text');
        if (!text) {
            return;
        }
        this.set('showYear', text.indexOf('{{year}}') !== -1);
        this.set('showCopyrightHolders', text.indexOf('{{copyrightHolders}}') !== -1);
    }),
    actions: {
        selectLicense(license) {
            this.set('nodeLicense', license);
        },
        toggleFullText() {
            this.set('showText', !this.get('showText'));
        },
        save() {
            let values = {
                licenseType: this.get('nodeLicense'),
                year: this.get('year'),
                copyrightHolders: this.get('copyrightHolders') ? this.get('copyrightHolders') : ''
            };
            this.attrs.editLicense(
                values,
                !((this.get('yearRequired') && !values.year) || (this.get('copyrightHoldersRequired') && values.copyrightHolders.length === 0))
            );
        },
        sendSubmit() {
            this.sendAction('pressSubmit');
        },
        dismiss() {
            this.attrs.dismiss();
        }
    }
});
<|endoftext|>