<commit_before>import Component from '@ember/component';
import { isArray } from '@ember/array';
import { isEmpty, isPresent } from '@ember/utils';
import { on } from '@ember/object/evented';
import { alias, reads } from '@ember/object/computed';
import EmberObject, {
  computed,
  observer,
  defineProperty
} from '@ember/object';
import { inject as service } from '@ember/service';
import humanize from "ember-form-builder/utilities/humanize";
import guessType from "ember-form-builder/utilities/guess-type";
import { dasherize } from '@ember/string';
import { once } from '@ember/runloop';

function mergedClass(name) {
  return computed(`${name}Class`, function() {
    return [
      this.get(`configuration.${name}Class`),
      this.get(`${name}Class`)
    ].join(' ');
  })
}

const extension = {
  translationService: service("formBuilderTranslations"),
  // TODO: assertions
  class: null,
  classNameBindings: ["wrapperClassName", "wrapperTypeClassName", 'attributeClassName',
    'modelClassName', 'inlineLabel'],
  hasFocusedOut: false,
  as: computed("_model", "attr", function() {
    return guessType(this.get("_model"), this.get("attr"), this);
  }),
  attr: null,
  configuration: {},

  type:       reads("as"),
  object:     reads("builder.object"),
  _model:     reads("builder.model"),
  modelName:  reads("builder.modelName"),
  builder: computed({
    set(key, value) {
      if (value && value.builder) {
        return value.builder;
      } else {
        return value;
      }
    }
  }),

  init: function() {
    this._super();

    this.objectOrAttrChanged();
  },

  focusOut: function() {
    once(this, this.handleFocusOut);
  },

  handleFocusOut() {
    this.set('hasFocusedOut', true);
  },

  setupClassNameBindings: on("init", function() {
    let newBindings = [
      "hasErrors:" + this.get("configuration.wrapperWithErrorsClass"),
      "hasUnit:" + this.get("configuration.wrapperWithUnitClass")
    ];
    let combinedBindings = (this.get("classNameBindings") || []).concat(newBindings);
    this.set("classNameBindings", combinedBindings);
  }),

  validations: computed("model", "attr", function() {
    return this.get("object.validations." + this.get("attr"));
  }),

  isRequired: computed("validations.presence", function() {
    var presenceValidator = this.get("validations.presence");
    return presenceValidator && !presenceValidator.soft;
  }),

  canValidate: computed("hasFocusedOut", "builder.isValid", function() {
    return this.get("hasFocusedOut") || !this.get("builder.isValid");
  }),

  hasErrors: computed("canValidate", "errors", function() {
    return this.get("canValidate") && !isEmpty(this.get("errors"));
  }),

  hasUnit: computed("unit", function() {
    return isPresent(this.get("unit"));
  }),

  errorMessages: computed("errors", function() {
    return this.get("errors").join(", ");
  }),

  objectOrAttrChanged: observer("object", "attr", function() {
    var errorsAttribute = this.get('builder').errorsPathFor(this.get('attr'));
    defineProperty(this, "errors", reads(errorsAttribute));

    var stringValueibute = "object." + this.get("attr");
    defineProperty(this, "value", computed(stringValueibute, {
      get: function() {
        return this.get(stringValueibute);
      },

      set: function(key, value) {
        this.set(stringValueibute, value);
        return value;
      }
    }));
  }),

  additionalAttributeNames: computed("attrs", function() {
    let additionalAttributeNames = [];

    for(let key in this.get("attrs")) {
      if (simpleInputAttributeNames.indexOf(key) < 0) {
        additionalAttributeNames.push(key);
      }
    }

    return additionalAttributeNames;
  }),

  additionalAttributes: computed("additionalAttributeNames.[]", function() {
    let proxy = EmberObject.create({ content: this });
    if (isArray(this.get("additionalAttributeNames"))) {
      this.get("additionalAttributeNames").forEach((attributeName) => {
        defineProperty(proxy, attributeName, alias(`content.${attributeName}`));
      });
    }
    return proxy;
  }),

  wrapperClassName: mergedClass('wrapper'),
  wrapperTypeClassName: computed("type", function() {
    return this.get("type") + "-input";
  }),
  unitClassName:    mergedClass('unit'),
  errorsClassName:  mergedClass('errors'),
  fieldClassName:   mergedClass('field'),
  inputClassName:   mergedClass('input'),
  hintClassName:    mergedClass('hint'),
  attributeClassName: computed('attr', function() {
    let attr = this.get('attr');
    return attr ? `${dasherize(attr)}-attr-input` : null;
  }),
  modelClassName: computed('modelName', function() {
    let modelName = this.get('modelName');
    return  modelName ? `${dasherize(modelName)}-model-input` : null;
  }),

  inputComponentName: computed("attr", "builder", function() {
    return "inputs/" + this.get("type") + "-input";
  }),

  inputElementId: computed("elementId", function() {
    return this.get("elementId") + "Input";
  }),

  inputName: computed('builder.name', 'attr', function() {
    var prefix = this.get('builder.name');
    var name = this.get('attr');
    if (isPresent(prefix)) {
      name = prefix + '[' + name + ']';
    }
    return name;
  }),

  label: computed("builder.translationKey", "attr", "labelTranslation", function() {
    var key;

    if (isPresent(this.get("labelTranslation"))) {
      key = this.get("labelTranslation");
    } else {
      key = this.get("builder.translationKey") + ".attributes." + this.get("attr");
    }

    var result;
    if (this.get("translationService").exists(key)) {
      result = this.get("translationService").t(key);
    }
    if (isEmpty(result)) {
      result = humanize(this.get("attr"));
    }
    return result;
  }),

  hasLabel: computed("inlineLabel", "label", function() {
    return !this.get("inlineLabel") && this.get("label") !== false;
  }),

  inlineLabel: computed("type", function() {
    return this.get("type") === "boolean";
  }),

  hasInlineLabel: computed("inlineLabel", "label", function() {
    return this.get("inlineLabel") && this.get("label") !== false;
  }),

  hasHint: computed("hint", function() {
    return isPresent(this.get("hint"));
  }),

  hint: computed("builder.translationKey", "attr", "hintTranslation", function() {
    var key;

    if (isPresent(this.get("hintTranslation"))) {
      key = this.get("hintTranslation");
    } else {
      key = this.get("builder.translationKey") + ".hints." + this.get("attr");
    }

    if (this.get("translationService").exists(key)) {
      return this.get("translationService").t(key);
    }
  }),

  placeholder: computed("builder.translationKey", "attr", "placeholderTranslation", function() {
    var key;

    if (isPresent(this.get("placeholderTranslation"))) {
      key = this.get("placeholderTranslation");
    } else {
      key = this.get("builder.translationKey") + ".placeholders." + this.get("attr");
    }

    if (this.get("translationService").exists(key)) {
      return this.get("translationService").t(key);
    }
  })
};

let simpleInputAttributeNames = Object.keys(extension).concat(["builder", "attr"]);
const SimpleInput = Component.extend(extension);

SimpleInput.reopenClass({
  positionalParams: ["attr"]
});

export default SimpleInput;
<commit_msg>fix typo<commit_after>import Component from '@ember/component';
import { isArray } from '@ember/array';
import { isEmpty, isPresent } from '@ember/utils';
import { on } from '@ember/object/evented';
import { alias, reads } from '@ember/object/computed';
import EmberObject, {
  computed,
  observer,
  defineProperty
} from '@ember/object';
import { inject as service } from '@ember/service';
import humanize from "ember-form-builder/utilities/humanize";
import guessType from "ember-form-builder/utilities/guess-type";
import { dasherize } from '@ember/string';
import { once } from '@ember/runloop';

function mergedClass(name) {
  return computed(`${name}Class`, function() {
    return [
      this.get(`configuration.${name}Class`),
      this.get(`${name}Class`)
    ].join(' ');
  })
}

const extension = {
  translationService: service("formBuilderTranslations"),
  // TODO: assertions
  class: null,
  classNameBindings: ["wrapperClassName", "wrapperTypeClassName", 'attributeClassName',
    'modelClassName', 'inlineLabel'],
  hasFocusedOut: false,
  as: computed("_model", "attr", function() {
    return guessType(this.get("_model"), this.get("attr"), this);
  }),
  attr: null,
  configuration: {},

  type:       reads("as"),
  object:     reads("builder.object"),
  _model:     reads("builder.model"),
  modelName:  reads("builder.modelName"),
  builder: computed({
    set(key, value) {
      if (value && value.builder) {
        return value.builder;
      } else {
        return value;
      }
    }
  }),

  init: function() {
    this._super();

    this.objectOrAttrChanged();
  },

  focusOut: function() {
    once(this, this.handleFocusOut);
  },

  handleFocusOut() {
    this.set('hasFocusedOut', true);
  },

  setupClassNameBindings: on("init", function() {
    let newBindings = [
      "hasErrors:" + this.get("configuration.wrapperWithErrorsClass"),
      "hasUnit:" + this.get("configuration.wrapperWithUnitClass")
    ];
    let combinedBindings = (this.get("classNameBindings") || []).concat(newBindings);
    this.set("classNameBindings", combinedBindings);
  }),

  validations: computed("model", "attr", function() {
    return this.get("object.validations." + this.get("attr"));
  }),

  isRequired: computed("validations.presence", function() {
    var presenceValidator = this.get("validations.presence");
    return presenceValidator && !presenceValidator.soft;
  }),

  canValidate: computed("hasFocusedOut", "builder.isValid", function() {
    return this.get("hasFocusedOut") || !this.get("builder.isValid");
  }),

  hasErrors: computed("canValidate", "errors", function() {
    return this.get("canValidate") && !isEmpty(this.get("errors"));
  }),

  hasUnit: computed("unit", function() {
    return isPresent(this.get("unit"));
  }),

  errorMessages: computed("errors", function() {
    return this.get("errors").join(", ");
  }),

  objectOrAttrChanged: observer("object", "attr", function() {
    var errorsAttribute = this.get('builder').errorsPathFor(this.get('attr'));
    defineProperty(this, "errors", reads(errorsAttribute));

    var valueAttribute = "object." + this.get("attr");
    defineProperty(this, "value", computed(valueAttribute, {
      get: function() {
        return this.get(valueAttribute);
      },

      set: function(key, value) {
        this.set(valueAttribute, value);
        return value;
      }
    }));
  }),

  additionalAttributeNames: computed("attrs", function() {
    let additionalAttributeNames = [];

    for(let key in this.get("attrs")) {
      if (simpleInputAttributeNames.indexOf(key) < 0) {
        additionalAttributeNames.push(key);
      }
    }

    return additionalAttributeNames;
  }),

  additionalAttributes: computed("additionalAttributeNames.[]", function() {
    let proxy = EmberObject.create({ content: this });
    if (isArray(this.get("additionalAttributeNames"))) {
      this.get("additionalAttributeNames").forEach((attributeName) => {
        defineProperty(proxy, attributeName, alias(`content.${attributeName}`));
      });
    }
    return proxy;
  }),

  wrapperClassName: mergedClass('wrapper'),
  wrapperTypeClassName: computed("type", function() {
    return this.get("type") + "-input";
  }),
  unitClassName:    mergedClass('unit'),
  errorsClassName:  mergedClass('errors'),
  fieldClassName:   mergedClass('field'),
  inputClassName:   mergedClass('input'),
  hintClassName:    mergedClass('hint'),
  attributeClassName: computed('attr', function() {
    let attr = this.get('attr');
    return attr ? `${dasherize(attr)}-attr-input` : null;
  }),
  modelClassName: computed('modelName', function() {
    let modelName = this.get('modelName');
    return  modelName ? `${dasherize(modelName)}-model-input` : null;
  }),

  inputComponentName: computed("attr", "builder", function() {
    return "inputs/" + this.get("type") + "-input";
  }),

  inputElementId: computed("elementId", function() {
    return this.get("elementId") + "Input";
  }),

  inputName: computed('builder.name', 'attr', function() {
    var prefix = this.get('builder.name');
    var name = this.get('attr');
    if (isPresent(prefix)) {
      name = prefix + '[' + name + ']';
    }
    return name;
  }),

  label: computed("builder.translationKey", "attr", "labelTranslation", function() {
    var key;

    if (isPresent(this.get("labelTranslation"))) {
      key = this.get("labelTranslation");
    } else {
      key = this.get("builder.translationKey") + ".attributes." + this.get("attr");
    }

    var result;
    if (this.get("translationService").exists(key)) {
      result = this.get("translationService").t(key);
    }
    if (isEmpty(result)) {
      result = humanize(this.get("attr"));
    }
    return result;
  }),

  hasLabel: computed("inlineLabel", "label", function() {
    return !this.get("inlineLabel") && this.get("label") !== false;
  }),

  inlineLabel: computed("type", function() {
    return this.get("type") === "boolean";
  }),

  hasInlineLabel: computed("inlineLabel", "label", function() {
    return this.get("inlineLabel") && this.get("label") !== false;
  }),

  hasHint: computed("hint", function() {
    return isPresent(this.get("hint"));
  }),

  hint: computed("builder.translationKey", "attr", "hintTranslation", function() {
    var key;

    if (isPresent(this.get("hintTranslation"))) {
      key = this.get("hintTranslation");
    } else {
      key = this.get("builder.translationKey") + ".hints." + this.get("attr");
    }

    if (this.get("translationService").exists(key)) {
      return this.get("translationService").t(key);
    }
  }),

  placeholder: computed("builder.translationKey", "attr", "placeholderTranslation", function() {
    var key;

    if (isPresent(this.get("placeholderTranslation"))) {
      key = this.get("placeholderTranslation");
    } else {
      key = this.get("builder.translationKey") + ".placeholders." + this.get("attr");
    }

    if (this.get("translationService").exists(key)) {
      return this.get("translationService").t(key);
    }
  })
};

let simpleInputAttributeNames = Object.keys(extension).concat(["builder", "attr"]);
const SimpleInput = Component.extend(extension);

SimpleInput.reopenClass({
  positionalParams: ["attr"]
});

export default SimpleInput;
<|endoftext|>


<commit_before>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { computed } from '@ember/object';
import { isBlank, isEmpty } from '@ember/utils';
import layout from '../templates/components/single-event';
import moment from 'moment';

export default Component.extend({
  layout,
  store: service(),
  i18n: service(),
  event: null,
  classNames: ['single-event'],

  taughtBy: computed('i18n.locale', 'event.instructors', function(){
    const instructors = this.get('event.instructors');
    if (isEmpty(instructors)) {
      return '';
    }
    return this.get('i18n').t('general.taughtBy', {instructors});
  }),

  sessionIs: computed('event.sessionType', 'i18n.locale', function(){
    const i18n = this.get('i18n');
    const type = this.get('event.sessionTypeTitle');
    return i18n.t('general.sessionIs', { type });
  }),

  courseObjectives: computed('i18n.locale', 'event.courseObjectives.[]', 'event.competencies.[]', async function(){
    const i18n = this.get('i18n');
    const event = this.get('event');
    const objectives =  event.courseObjectives;
    const competencies = event.competencies;
    return objectives.map(objective => {
      //strip all HTML
      const title = objective.title.replace(/(<([^>]+)>)/ig,"");
      const position = objective.position;
      if(isEmpty(objective.competencies)) {
        return {
          id: objective.id,
          title,
          domain: i18n.t('general.noAssociatedCompetencies'),
          position
        };
      }
      const competencyId = objective.competencies[0];
      const competency = competencies.findBy('id', competencyId);
      const parentId = competency.parent;
      let domain = competency;
      if (! isEmpty(parentId)) {
        domain = competencies.findBy('id', parentId);
      }
      return {
        id: objective.id,
        title,
        domain: competency.title + ' (' + domain.title + ')',
        position
      };
    }).sort(this.positionSortingCallback);
  }),

  typedLearningMaterials: computed('event.learningMaterials', function() {
    const lms = this.get('event.learningMaterials') || [];
    lms.forEach(lm => {
      if (lm.isBlanked) {
        lm['type'] = 'unknown';
        return;
      }
      if (!isBlank(lm.citation)) {
        lm['type'] = 'citation';
      } else if (!isBlank(lm.link)) {
        lm['type'] = 'link';
      } else {
        lm['type'] = 'file';
      }
    });
    return lms;
  }),

  courseLearningMaterials: computed('i18n.locale', 'typedLearningMaterials', function() {
    const eventLms = this.get('typedLearningMaterials') || [];
    return eventLms.filterBy('courseLearningMaterial').sort((lm1, lm2) => {
      let pos1 = lm1.position || 0;
      let pos2 = lm2.position || 0;

      // 1. position, asc
      if (pos1 > pos2) {
        return 1;
      } else if (pos1 < pos2) {
        return -1;
      }

      // 2. course learning material id, desc
      let id1 = lm1.courseLearningMaterial;
      let id2 = lm2.courseLearningMaterial;
      if (id1 > id2) {
        return -1;
      } else if (id1 < id2) {
        return 1;
      }
      return 0;
    });
  }),

  sessionObjectives: computed('i18n.locale', 'event.sessionObjectives.[]', 'event.competencies.[]', async function(){
    const i18n = this.get('i18n');
    const event = this.get('event');
    const objectives =  event.sessionObjectives;
    const competencies = event.competencies;
    return objectives.map(objective => {
      //strip all HTML
      const title = objective.title.replace(/(<([^>]+)>)/ig,"");
      const position = objective.position;
      if(isEmpty(objective.competencies)) {
        return {
          id: objective.id,
          title,
          domain: i18n.t('general.noAssociatedCompetencies'),
          position
        };
      }
      const competencyId = objective.competencies[0];
      const competency = competencies.findBy('id', competencyId);
      const parentId = competency.parent;
      let domain = competency;
      if (! isEmpty(parentId)) {
        domain = competencies.findBy('id', parentId);
      }
      return {
        id: objective.id,
        title,
        domain: competency.title + ' (' + domain.title + ')',
        position
      };
    }).sort(this.positionSortingCallback);
  }),

  sessionLearningMaterials: computed('i18n.locale', 'typedLearningMaterials', function() {
    const eventLms = this.get('typedLearningMaterials') || [];
    return eventLms.filterBy('sessionLearningMaterial').sort((lm1, lm2) => {
      let pos1 = lm1.position || 0;
      let pos2 = lm2.position || 0;

      // 1. position, asc
      if (pos1 > pos2) {
        return 1;
      } else if (pos1 < pos2) {
        return -1;
      }

      // 2. session learning material id, desc
      let id1 = lm1.sessionLearningMaterial;
      let id2 = lm2.sessionLearningMaterial;
      if (id1 > id2) {
        return -1;
      } else if (id1 < id2) {
        return 1;
      }
      return 0;
    });
  }),

  course: computed('session.course', async function(){
    const session = await this.get('session');
    return await session.get('course');
  }),

  session: computed('event.session', async function () {
    const store = this.get('store');
    const sessionId = this.get('event.session');
    return store.find('session', sessionId);
  }),

  recentlyUpdated: computed('event.lastModified', function(){
    const lastModifiedDate = moment(this.get('event.lastModified'));
    const today = moment();
    const daysSinceLastUpdate = today.diff(lastModifiedDate, 'days');
    return daysSinceLastUpdate < 6;
  }),

  /**
   * Callback function for Array.sort().
   * Compares two given Objects by their position property (in ascending order), and then by id (descending).
   *
   * @method positionSortingCallback
   * @param {Object} obj1
   * @param {Object} obj2
   * @return {Number}
   */
  positionSortingCallback(obj1, obj2) {
    let pos1 = obj1.position;
    let pos2 = obj2.position;
    // 1. position, asc
    if (pos1 > pos2) {
      return 1;
    } else if (pos1 < pos2) {
      return -1;
    }

    // 2. id, desc
    let id1 = obj1.id;
    let id2 = obj2.id;
    if (id1 > id2) {
      return -1;
    } else if (id1 < id2) {
      return 1;
    }
    return 0;
  },
});
<commit_msg>Remove old computed properties<commit_after>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { computed } from '@ember/object';
import { isBlank, isEmpty } from '@ember/utils';
import layout from '../templates/components/single-event';
import moment from 'moment';

export default Component.extend({
  layout,
  store: service(),
  i18n: service(),
  event: null,
  classNames: ['single-event'],

  taughtBy: computed('i18n.locale', 'event.instructors', function(){
    const instructors = this.get('event.instructors');
    if (isEmpty(instructors)) {
      return '';
    }
    return this.get('i18n').t('general.taughtBy', {instructors});
  }),

  sessionIs: computed('event.sessionType', 'i18n.locale', function(){
    const i18n = this.get('i18n');
    const type = this.get('event.sessionTypeTitle');
    return i18n.t('general.sessionIs', { type });
  }),

  courseObjectives: computed('i18n.locale', 'event.courseObjectives.[]', 'event.competencies.[]', async function(){
    const i18n = this.get('i18n');
    const event = this.get('event');
    const objectives =  event.courseObjectives;
    const competencies = event.competencies;
    return objectives.map(objective => {
      //strip all HTML
      const title = objective.title.replace(/(<([^>]+)>)/ig,"");
      const position = objective.position;
      if(isEmpty(objective.competencies)) {
        return {
          id: objective.id,
          title,
          domain: i18n.t('general.noAssociatedCompetencies'),
          position
        };
      }
      const competencyId = objective.competencies[0];
      const competency = competencies.findBy('id', competencyId);
      const parentId = competency.parent;
      let domain = competency;
      if (! isEmpty(parentId)) {
        domain = competencies.findBy('id', parentId);
      }
      return {
        id: objective.id,
        title,
        domain: competency.title + ' (' + domain.title + ')',
        position
      };
    }).sort(this.positionSortingCallback);
  }),

  typedLearningMaterials: computed('event.learningMaterials', function() {
    const lms = this.get('event.learningMaterials') || [];
    lms.forEach(lm => {
      if (lm.isBlanked) {
        lm['type'] = 'unknown';
        return;
      }
      if (!isBlank(lm.citation)) {
        lm['type'] = 'citation';
      } else if (!isBlank(lm.link)) {
        lm['type'] = 'link';
      } else {
        lm['type'] = 'file';
      }
    });
    return lms;
  }),

  courseLearningMaterials: computed('i18n.locale', 'typedLearningMaterials', function() {
    const eventLms = this.get('typedLearningMaterials') || [];
    return eventLms.filterBy('courseLearningMaterial').sort((lm1, lm2) => {
      let pos1 = lm1.position || 0;
      let pos2 = lm2.position || 0;

      // 1. position, asc
      if (pos1 > pos2) {
        return 1;
      } else if (pos1 < pos2) {
        return -1;
      }

      // 2. course learning material id, desc
      let id1 = lm1.courseLearningMaterial;
      let id2 = lm2.courseLearningMaterial;
      if (id1 > id2) {
        return -1;
      } else if (id1 < id2) {
        return 1;
      }
      return 0;
    });
  }),

  sessionObjectives: computed('i18n.locale', 'event.sessionObjectives.[]', 'event.competencies.[]', async function(){
    const i18n = this.get('i18n');
    const event = this.get('event');
    const objectives =  event.sessionObjectives;
    const competencies = event.competencies;
    return objectives.map(objective => {
      //strip all HTML
      const title = objective.title.replace(/(<([^>]+)>)/ig,"");
      const position = objective.position;
      if(isEmpty(objective.competencies)) {
        return {
          id: objective.id,
          title,
          domain: i18n.t('general.noAssociatedCompetencies'),
          position
        };
      }
      const competencyId = objective.competencies[0];
      const competency = competencies.findBy('id', competencyId);
      const parentId = competency.parent;
      let domain = competency;
      if (! isEmpty(parentId)) {
        domain = competencies.findBy('id', parentId);
      }
      return {
        id: objective.id,
        title,
        domain: competency.title + ' (' + domain.title + ')',
        position
      };
    }).sort(this.positionSortingCallback);
  }),

  sessionLearningMaterials: computed('i18n.locale', 'typedLearningMaterials', function() {
    const eventLms = this.get('typedLearningMaterials') || [];
    return eventLms.filterBy('sessionLearningMaterial').sort((lm1, lm2) => {
      let pos1 = lm1.position || 0;
      let pos2 = lm2.position || 0;

      // 1. position, asc
      if (pos1 > pos2) {
        return 1;
      } else if (pos1 < pos2) {
        return -1;
      }

      // 2. session learning material id, desc
      let id1 = lm1.sessionLearningMaterial;
      let id2 = lm2.sessionLearningMaterial;
      if (id1 > id2) {
        return -1;
      } else if (id1 < id2) {
        return 1;
      }
      return 0;
    });
  }),

  recentlyUpdated: computed('event.lastModified', function(){
    const lastModifiedDate = moment(this.get('event.lastModified'));
    const today = moment();
    const daysSinceLastUpdate = today.diff(lastModifiedDate, 'days');
    return daysSinceLastUpdate < 6;
  }),

  /**
   * Callback function for Array.sort().
   * Compares two given Objects by their position property (in ascending order), and then by id (descending).
   *
   * @method positionSortingCallback
   * @param {Object} obj1
   * @param {Object} obj2
   * @return {Number}
   */
  positionSortingCallback(obj1, obj2) {
    let pos1 = obj1.position;
    let pos2 = obj2.position;
    // 1. position, asc
    if (pos1 > pos2) {
      return 1;
    } else if (pos1 < pos2) {
      return -1;
    }

    // 2. id, desc
    let id1 = obj1.id;
    let id2 = obj2.id;
    if (id1 > id2) {
      return -1;
    } else if (id1 < id2) {
      return 1;
    }
    return 0;
  },
});
<|endoftext|>


<commit_before>/* global window, navigator */

import Ember from 'ember';
import layout from '../templates/components/smart-banner';
import getOwner from 'ember-getowner-polyfill';
import bannerStorage from '../utils/banner-storage';

const {
  computed,
} = Ember;

const {
  getDayClosed,
  getDayVisited,
  setDayClosed,
  setDayVisited,
} = bannerStorage;

export default Ember.Component.extend({
  layout,

  classNames: ['ember-smart-banner'],
  // http://discuss.emberjs.com/t/best-practices-accessing-app-config-from-addon-code/7006/16
  config: computed(function() {
    return getOwner(this).resolveRegistration('config:environment').emberSmartBanner;
  }),

  title: computed.or('titleIOS', 'titleAndroid', 'config.title', 'bannerDefaults.title'),
  description: computed.or('descriptionIOS', 'descriptionAndroid', 'config.description', 'bannerDefaults.description'),
  linkText: computed.or('linkTextIOS', 'linkTextAndroid', 'config.linkText', 'bannerDefaults.linkText'),
  iconUrl: computed.or('config.iconUrl', 'bannerDefaults.iconUrl'),
  showBanner: computed.and('bannerOpen', 'supportsOS', 'afterCloseBool', 'afterVisitBool'), // Set showBanner to true to always show
  link: computed.or('displayAppStoreLink', 'displayMarketLink'),

  userAgent: computed(function() {
    return (navigator.userAgent || navigator.vendor || window.opera);
  }),

  supportsOS: computed.or('supportsIOS', 'supportsAndroid'),
  supportsIOS: computed.and('iOS', 'appIdIOS'),
  supportsAndroid: computed.and('android', 'appIdAndroid'),

  iOS: computed('userAgent', function() {
    const userAgent = this.get('userAgent');
    return (userAgent.match(/iPad/i) || userAgent.match(/iPhone/i) || userAgent.match(/iPod/i));
  }),

  android: computed('userAgent', function() {
    const userAgent = this.get('userAgent');
    return (userAgent.match(/Android/i));
  }),

  titleIOS: computed.and('iOS', 'config.titleIOS'),
  descriptionIOS: computed.and('iOS', 'config.descriptionIOS'),
  linkTextIOS: computed.and('iOS', 'config.linkTextIOS'),
  appStoreLanguage: computed.or('config.appStoreLanguage', 'bannerDefaults.appStoreLanguage'),
  appIdIOS: computed.or('config.appIdIOS', 'bannerDefaults.appIdIOS'),
  appStoreLink: computed(function() {
    return (
      `${this.get('bannerDefaults.appStoreLinkBase')}/${this.get('appStoreLanguage')}` +
        `/app/id${this.get('appIdIOS')}`
    );
  }),
  displayAppStoreLink: computed.and('supportsIOS','appStoreLink'),

  titleAndroid: computed.and('android', 'config.titleAndroid'),
  descriptionAndroid: computed.and('android', 'config.descriptionAndroid'),
  linkTextAndroid: computed.and('android', 'config.linkTextAndroid'),
  appIdAndroid: computed.or('config.appIdAndroid', 'bannerDefaults.appIdAndroid'),
  marketLink: computed(function() {
    return `${this.get('bannerDefaults.marketLinkBase')}${this.get('appIdAndroid')}`;
  }),
  displayMarketLink: computed.and('supportsAndroid','marketLink'),

  bannerDefaults: {
    appStoreLinkBase: 'https://itunes.apple.com',
    appStoreLanguage: 'en',
    marketLinkBase: 'market://details?id=',
    title: 'Download our App',
    linkText: 'View',
    iconUrl: 'http://icons.iconarchive.com/icons/wineass/ios7-redesign/512/Appstore-icon.png'
  },

  bannerClosed: false,
  bannerOpen: computed.not('bannerClosed'),

  actions: {
    openLink: function() {
      this.set('bannerClosed', true);
      setDayVisited();
      const visitFn = Ember.getWithDefault(this, 'attrs.onvisit', Ember.K);
      visitFn();
    },

    closeBanner: function(e) {
      e.preventDefault();
      this.set('bannerClosed', true);
      setDayClosed();
      const closeFn = Ember.getWithDefault(this, 'attrs.onclose', Ember.K);
      closeFn();
    }
  },

  // Number of days after close to wait to show banner again
  // Set to true if always show banner after clicking the close button
  // Set false if the banner never shows again after clicking close
  openAfterClose: computed.reads('config.openAfterClose'),

  // Number of days after visit to wait to show banner again
  // Set to true if always show banner after clicking the visit button
  // Set false if the banner never shows again after clicking visit
  openAfterVisit: computed.reads('config.openAfterVisit'),

  neverShowAfterClose: computed.equal('openAfterClose', false),
  recentlyClosed: computed.bool('daysSinceClose'),
  restrictAfterClose: restrictMacro('openAfterClose'),

  neverShowAfterVisit: computed.equal('openAfterVisit', false),
  recentlyVisited: computed.bool('daysSinceVisit'),
  restrictAfterVisit: restrictMacro('openAfterVisit'),

  afterCloseBool: computed('daysSinceClose', 'openAfterClose', function() {
    const wasRecentlyClosed = this.get('recentlyClosed');
    const neverShowAfterClose = this.get('neverShowAfterClose');
    const restrictAfterClose = this.get('restrictAfterClose');

    if (neverShowAfterClose && wasRecentlyClosed) {
      // never show if { openAfterClose: false } && has been closed
      return false;
    }

    if (restrictAfterClose && wasRecentlyClosed) {
      // if { openAfterClose: isValidNumber }
      return this.gteDependentKeys('daysSinceClose', 'openAfterClose');
    }

    return true;
  }),

  afterVisitBool: computed('daysSinceVisit', 'openAfterVisit', function() {
    const wasRecentlyVisited = this.get('recentlyVisited');
    const neverShowAfterVisit = this.get('neverShowAfterVisit');
    const restrictAfterVisit = this.get('restrictAfterVisit');

    if (neverShowAfterVisit && wasRecentlyVisited) {
      // never show if { openAfterVisit: false } && has been visited
      return false;
    }

    if (restrictAfterVisit && wasRecentlyVisited) {
      // if { openAfterVisit: isValidNumber }
      return this.gteDependentKeys('daysSinceVisit', 'openAfterVisit');
    }

    return true;
  }),

  gteDependentKeys(firstKey, secondKey) {
    return (this.get(firstKey) >= this.get(secondKey));
  },

  daysSinceClose: computed(function() {
    const timeSinceClosed = new Date() - Date.parse(getDayClosed());
    return Math.floor(timeSinceClosed / (24 * 60 * 60 * 1000)); // Convert ms to days
  }),

  daysSinceVisit: computed(function() {
    const timeSinceVisited = new Date() - Date.parse(getDayVisited());
    return Math.floor(timeSinceVisited / (24 * 60 * 60 * 1000)); // Convert ms to days
  }),

  //https://github.com/jasny/jquery.smartbanner/blob/master/jquery.smartbanner.js
});

function restrictMacro(delayKey) {
  return computed(delayKey, function() {
    const openDelay = this.get(delayKey);
    const isValidNumber = isNaN(parseInt(openDelay, 10)) === false;
    return isValidNumber;
  });
}
<commit_msg>Add comments to clarify localstorage-reading api<commit_after>/* global window, navigator */

import Ember from 'ember';
import layout from '../templates/components/smart-banner';
import getOwner from 'ember-getowner-polyfill';
import bannerStorage from '../utils/banner-storage';

const {
  computed,
} = Ember;

const {
  getDayClosed,
  getDayVisited,
  setDayClosed,
  setDayVisited,
} = bannerStorage;

export default Ember.Component.extend({
  layout,

  classNames: ['ember-smart-banner'],
  // http://discuss.emberjs.com/t/best-practices-accessing-app-config-from-addon-code/7006/16
  config: computed(function() {
    return getOwner(this).resolveRegistration('config:environment').emberSmartBanner;
  }),

  title: computed.or('titleIOS', 'titleAndroid', 'config.title', 'bannerDefaults.title'),
  description: computed.or('descriptionIOS', 'descriptionAndroid', 'config.description', 'bannerDefaults.description'),
  linkText: computed.or('linkTextIOS', 'linkTextAndroid', 'config.linkText', 'bannerDefaults.linkText'),
  iconUrl: computed.or('config.iconUrl', 'bannerDefaults.iconUrl'),
  showBanner: computed.and('bannerOpen', 'supportsOS', 'afterCloseBool', 'afterVisitBool'), // Set showBanner to true to always show
  link: computed.or('displayAppStoreLink', 'displayMarketLink'),

  userAgent: computed(function() {
    return (navigator.userAgent || navigator.vendor || window.opera);
  }),

  supportsOS: computed.or('supportsIOS', 'supportsAndroid'),
  supportsIOS: computed.and('iOS', 'appIdIOS'),
  supportsAndroid: computed.and('android', 'appIdAndroid'),

  iOS: computed('userAgent', function() {
    const userAgent = this.get('userAgent');
    return (userAgent.match(/iPad/i) || userAgent.match(/iPhone/i) || userAgent.match(/iPod/i));
  }),

  android: computed('userAgent', function() {
    const userAgent = this.get('userAgent');
    return (userAgent.match(/Android/i));
  }),

  titleIOS: computed.and('iOS', 'config.titleIOS'),
  descriptionIOS: computed.and('iOS', 'config.descriptionIOS'),
  linkTextIOS: computed.and('iOS', 'config.linkTextIOS'),
  appStoreLanguage: computed.or('config.appStoreLanguage', 'bannerDefaults.appStoreLanguage'),
  appIdIOS: computed.or('config.appIdIOS', 'bannerDefaults.appIdIOS'),
  appStoreLink: computed(function() {
    return (
      `${this.get('bannerDefaults.appStoreLinkBase')}/${this.get('appStoreLanguage')}` +
        `/app/id${this.get('appIdIOS')}`
    );
  }),
  displayAppStoreLink: computed.and('supportsIOS','appStoreLink'),

  titleAndroid: computed.and('android', 'config.titleAndroid'),
  descriptionAndroid: computed.and('android', 'config.descriptionAndroid'),
  linkTextAndroid: computed.and('android', 'config.linkTextAndroid'),
  appIdAndroid: computed.or('config.appIdAndroid', 'bannerDefaults.appIdAndroid'),
  marketLink: computed(function() {
    return `${this.get('bannerDefaults.marketLinkBase')}${this.get('appIdAndroid')}`;
  }),
  displayMarketLink: computed.and('supportsAndroid','marketLink'),

  bannerDefaults: {
    appStoreLinkBase: 'https://itunes.apple.com',
    appStoreLanguage: 'en',
    marketLinkBase: 'market://details?id=',
    title: 'Download our App',
    linkText: 'View',
    iconUrl: 'http://icons.iconarchive.com/icons/wineass/ios7-redesign/512/Appstore-icon.png'
  },

  bannerClosed: false,
  bannerOpen: computed.not('bannerClosed'),

  actions: {
    openLink: function() {
      this.set('bannerClosed', true);
      setDayVisited();
      const visitFn = Ember.getWithDefault(this, 'attrs.onvisit', Ember.K);
      visitFn();
    },

    closeBanner: function(e) {
      e.preventDefault();
      this.set('bannerClosed', true);
      setDayClosed();
      const closeFn = Ember.getWithDefault(this, 'attrs.onclose', Ember.K);
      closeFn();
    }
  },

  // Number of days after close to wait to show banner again
  // Set to true if always show banner after clicking the close button
  // Set false if the banner never shows again after clicking close
  openAfterClose: computed.reads('config.openAfterClose'),

  // Number of days after visit to wait to show banner again
  // Set to true if always show banner after clicking the visit button
  // Set false if the banner never shows again after clicking visit
  openAfterVisit: computed.reads('config.openAfterVisit'),

  neverShowAfterClose: computed.equal('openAfterClose', false),
  recentlyClosed: computed.bool('daysSinceClose'),
  restrictAfterClose: restrictMacro('openAfterClose'),

  neverShowAfterVisit: computed.equal('openAfterVisit', false),
  recentlyVisited: computed.bool('daysSinceVisit'),
  restrictAfterVisit: restrictMacro('openAfterVisit'),

  afterCloseBool: computed('daysSinceClose', 'openAfterClose', function() {
    const wasRecentlyClosed = this.get('recentlyClosed');
    const neverShowAfterClose = this.get('neverShowAfterClose');
    const restrictAfterClose = this.get('restrictAfterClose');

    if (neverShowAfterClose && wasRecentlyClosed) {
      // never show if { openAfterClose: false } && has been closed
      return false;
    }

    if (restrictAfterClose && wasRecentlyClosed) {
      // if { openAfterClose: isValidNumber }
      return this.gteDependentKeys('daysSinceClose', 'openAfterClose');
    }

    return true;
  }),

  afterVisitBool: computed('daysSinceVisit', 'openAfterVisit', function() {
    const wasRecentlyVisited = this.get('recentlyVisited');
    const neverShowAfterVisit = this.get('neverShowAfterVisit');
    const restrictAfterVisit = this.get('restrictAfterVisit');

    if (neverShowAfterVisit && wasRecentlyVisited) {
      // never show if { openAfterVisit: false } && has been visited
      return false;
    }

    if (restrictAfterVisit && wasRecentlyVisited) {
      // if { openAfterVisit: isValidNumber }
      return this.gteDependentKeys('daysSinceVisit', 'openAfterVisit');
    }

    return true;
  }),

  gteDependentKeys(firstKey, secondKey) {
    return (this.get(firstKey) >= this.get(secondKey));
  },

  // Returns NaN if no localstorage data has been set
  daysSinceClose: computed(function() {
    const timeSinceClosed = new Date() - Date.parse(getDayClosed());
    return Math.floor(timeSinceClosed / (24 * 60 * 60 * 1000)); // Convert ms to days
  }),

  // Returns NaN if no localstorage data has been set
  daysSinceVisit: computed(function() {
    const timeSinceVisited = new Date() - Date.parse(getDayVisited());
    return Math.floor(timeSinceVisited / (24 * 60 * 60 * 1000)); // Convert ms to days
  }),

  //https://github.com/jasny/jquery.smartbanner/blob/master/jquery.smartbanner.js
});

function restrictMacro(delayKey) {
  return computed(delayKey, function() {
    const openDelay = this.get(delayKey);
    const isValidNumber = isNaN(parseInt(openDelay, 10)) === false;
    return isValidNumber;
  });
}
<|endoftext|>


<commit_before>import { isEmpty } from '@ember/utils';
import { helper } from '@ember/component/helper';
import { htmlSafe } from '@ember/string';
import markdownit from 'markdown-it';
import markdownItAttrs from 'markdown-it-attrs';

function targetLinks(html) {
  let origin = window.location.origin;

  let parser = new DOMParser();
  let doc = parser.parseFromString(html, 'text/html');
  let nodes = doc.querySelectorAll(`a[href^='mailto'], a[href^='http']:not([href^='${origin}'])`);

  for (let i = 0; i < nodes.length; i++) {
    let node = nodes[i];

    node.setAttribute('target', '_blank');
    node.setAttribute('rel', 'noopener noreferrer');
  }

  let newHTML = doc.body.innerHTML;

  return newHTML;
}

export function renderMarkdown([raw]) {
  const renderer = markdownit({
    html: true
  });

  renderer.use(markdownItAttrs);

  if (isEmpty(raw)) {
    return '';
  } else {
    let html = renderer.render(raw);

    html = targetLinks(html);

    return htmlSafe(html);
  }
}

export default helper(renderMarkdown);
<commit_msg>[SP-876] handle null doc for IE 11<commit_after>import { isEmpty } from '@ember/utils';
import { helper } from '@ember/component/helper';
import { htmlSafe } from '@ember/string';
import markdownit from 'markdown-it';
import markdownItAttrs from 'markdown-it-attrs';

function targetLinks(html) {
  let origin = window.location.origin;

  let parser = new DOMParser();
  let doc = parser.parseFromString(html, 'text/html');
  let nodes = doc.querySelectorAll(`a[href^='mailto'], a[href^='http']:not([href^='${origin}'])`);

  for (let i = 0; i < nodes.length; i++) {
    let node = nodes[i];

    node.setAttribute('target', '_blank');
    node.setAttribute('rel', 'noopener noreferrer');
  }

  // IE 11 returns NULL on empty string :/
  return doc.body ? doc.body.innerHTML : '';
}

export function renderMarkdown([raw]) {
  const renderer = markdownit({
    html: true
  });

  renderer.use(markdownItAttrs);

  if (isEmpty(raw)) {
    return '';
  } else {
    let html = renderer.render(raw);

    html = targetLinks(html);

    return htmlSafe(html);
  }
}

export default helper(renderMarkdown);
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';

export default DS.RESTSerializer.extend({

  primaryKey: 'objectId',

  normalizeArrayResponse: function( store, primaryType, payload ) {
    console.log("normalizeArrayResponse")

    var namespacedPayload = {};
    namespacedPayload[ Ember.String.pluralize( primaryType.modelName ) ] = payload.results;

    return this._super( store, primaryType, namespacedPayload );
  },

  normalizeSingleResponse: function( store, primaryType, payload, recordId ) {
    console.log("normalizeSingleResponse")

    var namespacedPayload = {};
    namespacedPayload[ primaryType.modelName ] = payload; // this.normalize(primaryType, payload);

    return this._super( store, primaryType, namespacedPayload, recordId );
  },

  modelNameFromPayloadKey: function( key ) {
    console.log("modelNameFromPayloadKey")

    return Ember.String.dasherize( Ember.String.singularize( key ) );
  },

  /**
  * Because Parse only returns the updatedAt/createdAt values on updates
  * we have to intercept it here to assure that the adapter knows which
  * record ID we are dealing with (using the primaryKey).
  */
  normalizeResponse: function( store, primaryModelClass, payload, id, requestType ) {
    console.log("normalizeResponse")

    if( id !== null && ( 'updateRecord' === requestType || 'deleteRecord' === requestType ) ) {
      payload[ this.get( 'primaryKey' ) ] = id;
    }

    return this._super( store, primaryModelClass, payload, id, requestType );
  },

  /**
  * Extracts count from the payload so that you can get the total number
  * of records in Parse if you're using skip and limit.
  */
  extractMeta: function( store, type, payload ) {
    console.log("extractMeta")

    if ( payload && payload.count ) {
      store.metadataFor( type, { count: payload.count } );
      delete payload.count;
    }
  },

  /**
  * Special handling for the Date objects inside the properties of
  * Parse responses.
  */
  extractAttributes: function( type, hash ) {
    console.log("normalizeAttributes")
    console.log(type)
    console.log(hash)

    type.eachAttribute( function( key, meta ) {

      if ( 'date' === meta.type && 'object' === Ember.typeOf( hash[key] ) && hash[key].iso ) {
        hash[key] = hash[key].iso; //new Date(hash[key].iso).toISOString();
      }
    });
    this._super( type, hash );
  },

  /**
  * Special handling of the Parse relation types. In certain
  * conditions there is a secondary query to retrieve the "many"
  * side of the "hasMany".
  */
  extractRelationships: function( type, hash ) {
    console.log("normalizeRelationships")
    console.log(type)
    console.log(hash)

    var store      = this.get('store'),
      serializer = this;

    type.eachRelationship( function( key, relationship ) {
      console.log("type.eachRelationship")

      console.log(key)
      console.log(relationship)
      console.log(relationship.options)
      console.log(relationship.kind)

      var options = relationship.options;

      // Handle the belongsTo relationships
      if ( hash[key] && 'belongsTo' === relationship.kind ) {
        console.log(hash[key].objectId)

        hash[key] = hash[key].objectId;
      }

      // Handle the hasMany relationships
      if ( hash[key] && 'hasMany' === relationship.kind ) {

        // If this is a Relation hasMany then we need to supply
        // the links property so the adapter can async call the
        // relationship.
        // The adapter findHasMany has been overridden to make use of this.
        if ( options.relation ) {
          hash.links = {};
          hash.links[key] = { type: relationship.type, key: key };
        }

        if ( options.array ) {
          // Parse will return [null] for empty relationships
          if ( hash[key].length && hash[key] ) {
            hash[key].forEach( function( item, index, items ) {
              // When items are pointers we just need the id
              // This occurs when request was made without the include query param.
              if ( 'Pointer' === item.__type ) {
                items[index] = item.objectId;

              } else {
                // When items are objects we need to clean them and add them to the store.
                // This occurs when request was made with the include query param.
                delete item.__type;
                delete item.className;
                item.id = item.objectId;
                delete item.objectId;
                item.type = relationship.type;
                serializer.extractAttributes( relationship.type, item );
                serializer.extractRelationships( relationship.type, item );
                store.push( relationship.type, item );
              }
            });
          }
        }
      }

      console.log("here")
    }, this );
    console.log("here 2")
    console.log(hash)

    this._super( type, hash );
    console.log("hash")

  },

  serializeIntoHash: function( hash, type, record, options ) {
    console.log("serializeIntoHash")

    Ember.merge( hash, this.serialize( record, options ) );
  },

  serializeAttribute: function( record, json, key, attribute ) {
    console.log("serializeAttribute")

    // These are Parse reserved properties and we won't send them.
    if ( 'createdAt' === key ||
         'updatedAt' === key ||
         'emailVerified' === key ||
         'sessionToken' === key
    ) {
      delete json[key];

    } else {
      this._super( record, json, key, attribute );
    }
  },

  serializeBelongsTo: function( record, json, relationship ) {
    console.log("serializeBelongsTo")

    var key       = relationship.key,
      belongsTo = record.get( key );

    if ( belongsTo ) {
      // @TODO: Perhaps this is working around a bug in Ember-Data? Why should
      // promises be returned here.
      if ( belongsTo instanceof DS.PromiseObject ) {
        if ( !belongsTo.get('isFulfilled' ) ) {
          throw new Error( 'belongsTo values *must* be fulfilled before attempting to serialize them' );
        }

        belongsTo = belongsTo.get( 'content' );
      }

      json[key] = {
        '__type'    : 'Pointer',
        'className' : this.parseClassName( belongsTo.constructor.modelName ),
        'objectId'  : belongsTo.get( 'id' )
      };
    }
  },

  parseClassName: function( key ) {
    console.log("parseClassName")

    if ( 'parseUser' === key) {
      return '_User';

    } else {
      return Ember.String.capitalize( Ember.String.camelize( key ) );
    }
  },

  serializeHasMany: function( record, json, relationship ) {
    console.log("serializeHasMany")

    var key     = relationship.key,
      hasMany = record.get( key ),
      options = relationship.options;

    if ( hasMany && hasMany.get( 'length' ) > 0 ) {
      json[key] = { 'objects': [] };

      if ( options.relation ) {
        json[key].__op = 'AddRelation';
      }

      if ( options.array ) {
        json[key].__op = 'AddUnique';
      }

      hasMany.forEach( function( child ) {
        json[key].objects.push({
          '__type'    : 'Pointer',
          'className' : child.parseClassName(),
          'objectId'  : child.get( 'id' )
        });
      });

      if ( hasMany._deletedItems && hasMany._deletedItems.length ) {
        if ( options.relation ) {
          var addOperation    = json[key],
            deleteOperation = { '__op': 'RemoveRelation', 'objects': [] };

          hasMany._deletedItems.forEach( function( item ) {
            deleteOperation.objects.push({
              '__type'    : 'Pointer',
              'className' : item.type,
              'objectId'  : item.id
            });
          });

          json[key] = { '__op': 'Batch', 'ops': [addOperation, deleteOperation] };
        }

        if ( options.array ) {
          json[key].deleteds = { '__op': 'Remove', 'objects': [] };

          hasMany._deletedItems.forEach( function( item ) {
            json[key].deleteds.objects.push({
              '__type'    : 'Pointer',
              'className' : item.type,
              'objectId'  : item.id
            });
          });
        }
      }

    } else {
      json[key] = [];
    }
  }

});
<commit_msg>Migrating normalizeRelationships<commit_after>import Ember from 'ember';
import DS from 'ember-data';

export default DS.RESTSerializer.extend({

  primaryKey: 'objectId',

  normalizeArrayResponse: function( store, primaryType, payload ) {
    console.log("normalizeArrayResponse")

    var namespacedPayload = {};
    namespacedPayload[ Ember.String.pluralize( primaryType.modelName ) ] = payload.results;

    return this._super( store, primaryType, namespacedPayload );
  },

  normalizeSingleResponse: function( store, primaryType, payload, recordId ) {
    console.log("normalizeSingleResponse")

    var namespacedPayload = {};
    namespacedPayload[ primaryType.modelName ] = payload; // this.normalize(primaryType, payload);

    return this._super( store, primaryType, namespacedPayload, recordId );
  },

  modelNameFromPayloadKey: function( key ) {
    console.log("modelNameFromPayloadKey")

    return Ember.String.dasherize( Ember.String.singularize( key ) );
  },

  /**
  * Because Parse only returns the updatedAt/createdAt values on updates
  * we have to intercept it here to assure that the adapter knows which
  * record ID we are dealing with (using the primaryKey).
  */
  normalizeResponse: function( store, primaryModelClass, payload, id, requestType ) {
    console.log("normalizeResponse")

    if( id !== null && ( 'updateRecord' === requestType || 'deleteRecord' === requestType ) ) {
      payload[ this.get( 'primaryKey' ) ] = id;
    }

    return this._super( store, primaryModelClass, payload, id, requestType );
  },

  /**
  * Extracts count from the payload so that you can get the total number
  * of records in Parse if you're using skip and limit.
  */
  extractMeta: function( store, type, payload ) {
    console.log("extractMeta")

    if ( payload && payload.count ) {
      store.metadataFor( type, { count: payload.count } );
      delete payload.count;
    }
  },

  /**
  * Special handling for the Date objects inside the properties of
  * Parse responses.
  */
  extractAttributes: function( type, hash ) {
    console.log("extractAttributes")
    type.eachAttribute( function( key, meta ) {
      if ( 'date' === meta.type && 'object' === Ember.typeOf( hash[key] ) && hash[key].iso ) {
        hash[key] = hash[key].iso; //new Date(hash[key].iso).toISOString();
      }
    });
    return hash
  },

  /**
  * Special handling of the Parse relation types. In certain
  * conditions there is a secondary query to retrieve the "many"
  * side of the "hasMany".
  */
  extractRelationships: function( type, hash ) {
    console.log("normalizeRelationships")
    console.log(type)
    console.log(hash)

    var store      = this.get('store'),
      serializer = this;

    type.eachRelationship( function( key, relationship ) {
      console.log("type.eachRelationship")

      console.log(key)
      console.log(relationship)
      console.log(relationship.options)
      console.log(relationship.kind)

      var options = relationship.options;

      // Handle the belongsTo relationships

      console.log(hash[key] && 'belongsTo' === relationship.kind)
      if ( hash[key] && 'belongsTo' === relationship.kind ) {
        console.log(hash[key].objectId)
        var item = hash[key]
        this.handleRelationshipPayload(item, hash, key, relationship, serializer,store)

        // hash[key] = hash[key].objectId;
      }

      // Handle the hasMany relationships
      if ( hash[key] && 'hasMany' === relationship.kind ) {

        // If this is a Relation hasMany then we need to supply
        // the links property so the adapter can async call the
        // relationship.
        // The adapter findHasMany has been overridden to make use of this.
        if ( options.relation ) {
          hash.links = {};
          hash.links[key] = { type: relationship.type, key: key };
        }

        if ( options.array ) {
          // Parse will return [null] for empty relationships
          if ( hash[key].length && hash[key] ) {
            hash[key].forEach( function( item, index, items ) {
              // When items are pointers we just need the id
              // This occurs when request was made without the include query param.
              // if ( 'Pointer' === item.__type ) {
              //   items[index] = item.objectId;
              //
              // } else {
              //   // When items are objects we need to clean them and add them to the store.
              //   // This occurs when request was made with the include query param.
              //   delete item.__type;
              //   delete item.className;
              //   item.id = item.objectId;
              //   delete item.objectId;
              //   item.type = relationship.type;
              //   serializer.extractAttributes( relationship.type, item );
              //   serializer.extractRelationships( relationship.type, item );
              //   store.push( relationship.type, item );
              // }

              handleRelationshipPayload(item, hash, index, relationship, serializer,store)
            });
          }
        }
      }

      console.log("here")
    }, this );
    console.log("here 2")
    console.log(hash)

    return hash
    console.log("hash")

  },

  handleRelationshipPayload: function(item, hash, key, relationship,  serializer, store) {

    if ( 'Pointer' === item.__type ) {
      hash[key] = item.objectId;

    } else {
      // When items are objects we need to clean them and add them to the store.
      // This occurs when request was made with the include query param.
      delete item.__type;
      delete item.className;
      item.id = item.objectId;
      delete item.objectId;
      item.type = relationship.type;
      var modelType = store.modelFor(relationship.type)
      serializer.extractAttributes( modelType, item );
      serializer.extractRelationships( modelType, item );
      var serialized = serializer.normalize( modelType, item );
      serialized.data.id = item["id"]
      store.push(serialized);
    }

  },

  serializeIntoHash: function( hash, type, record, options ) {
    console.log("serializeIntoHash")

    Ember.merge( hash, this.serialize( record, options ) );
  },

  serializeAttribute: function( record, json, key, attribute ) {
    console.log("serializeAttribute")

    // These are Parse reserved properties and we won't send them.
    if ( 'createdAt' === key ||
         'updatedAt' === key ||
         'emailVerified' === key ||
         'sessionToken' === key
    ) {
      delete json[key];

    } else {
      this._super( record, json, key, attribute );
    }
  },

  serializeBelongsTo: function( record, json, relationship ) {
    console.log("serializeBelongsTo")

    var key       = relationship.key,
      belongsTo = record.get( key );

    if ( belongsTo ) {
      // @TODO: Perhaps this is working around a bug in Ember-Data? Why should
      // promises be returned here.
      if ( belongsTo instanceof DS.PromiseObject ) {
        if ( !belongsTo.get('isFulfilled' ) ) {
          throw new Error( 'belongsTo values *must* be fulfilled before attempting to serialize them' );
        }

        belongsTo = belongsTo.get( 'content' );
      }

      json[key] = {
        '__type'    : 'Pointer',
        'className' : this.parseClassName( belongsTo.constructor.modelName ),
        'objectId'  : belongsTo.get( 'id' )
      };
    }
  },

  parseClassName: function( key ) {
    console.log("parseClassName")

    if ( 'parseUser' === key) {
      return '_User';

    } else {
      return Ember.String.capitalize( Ember.String.camelize( key ) );
    }
  },

  serializeHasMany: function( record, json, relationship ) {
    console.log("serializeHasMany")

    var key     = relationship.key,
      hasMany = record.get( key ),
      options = relationship.options;

    if ( hasMany && hasMany.get( 'length' ) > 0 ) {
      json[key] = { 'objects': [] };

      if ( options.relation ) {
        json[key].__op = 'AddRelation';
      }

      if ( options.array ) {
        json[key].__op = 'AddUnique';
      }

      hasMany.forEach( function( child ) {
        json[key].objects.push({
          '__type'    : 'Pointer',
          'className' : child.parseClassName(),
          'objectId'  : child.get( 'id' )
        });
      });

      if ( hasMany._deletedItems && hasMany._deletedItems.length ) {
        if ( options.relation ) {
          var addOperation    = json[key],
            deleteOperation = { '__op': 'RemoveRelation', 'objects': [] };

          hasMany._deletedItems.forEach( function( item ) {
            deleteOperation.objects.push({
              '__type'    : 'Pointer',
              'className' : item.type,
              'objectId'  : item.id
            });
          });

          json[key] = { '__op': 'Batch', 'ops': [addOperation, deleteOperation] };
        }

        if ( options.array ) {
          json[key].deleteds = { '__op': 'Remove', 'objects': [] };

          hasMany._deletedItems.forEach( function( item ) {
            json[key].deleteds.objects.push({
              '__type'    : 'Pointer',
              'className' : item.type,
              'objectId'  : item.id
            });
          });
        }
      }

    } else {
      json[key] = [];
    }
  }

});
<|endoftext|>


<commit_before>var port = 7777;
var host = "127.0.0.1";

var dgram = require("dgram");
var server = dgram.createSocket("udp4");

server.on("error",function(err){
	console.log("Server error:\n", err.stack);
	server.close();
});

server.on("listening", function(){
	var address = server.address();
	console.log("Server listening ", address.address, ":", address.port);
});

server.on("message", function(msg, rinfo){
	console.log("message: ", msg, rinfo.address, ":", rinfo.port);

	var res = new Buffer('OK');
	var client = dgram.createSocket("udp4");
	client.send(res, 0, res.length, rinfo.port, rinfo.host, function(err, bytes){
		client.close();
	});

});

server.bind(port, host);<commit_msg>WS: Simple http server defined<commit_after>
var servers = {
	udp: {
		host: "127.0.0.1",
		port: 7777
	},
	http: {
		host: "127.0.0.1",
		port: 8080
	}
}

// UDP Server

var dgram = require("dgram");
var udpServer = dgram.createSocket("udp4");

udpServer.on("error",function(err){
	console.log("Server error:\n", err.stack);
	udpServer.close();
});

udpServer.on("listening", function(){
	var address = udpServer.address();
	console.log("UDP Server listening ", address.address, ":", address.port);
});

udpServer.on("message", function(msg, rinfo){
	console.log("message: ", msg, rinfo.address, ":", rinfo.port);

	var res = new Buffer('OK');
	var client = dgram.createSocket("udp4");
	client.send(res, 0, res.length, rinfo.port, rinfo.host, function(err, bytes){
		client.close();
	});

});

udpServer.bind(servers.udp.port, servers.udp.host);

// HTTP Server

var http = require('http');

var httpServer = http.createServer(function(req, res){
	res.writeHead(200, 'Content-Type: text/json');
	res.end('{}');
});

httpServer = httpServer.listen(servers.http.port, servers.http.host);

console.log("HTTP Server listening ", servers.http.host, ":", servers.http.port);<|endoftext|>


<commit_before>// You need to bring in jQuery first in order for this to work
//
// Call it like this:
// pkTagahead(<?php echo json_encode(url_for("taggableComplete/complete")) ?>);
//
// Or similar. Now all of your input elements with the input-tag class
// automatically gain the typeahead suggestion feature.
//
// If you're not using Symfony and sfDoctrineActAsTaggablePlugin,
// pass your own URL that returns a
 containing
s with the
// FULL TEXT of what the ENTIRE tag string will be if the user takes
// that suggestion, with the new tag you're suggesting an  link
// to #. Then use CSS to hide (visibility: none) the part of the
//
 that is not in the . Don't introduce any extra whitespace.

function pkTagahead(tagaheadUrl)
{
  $(function() {
    function getKey(event)
    {
      // Portable keycodes sigh
      return event.keyCode ? event.keyCode : event.which;
    }
    function setClick(target)
    {
      $(target).find('a').click(function(event)
      {
        // span contains ul contains li contains a
        var span = this.parentNode.parentNode.parentNode;
        var input = $(span).data("tag-peer");
        // Get text from the li
        var parent = this.parentNode;
        $(input).val($(parent).text());
        $(input).focus();
        return false;
      });
    }
    // Add suggestions span (you'll need to style that)
    $('input.tag-input').after("

");
    // Each tag field remembers its suggestions span...
    $('input.tag-input').each(function()
    {
      $(this).data("tag-peer", $(this).next()[0]);
    });
    // And vice versa
    $('div.tag-suggestions').each(function()
    {
      $(this).data("tag-peer", $(this).prev()[0]);
    });
    // Now we can really throw down
    $('input.tag-input').keyup(function(event)
    {
      var key = getKey(event);
      // Tab key
      if (key == 9)
      {
        var peer = $(this).data("tag-peer");
        var suggestions = $(peer).find("li");
        if (suggestions.length)
        {
          $(this).val($(suggestions[0]).text());
          $(this).focus();
        }
        // In any case don't insert the tab
        return false;
      }
      else
      {
        // Trigger ajax update of suggestions
      }
    });
    $('input.tag-input').keypress(function(event)
    {
      // Firefox 2.0 mac is stubborn and only allows cancel here
      // (we will still get the keyup and do the real work there)
      var key = getKey(event);
      if (key == 9)
      {
        // Don't insert tabs, ever
        return false;
      }
    });
    var lastValues = {};
    setInterval(function()
    {
      // AJAX query for suggestions only when changes have taken place
      $('input.tag-input').each(function()
      {
        var last = $(this).data('tag-last');
        var value = $(this).val();
        var peer = $(this).data('tag-peer');
        if (last !== value)
        {
          $(this).data('tag-last', value);
          $.post(
            tagaheadUrl,
            {
              current: $(this).val()
            },
            function(data, textStatus)
            {
              $(peer).html(data);
              setClick(peer);
            }
          );
        }
      });
    }, 200);
  });
}

function aInlineTaggableWidget(selector, options)
{
	var typeaheadUrl = options['typeahead-url'];

	function makeLink(attributes, title, text)
	{
		var new_link = $('');
		new_link.attr(attributes);
		new_link.attr('title', title);
		new_link.text(text);

		return new_link;
	}

	function trimExcessCommas(string)
	{
		string = string.replace(/(^,)|(, ?$)/g, '');
		string = string.replace(/(,,)|(, ,)/, ',');
		string = $.trim(string);

		return string;
	}

	$(selector).each(function()
	{
		var popularTags = options['popular-tags'];
		var existingTags = options['existing-tags'];
		var popularsAttributes = {};
		var existingTagsAttributes = {};
		var existingDiv = $('
');
		var popularsDiv = $('
');

		// Establish the quick enhancement
		var tagInput = $(this);
		var typeAheadBox = $('<input />');
		typeAheadBox.attr('type', 'text');

		var addButton = $('');
		addButton.text('Add');
		addButton.attr({'href' : '#', 'class' : 'add-tags-link', 'title' : 'Add these tags'});

		tagInput.hide();
		tagInput.parent().append(typeAheadBox);
		tagInput.parent().append(addButton);

		// Add a list of popular tags to be added
		function addTagsToForm(link)
		{
			tag = link.attr('title');

			var value = tagInput.val() + ', ' + tag;
			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			var new_link = makeLink(existingTagsAttributes, tag, 'x ' + tag);
			new_link.bind('click', function() { removeTagsFromForm($(this)); return false; });
			existingDiv.append(new_link);
		}

		// Add a list of tags that may be removed
		function removeTagsFromForm(link)
		{
			tag = link.attr('title');
			var value = tagInput.val();

			value = value.replace(tag, '');
			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			if (typeof(popularTags[tag]) != 'undefined')
			{
				var linkLabel = tag + ' - ' + popularTags[tag];
				var new_link = makeLink(existingTagsAttributes, tag, linkLabel);
				new_link.bind('click', function() { addTagsToForm($(this)); return false; });
				popularsDiv.append(new_link);
			}
		}

		// a maker function for tag containers
		function makeTagContainer(containerLabel, tagArray, linkAttributes, linkLabelType)
		{
			// Add a list of tags that may be removed
			var tagContainer = $('
');
			tagContainer.addClass('a-inline-taggable-widget-tag-container');
			var header = $('
');
			header.text(containerLabel);
			tagContainer.append(header);

			var attributes = {};
			for (x in tagArray)
			{
				var linkLabel = '';
				if (linkLabelType == 'add')
				{
					linkLabel = x + ' - ' + tagArray[x];
				}
				else if (linkLabelType == 'remove')
				{
					linkLabel = 'x ' + x;
				}

				var new_link = makeLink(linkAttributes, x, linkLabel);

				if (linkLabelType == 'add')
				{
					new_link.bind('click', function() { addTagsToForm($(this));  return false; });
				}
				else if (linkLabelType == 'remove')
				{
					new_link.bind('click', function() { removeTagsFromForm($(this));  return false; });
				}

				tagContainer.append(new_link);
			}
			return tagContainer;
		}

		// Add the new tags to the existing form input.
		// If the user doesn't click Save changes or add... tough?
		function commitTagsToForm()
		{
			var value = tagInput.val() + ',' + typeAheadBox.val();
			value = trimExcessCommas(value);
			tagInput.val(value);
			typeAheadBox.val('');

			existingTags = {};
			var lp = value.split(',');
			for (x in lp)
			{
				existingTags[lp[x]] = lp[x];
			}

			existingDiv.html(makeTagContainer('Existing Tags', existingTags, existingTagsAttributes, 'remove').html());
			existingDiv.children('a').bind('click', function() { removeTagsFromForm($(this)); return false; });

			return false;
		}
		addButton.bind('click', function() { commitTagsToForm(); return false; });

		existingDiv = makeTagContainer('Existing Tags', existingTags, existingTagsAttributes, 'remove');
		tagInput.parent().prepend(existingDiv);

		popularsDiv = makeTagContainer('Popular Tags', popularTags, popularsAttributes, 'add');
		tagInput.parent().append(popularsDiv);

		// TypeAhead
		typeAheadBox.autocomplete(typeaheadUrl,
			{
				multiple: true
			});

		// Catch that enter key, baby!
		$(document).keyup(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		$(document).keypress(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		typeAheadBox.keyup(function(e)
			{
		 		if (e.keyCode == 13)
		 		{
		 			commitTagsToForm();
		 		}
		 	});
	});

}
<commit_msg>InlineTaggableWidget: performance enhancements, code readability enhancements, and moved functionality for unsetting existing popular tags to this script.<commit_after>// You need to bring in jQuery first in order for this to work
//
// Call it like this:
// pkTagahead(<?php echo json_encode(url_for("taggableComplete/complete")) ?>);
//
// Or similar. Now all of your input elements with the input-tag class
// automatically gain the typeahead suggestion feature.
//
// If you're not using Symfony and sfDoctrineActAsTaggablePlugin,
// pass your own URL that returns a
 containing
s with the
// FULL TEXT of what the ENTIRE tag string will be if the user takes
// that suggestion, with the new tag you're suggesting an  link
// to #. Then use CSS to hide (visibility: none) the part of the
//
 that is not in the . Don't introduce any extra whitespace.

function pkTagahead(tagaheadUrl)
{
  $(function() {
    function getKey(event)
    {
      // Portable keycodes sigh
      return event.keyCode ? event.keyCode : event.which;
    }
    function setClick(target)
    {
      $(target).find('a').click(function(event)
      {
        // span contains ul contains li contains a
        var span = this.parentNode.parentNode.parentNode;
        var input = $(span).data("tag-peer");
        // Get text from the li
        var parent = this.parentNode;
        $(input).val($(parent).text());
        $(input).focus();
        return false;
      });
    }
    // Add suggestions span (you'll need to style that)
    $('input.tag-input').after("

");
    // Each tag field remembers its suggestions span...
    $('input.tag-input').each(function()
    {
      $(this).data("tag-peer", $(this).next()[0]);
    });
    // And vice versa
    $('div.tag-suggestions').each(function()
    {
      $(this).data("tag-peer", $(this).prev()[0]);
    });
    // Now we can really throw down
    $('input.tag-input').keyup(function(event)
    {
      var key = getKey(event);
      // Tab key
      if (key == 9)
      {
        var peer = $(this).data("tag-peer");
        var suggestions = $(peer).find("li");
        if (suggestions.length)
        {
          $(this).val($(suggestions[0]).text());
          $(this).focus();
        }
        // In any case don't insert the tab
        return false;
      }
      else
      {
        // Trigger ajax update of suggestions
      }
    });
    $('input.tag-input').keypress(function(event)
    {
      // Firefox 2.0 mac is stubborn and only allows cancel here
      // (we will still get the keyup and do the real work there)
      var key = getKey(event);
      if (key == 9)
      {
        // Don't insert tabs, ever
        return false;
      }
    });
    var lastValues = {};
    setInterval(function()
    {
      // AJAX query for suggestions only when changes have taken place
      $('input.tag-input').each(function()
      {
        var last = $(this).data('tag-last');
        var value = $(this).val();
        var peer = $(this).data('tag-peer');
        if (last !== value)
        {
          $(this).data('tag-last', value);
          $.post(
            tagaheadUrl,
            {
              current: $(this).val()
            },
            function(data, textStatus)
            {
              $(peer).html(data);
              setClick(peer);
            }
          );
        }
      });
    }, 200);
  });
}

function aInlineTaggableWidget(selector, options)
{
	var typeaheadUrl = options['typeahead-url'];

	function makeLink(attributes, title, text)
	{
		var new_link = $('');
		new_link.attr(attributes);
		new_link.attr('title', title);
		new_link.text(text);

		return new_link;
	}

	function trimExcessCommas(string)
	{
		string = string.replace(/(^,)|(, ?$)/g, '');
		string = string.replace(/(,,)|(, ,)/, ',');
		string = $.trim(string);

		return string;
	}

	$(selector).each(function()
	{
		var popularTags = options['popular-tags'];
		var existingTags = options['existing-tags'];

		// We don't want to display popular tags that we're already using
		var unusedPopulars = {};
		for (x in popularTags)
		{
			if (typeof(existingTags[x]) == 'undefined')
			{
				unusedPopulars[x] = popularTags[x];
			}
		}

		var popularsAttributes = {};
		var existingTagsAttributes = {};
		var existingDiv = $('
');
		var popularsDiv = $('
');

		// Establish the quick enhancement
		var tagInput = $(this);
		var typeAheadBox = $('<input />');
		typeAheadBox.attr('type', 'text');

		var addButton = $('');
		addButton.text('Add');
		addButton.attr({'href' : '#', 'class' : 'add-tags-link', 'title' : 'Add these tags'});

		tagInput.hide();
		tagInput.parent().append(typeAheadBox);
		tagInput.parent().append(addButton);

		// Add a list of popular tags to be added
		function addTagsToForm(link)
		{
			tag = link.attr('title');

			var value = tagInput.val() + ', ' + tag;
			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			var new_link = makeLink(existingTagsAttributes, tag, 'x ' + tag);
			new_link.bind('click', function() { removeTagsFromForm($(this)); return false; });
			existingDiv.append(new_link);
		}

		// Add a list of tags that may be removed
		function removeTagsFromForm(link)
		{
			tag = link.attr('title');
			var value = tagInput.val();

			value = value.replace(tag, '');
			value = trimExcessCommas(value);
			tagInput.val(value);

			link.remove();

			// As we have just removed it from the list, we want the real deal populars.
			if (typeof(popularTags[tag]) != 'undefined')
			{
				var linkLabel = tag + ' - ' + popularTags[tag];
				var new_link = makeLink(existingTagsAttributes, tag, linkLabel);
				new_link.bind('click', function() { addTagsToForm($(this)); return false; });
				popularsDiv.append(new_link);
			}
		}

		// a maker function for tag containers
		function makeTagContainer(containerLabel, tagArray, linkAttributes, linkLabelType)
		{
			// Add a list of tags that may be removed
			var tagContainer = $('
');
			tagContainer.addClass('a-inline-taggable-widget-tag-container');
			var header = $('
');
			header.text(containerLabel);
			tagContainer.append(header);

			var attributes = {};
			for (x in tagArray)
			{
				var linkLabel = '';
				if (linkLabelType == 'add')
				{
					linkLabel = x + ' - ' + tagArray[x];
				}
				else if (linkLabelType == 'remove')
				{
					linkLabel = 'x ' + x;
				}

				var new_link = makeLink(linkAttributes, x, linkLabel);

				tagContainer.append(new_link);
			}
			return tagContainer;
		}

		// Add the new tags to the existing form input.
		// If the user doesn't click Save changes or add... tough?
		function commitTagsToForm()
		{
			var value = tagInput.val() + ',' + typeAheadBox.val();
			value = trimExcessCommas(value);
			tagInput.val(value);
			typeAheadBox.val('');

			existingTags = {};
			var lp = value.split(',');
			for (x in lp)
			{
				existingTags[lp[x]] = lp[x];
			}

			existingDiv.html(makeTagContainer('Existing Tags', existingTags, existingTagsAttributes, 'remove').html());
			existingDiv.children('a').bind('click', function() { removeTagsFromForm($(this)); return false; });

			return false;
		}
		addButton.bind('click', function() { commitTagsToForm(); return false; });

		existingDiv = makeTagContainer('Existing Tags', existingTags, existingTagsAttributes, 'remove');
		existingDiv.children('a').bind('click', function() { removeTagsFromForm($(this));  return false; });
		tagInput.parent().prepend(existingDiv);

		popularsDiv = makeTagContainer('Popular Tags', unusedPopulars, popularsAttributes, 'add');
		popularsDiv.children('a').bind('click', function() { addTagsToForm($(this));  return false; });
		tagInput.parent().append(popularsDiv);

		// TypeAhead
		typeAheadBox.autocomplete(typeaheadUrl,
			{
				multiple: true
			});

		// Catch that enter key, baby!
		$(document).keyup(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		$(document).keypress(function(e)
			{
				if (e.keyCode == 13)
				{
					e.preventDefault();
				}
			});

		typeAheadBox.keyup(function(e)
			{
		 		if (e.keyCode == 13)
		 		{
		 			commitTagsToForm();
		 		}
		 	});
	});

}
<|endoftext|>


<commit_before>'use strict';

app.controller('party/profile', [
  '$scope', 'displayService', 'party', 'pageService','constantService',
  function ($scope, display, party, page, constants) {

    var getUsers = function(volumes){

      var users = {};

      users.sponsors = [];
      users.labGroupMembers = [];
      users.nonGroupAffiliates = [];
      users.otherCollaborators = [];

      _(volumes).pluck('access').flatten().each(function(v){
        console.log("V: ", v);
        if(v.children > 0 ){
          users.sponsors.push(v);
        } else if(v.parents && v.parents.length && v.party.members && v.party.members.length) {
          users.labGroupMembers.push(v);
        } else if(v.parents && v.parents.length){
          users.nonGroupAffiliates.push(v);
        } else {
          users.otherCollaborators.push(v);
        }

      }).value();

      console.log(users);
      return users;
    };

    // This is a quick helper function to make sure that the isSelected
    // class is set to empty and to avoid repeating code.
    var unSetSelected = function(v){
      v.isSelected = '';
      if(v.v !== undefined){
        v.v = _.map(v.v, function(a){
          a.isSelected = '';
          return a;
        });
      }

      return v;
    };

    var unselectAll = function(){

      $scope.volumes.individual = _.map($scope.volumes.individual, unSetSelected);

      $scope.volumes.collaborator = _.map($scope.volumes.collaborator, unSetSelected);

      $scope.volumes.inherited = _.map($scope.volumes.inherited, unSetSelected);

      $scope.users.sponsors = _.map($scope.users.sponsors, unSetSelected);

      $scope.users.nonGroupAffiliates = _.map($scope.users.nonGroupAffiliates, unSetSelected);

      $scope.users.labGroupMembers = _.map($scope.users.labGroupMembers, unSetSelected);

      $scope.users.otherCollaborators = _.map($scope.users.otherCollaborators, unSetSelected);

    };

    $scope.clickVolume = function(volume) {

      unselectAll();

      volume.isSelected = 'volumeSelected';

      for(var i = 0; i < volume.access.length; i += 1 ){

        var j;

        for (j = 0; j < $scope.users.sponsors.length; j += 1){
          if($scope.users.sponsors[j].party.id === volume.access[i].party.id){
            $scope.users.sponsors[j].isSelected = 'userSelected';
          }
        }

        for (j = 0; j < $scope.users.labGroupMembers.length; j += 1){
          if($scope.users.labGroupMembers[j].party.id === volume.access[i].party.id){
            $scope.users.labGroupMembers[j].isSelected = 'userSelected';
          }
        }

        for (j = 0; j < $scope.users.nonGroupAffiliates.length; j += 1){
          if($scope.users.nonGroupAffiliates[j].party.id === volume.access[i].party.id){
            $scope.users.nonGroupAffiliates[j].isSelected = 'userSelected';
          }
        }

        for (j = 0; j < $scope.users.otherCollaborators.length; j += 1){
          if($scope.users.otherCollaborators[j].party.id === volume.access[i].party.id){
            $scope.users.otherCollaborators[j].isSelected = 'userSelected';
          }
        }

      }
    };

    // This should take in a user, then select volumes on each thing.
    $scope.clickUser = function(user){
      unselectAll();

      var i, j, k;

      for(i = 0; i < $scope.volumes.individual.length; i++){
        for(j = 0; j < $scope.volumes.individual[i].v.length; j++){
          for(k = 0; k < $scope.volumes.individual[i].v[j].access; k++){
            if($scope.volumes.individual[i].v[j].access[k].party.id == user.party.id){
              $scope.volumes.individual[i].v[j].isSelected = 'volumeSelected';
            }
          }
        }
      }

      for(i = 0; i < $scope.volumes.collaborator.length; i++){
        for(j = 0; j < $scope.volumes.collaborator[i].v.length; j++){
          for(k = 0; k < $scope.volumes.collaborator[i].v[j].access.length; k++){
            if($scope.volumes.collaborator[i].v[j].access[k].party.id == user.party.id) {
              $scope.volumes.collaborator[i].v[j].isSelected = 'volumeSelected';
            }
          }
        }
      }

      for(i = 0; i < $scope.volumes.inherited.length; i++){
        for(j = 0; j < $scope.volumes.inherited[i].v.length; j++){
          for(k = 0; k < $scope.volumes.inherited[i].v[j].access.length; k++){
            if($scope.volumes.inherited[i].v[j].access[k].party.id == user.party.id) {
              $scope.volumes.inherited[i].v[j].isSelected = 'volumeSelected';
            }
          }
        }
      }
    };

    var getParents = function(parents) {
      var tempParents = [];
      _.each(parents, function(p){
        if(p.member){
          var v = [];
          var tempThing = {
            p: p,
            v: v
          };
          tempParents.push(tempThing);
        }
      });
      return tempParents;
    };

    var getVolumes = function(volumes) {
      var tempVolumes = {};
      tempVolumes.individual = [];
      tempVolumes.collaborator = [];

      tempVolumes.inherited = getParents(party.parents);

      _.each(volumes, function(v){
        if(v.isIndividual){
          tempVolumes.individual.push({v: [v]});
        } else if(tempVolumes.isCollaborator){
          tempVolumes.collaborator.push({v: [v]});
        } else{
          for (var i=0;i<v.access.length;i++){
            for (var j=0;j<tempVolumes.inherited.length;j++){
              if (v.access[i].children && v.access[i].party.id === tempVolumes.inherited[j].p.party.id){
                tempVolumes.inherited[j].v.push(v);
                break;
              }
            }
          }
        }
      });
      return tempVolumes;
    };

    $scope.party = party;
    $scope.volumes = getVolumes(party.volumes);
    console.log("Volumes: ", $scope.volumes);
    $scope.users = getUsers(party.volumes);
    $scope.page = page;
    $scope.profile = page.$location.path() === '/profile';
    display.title = party.name;

    console.log($scope.volumes.inherited);
  }
]);
<commit_msg>Fix duplicate users coming up.<commit_after>'use strict';

app.controller('party/profile', [
  '$scope', 'displayService', 'party', 'pageService','constantService',
  function ($scope, display, party, page, constants) {

    var getUsers = function(volumes){

      var users = {};

      users.sponsors = [];
      users.labGroupMembers = [];
      users.nonGroupAffiliates = [];
      users.otherCollaborators = [];

      _(volumes).pluck('access').flatten().uniq(function(i){
        return i.party.id;
      }).each(function(v){
        console.log("V: ", v);
        if(v.children > 0 ){
          users.sponsors.push(v);
        } else if(v.parents && v.parents.length && v.party.members && v.party.members.length) {
          users.labGroupMembers.push(v);
        } else if(v.parents && v.parents.length){
          users.nonGroupAffiliates.push(v);
        } else {
          users.otherCollaborators.push(v);
        }

      }).value();

      console.log(users);
      return users;
    };

    // This is a quick helper function to make sure that the isSelected
    // class is set to empty and to avoid repeating code.
    var unSetSelected = function(v){
      v.isSelected = '';
      if(v.v !== undefined){
        v.v = _.map(v.v, function(a){
          a.isSelected = '';
          return a;
        });
      }

      return v;
    };

    var unselectAll = function(){

      $scope.volumes.individual = _.map($scope.volumes.individual, unSetSelected);

      $scope.volumes.collaborator = _.map($scope.volumes.collaborator, unSetSelected);

      $scope.volumes.inherited = _.map($scope.volumes.inherited, unSetSelected);

      $scope.users.sponsors = _.map($scope.users.sponsors, unSetSelected);

      $scope.users.nonGroupAffiliates = _.map($scope.users.nonGroupAffiliates, unSetSelected);

      $scope.users.labGroupMembers = _.map($scope.users.labGroupMembers, unSetSelected);

      $scope.users.otherCollaborators = _.map($scope.users.otherCollaborators, unSetSelected);

    };

    $scope.clickVolume = function(volume) {

      unselectAll();

      volume.isSelected = 'volumeSelected';

      for(var i = 0; i < volume.access.length; i += 1 ){

        var j;

        for (j = 0; j < $scope.users.sponsors.length; j += 1){
          if($scope.users.sponsors[j].party.id === volume.access[i].party.id){
            $scope.users.sponsors[j].isSelected = 'userSelected';
          }
        }

        for (j = 0; j < $scope.users.labGroupMembers.length; j += 1){
          if($scope.users.labGroupMembers[j].party.id === volume.access[i].party.id){
            $scope.users.labGroupMembers[j].isSelected = 'userSelected';
          }
        }

        for (j = 0; j < $scope.users.nonGroupAffiliates.length; j += 1){
          if($scope.users.nonGroupAffiliates[j].party.id === volume.access[i].party.id){
            $scope.users.nonGroupAffiliates[j].isSelected = 'userSelected';
          }
        }

        for (j = 0; j < $scope.users.otherCollaborators.length; j += 1){
          if($scope.users.otherCollaborators[j].party.id === volume.access[i].party.id){
            $scope.users.otherCollaborators[j].isSelected = 'userSelected';
          }
        }

      }
    };

    // This should take in a user, then select volumes on each thing.
    $scope.clickUser = function(user){
      unselectAll();

      var i, j, k;

      for(i = 0; i < $scope.volumes.individual.length; i++){
        for(j = 0; j < $scope.volumes.individual[i].v.length; j++){
          for(k = 0; k < $scope.volumes.individual[i].v[j].access; k++){
            if($scope.volumes.individual[i].v[j].access[k].party.id == user.party.id){
              $scope.volumes.individual[i].v[j].isSelected = 'volumeSelected';
            }
          }
        }
      }

      for(i = 0; i < $scope.volumes.collaborator.length; i++){
        for(j = 0; j < $scope.volumes.collaborator[i].v.length; j++){
          for(k = 0; k < $scope.volumes.collaborator[i].v[j].access.length; k++){
            if($scope.volumes.collaborator[i].v[j].access[k].party.id == user.party.id) {
              $scope.volumes.collaborator[i].v[j].isSelected = 'volumeSelected';
            }
          }
        }
      }

      for(i = 0; i < $scope.volumes.inherited.length; i++){
        for(j = 0; j < $scope.volumes.inherited[i].v.length; j++){
          for(k = 0; k < $scope.volumes.inherited[i].v[j].access.length; k++){
            if($scope.volumes.inherited[i].v[j].access[k].party.id == user.party.id) {
              $scope.volumes.inherited[i].v[j].isSelected = 'volumeSelected';
            }
          }
        }
      }
    };

    var getParents = function(parents) {
      var tempParents = [];
      _.each(parents, function(p){
        if(p.member){
          var v = [];
          var tempThing = {
            p: p,
            v: v
          };
          tempParents.push(tempThing);
        }
      });
      return tempParents;
    };

    var getVolumes = function(volumes) {
      var tempVolumes = {};
      tempVolumes.individual = [];
      tempVolumes.collaborator = [];

      tempVolumes.inherited = getParents(party.parents);

      _.each(volumes, function(v){
        if(v.isIndividual){
          tempVolumes.individual.push({v: [v]});
        } else if(tempVolumes.isCollaborator){
          tempVolumes.collaborator.push({v: [v]});
        } else{
          for (var i=0;i<v.access.length;i++){
            for (var j=0;j<tempVolumes.inherited.length;j++){
              if (v.access[i].children && v.access[i].party.id === tempVolumes.inherited[j].p.party.id){
                tempVolumes.inherited[j].v.push(v);
                break;
              }
            }
          }
        }
      });
      return tempVolumes;
    };

    $scope.party = party;
    $scope.volumes = getVolumes(party.volumes);
    console.log("Volumes: ", $scope.volumes);
    $scope.users = getUsers(party.volumes);
    $scope.page = page;
    $scope.profile = page.$location.path() === '/profile';
    display.title = party.name;

    console.log($scope.volumes.inherited);
  }
]);
<|endoftext|>