<commit_before>const Utils = require('../utils');

/**
 * A hand-coded <select>-like element which allows the selection of one of a
 * thing's properties. In a perfect world this would be a styled select, but
 * this is not a perfect world.
 * @constructor
 * @param {DevicePropertyBlock} devicePropertyBlock
 * @param {Element} parent
 * @param {ThingDescription} thing
 */
function PropertySelect(devicePropertyBlock, parent, thing) {
  this.devicePropertyBlock = devicePropertyBlock;
  this.thing = thing;
  this.role = '';

  this.elt = document.createElement('div');
  this.elt.classList.add('property-select');

  this.onClick = this.onClick.bind(this);
  this.elt.addEventListener('click', this.onClick);

  this.clearOptions();

  // Disable dragging started by clicking property select
  this.elt.addEventListener('mousedown', (e) => {
    e.stopPropagation();
  });
  this.elt.addEventListener('touchstart', (e) => {
    e.stopPropagation();
  });

  parent.appendChild(this.elt);
}

/**
 * Reset the options list to only include the placeholder Select Property
 */
PropertySelect.prototype.clearOptions = function() {
  this.elt.innerHTML = '';
  this.addOption('Select Property', null, true);
};

/**
 * Add an option to the options list
 * @param {String} name - text content of option
 * @param {Object} ruleFragment - associated rule fragment with either trigger
 * or property defined
 * @param {boolean} selected - if the option is selected
 */
PropertySelect.prototype.addOption = function(name, ruleFragment, selected) {
  const elt = document.createElement('div');
  elt.classList.add('property-select-option');
  if (selected) {
    elt.classList.add('selected');
  }
  elt.dataset.ruleFragment = JSON.stringify(ruleFragment);
  const nameElt = document.createElement('span');
  nameElt.classList.add('property-select-name');
  nameElt.textContent = name;
  nameElt.title = name;
  elt.appendChild(nameElt);

  if (!ruleFragment) {
    this.elt.appendChild(elt);
    return;
  }

  const property = getProperty(ruleFragment);
  if (!property) {
    this.updateOption(elt);
    this.elt.appendChild(elt);
    return;
  }

  const stopPropagation = (e) => {
    e.stopPropagation();
  };

  if (property.type === 'number') {
    let ltOption, gtOption;
    if (ruleFragment.trigger) {
      ltOption = document.createElement('option');
      ltOption.textContent = '<';
      ltOption.classList.add('lt-option');
      gtOption = document.createElement('option');
      gtOption.textContent = '>';
      gtOption.classList.add('gt-option');

      const select = document.createElement('select');
      select.appendChild(ltOption);
      select.appendChild(gtOption);
      select.addEventListener('click', stopPropagation);
      elt.appendChild(select);
    }

    const valueInput = document.createElement('input');
    valueInput.classList.add('value-input');
    valueInput.type = 'number';
    valueInput.addEventListener('click', stopPropagation);
    elt.appendChild(valueInput);

    elt.addEventListener('change', () => {
      if (ruleFragment.trigger) {
        if (ltOption.selected) {
          ruleFragment.trigger.levelType = 'LESS';
        } else if (gtOption.selected) {
          ruleFragment.trigger.levelType = 'GREATER';
        }
        ruleFragment.trigger.value = parseFloat(valueInput.value);
      } else {
        ruleFragment.effect.value = parseFloat(valueInput.value);
      }
      this.devicePropertyBlock.rulePart = ruleFragment;
      this.devicePropertyBlock.onRuleChange();
      elt.dataset.ruleFragment = JSON.stringify(ruleFragment);
    });
  } else if (property.type === 'string') {
    const valueInput = document.createElement('input');
    valueInput.classList.add('value-input');
    if (property.name === 'color') {
      valueInput.type = 'color';
    } else {
      valueInput.type = 'text';
    }
    valueInput.addEventListener('click', stopPropagation);
    elt.appendChild(valueInput);

    elt.addEventListener('change', () => {
      const dpbRulePart = this.devicePropertyBlock.rulePart;
      let selected = !dpbRulePart;
      if (ruleFragment.trigger) {
        ruleFragment.trigger.value = valueInput.value;
        selected = selected || (dpbRulePart.trigger &&
          dpbRulePart.trigger.type === ruleFragment.trigger.type);
      } else {
        ruleFragment.effect.value = valueInput.value;
        selected = selected || (dpbRulePart.effect &&
          dpbRulePart.effect.property.href ===
          ruleFragment.effect.property.href);
      }
      elt.dataset.ruleFragment = JSON.stringify(ruleFragment);

      if (selected) {
        this.devicePropertyBlock.rulePart = ruleFragment;
        this.devicePropertyBlock.onRuleChange();
      }
    });
  }

  this.updateOption(elt);

  this.elt.appendChild(elt);
};

PropertySelect.prototype.updateOption = function(optionElt) {
  const ruleFragment = JSON.parse(optionElt.dataset.ruleFragment);

  if (!ruleFragment) {
    return;
  }

  const property = getProperty(ruleFragment);
  if (!property) {
    return;
  }

  const fragmentValue = ruleFragment.trigger ?
    ruleFragment.trigger.value :
    ruleFragment.effect.value;

  const valueInput = optionElt.querySelector('.value-input');

  if (property.type === 'number') {
    if (ruleFragment.trigger) {
      if (ruleFragment.trigger.levelType === 'GREATER') {
        optionElt.querySelector('.gt-option').setAttribute('selected', '');
      } else {
        optionElt.querySelector('.lt-option').setAttribute('selected', '');
      }
    }
    valueInput.value = fragmentValue;
  } else if (property.name === 'color' || property.type === 'string') {
    valueInput.value = fragmentValue;
  }
};

/**
 * Updates available options based on the PropertySelector's
 * DevicePropertyBlock's role.
 * @param {'trigger'|'effect'} role
 */
PropertySelect.prototype.updateOptionsForRole = function(role) {
  if (this.role === role) {
    return;
  }
  this.role = role;

  this.clearOptions();

  for (const propName of Object.keys(this.thing.properties)) {
    const property = this.thing.properties[propName];
    if (!property.name) {
      property.name = propName;
    }
    const name = Utils.capitalize(property.name);
    if (role === 'trigger') {
      if (property.type === 'boolean') {
        const triggerOn = {
          type: 'BooleanTrigger',
          property: property,
          onValue: true,
        };
        const triggerOff = Object.assign({}, triggerOn, {
          onValue: false,
        });
        const onName = name;
        let offName = `Not ${onName}`;
        if (property.name === 'on') {
          offName = 'Off';
        }
        this.addOption(onName, {
          trigger: triggerOn,
        });
        this.addOption(offName, {
          trigger: triggerOff,
        });
      } else if (property.type === 'string') {
        let value = '';
        if (property.name === 'color') {
          value = '#ffffff';
        }
        this.addOption(name, {
          trigger: {
            type: 'EqualityTrigger',
            property: property,
            value: value,
          },
        });
      } else if (property.type === 'number') {
        const max = property.maximum || property.max || 0;
        const min = property.minimum || property.min || 0;
        const value = (max + min) / 2;

        this.addOption(name, {
          trigger: {
            type: 'LevelTrigger',
            property: property,
            levelType: 'LESS',
            value: value,
          },
        });
      }
    } else if (role === 'effect') {
      if (property.type === 'boolean') {
        const effectOn = {
          type: 'PulseEffect',
          property: property,
          value: true,
        };
        const effectOff = Object.assign({}, effectOn, {
          value: false,
        });
        const onName = name;
        let offName = `Not ${onName}`;
        if (property.name === 'on') {
          offName = 'Off';
        }
        this.addOption(onName, {
          effect: effectOn,
        });
        this.addOption(offName, {
          effect: effectOff,
        });
      } else if (property.name === 'color') {
        this.addOption(name, {
          effect: {
            type: 'PulseEffect',
            property: property,
            value: '#ffffff',
          },
        });
      } else if (property.type === 'string') {
        this.addOption(name, {
          effect: {
            type: 'PulseEffect',
            property: property,
            value: 'text',
          },
        });
      } else if (property.type === 'number') {
        const max = property.maximum || property.max || 0;
        const min = property.minimum || property.min || 0;
        const value = (max + min) / 2;
        this.addOption(name, {
          effect: {
            type: 'PulseEffect',
            property: property,
            value: value,
          },
        });
      }
    }
  }
  if (role === 'trigger') {
    this.addEventOptions();
  } else if (role === 'effect') {
    this.addActionOptions();
  }
};

PropertySelect.prototype.addEventOptions = function() {
  for (const name of Object.keys(this.thing.events)) {
    const eventTrigger = {
      type: 'EventTrigger',
      thing: {
        href: this.thing.href,
      },
      event: name,
    };
    this.addOption(`Event "${name}"`, {
      trigger: eventTrigger,
    });
  }
};

PropertySelect.prototype.addActionOptions = function() {
  for (const name of Object.keys(this.thing.actions)) {
    const actionEffect = {
      type: 'ActionEffect',
      thing: {
        href: this.thing.href,
      },
      action: name,
      parameters: {},
    };
    this.addOption(`Action "${name}"`, {
      effect: actionEffect,
    });
  }
};

/**
 * Select an option when the user clicks it
 * @param {Event} e
 */
PropertySelect.prototype.onClick = function(e) {
  this.elt.classList.toggle('open');
  if (!this.elt.classList.contains('open')) {
    let target = e.target;
    if (!target.classList.contains('property-select-option')) {
      target = target.parentNode;
    }

    // We were open, so that was a click to select
    this.select(target);

    const rulePart = JSON.parse(target.dataset.ruleFragment);
    if (!rulePart) {
      return;
    }

    this.devicePropertyBlock.rulePart = rulePart;
    this.devicePropertyBlock.elt.classList.remove('open');
    this.devicePropertyBlock.onRuleChange();
  } else {
    this.devicePropertyBlock.elt.classList.add('open');
  }
};

function propertyEqual(a, b) {
  if ((!a) && (!b)) {
    return true;
  }

  return a && b &&
    a.type === b.type &&
    a.name === b.name &&
    a.href === b.href;
}

function getProperty(ruleFragment) {
  return ruleFragment.trigger ?
    ruleFragment.trigger.property :
    ruleFragment.effect.property;
}

function ruleFragmentEqual(a, b) {
  if ((!!a) !== (!!b)) {
    return false;
  }
  if (a.trigger && !b.trigger) {
    return false;
  }
  if (a.effect && !b.effect) {
    return false;
  }

  const aPart = a.trigger || a.effect;
  const bPart = b.trigger || b.effect;

  const aProperty = getProperty(a);
  const bProperty = getProperty(b);

  if (!propertyEqual(aProperty, bProperty)) {
    return false;
  }

  if (aPart.type.match(/(Set|Pulse)Effect/)) {
    if (!bPart.type.match(/(Set|Pulse)Effect/)) {
      return false;
    }
  } else if (aPart.type !== bPart.type) {
    return false;
  }

  if (aProperty && aProperty.type === 'boolean') {
    return aPart.onValue === bPart.onValue;
  }
  if (aPart.type === 'EventTrigger') {
    return aPart.event === bPart.event;
  }
  if (aPart.type === 'ActionEffect') {
    return aPart.action === bPart.action;
  }
  return true;
}

/**
 * Select an option by value
 * @param {Object} ruleFragment
 */
PropertySelect.prototype.selectByRuleFragment = function(ruleFragment) {
  const elements = this.elt.querySelectorAll('.property-select-option');
  for (const optionElt of elements) {
    const optionRuleFragment = JSON.parse(optionElt.dataset.ruleFragment);
    if (ruleFragmentEqual(optionRuleFragment, ruleFragment)) {
      optionElt.dataset.ruleFragment = JSON.stringify(ruleFragment);
      this.updateOption(optionElt);
      this.select(optionElt);
      return;
    }
  }
};

/**
 * Select a specific option
 * @param {Element} option - option's corresponding element
 */
PropertySelect.prototype.select = function(optionElt) {
  const selected = this.elt.querySelector('.selected');
  if (selected) {
    if (!JSON.parse(selected.dataset.ruleFragment)) {
      if (selected === optionElt) {
        return;
      }
      this.elt.removeChild(selected);
    } else {
      selected.classList.remove('selected');
    }
  }
  optionElt.classList.add('selected');
};

module.exports = PropertySelect;
<commit_msg>Skip actions with input<commit_after>const Utils = require('../utils');

/**
 * A hand-coded <select>-like element which allows the selection of one of a
 * thing's properties. In a perfect world this would be a styled select, but
 * this is not a perfect world.
 * @constructor
 * @param {DevicePropertyBlock} devicePropertyBlock
 * @param {Element} parent
 * @param {ThingDescription} thing
 */
function PropertySelect(devicePropertyBlock, parent, thing) {
  this.devicePropertyBlock = devicePropertyBlock;
  this.thing = thing;
  this.role = '';

  this.elt = document.createElement('div');
  this.elt.classList.add('property-select');

  this.onClick = this.onClick.bind(this);
  this.elt.addEventListener('click', this.onClick);

  this.clearOptions();

  // Disable dragging started by clicking property select
  this.elt.addEventListener('mousedown', (e) => {
    e.stopPropagation();
  });
  this.elt.addEventListener('touchstart', (e) => {
    e.stopPropagation();
  });

  parent.appendChild(this.elt);
}

/**
 * Reset the options list to only include the placeholder Select Property
 */
PropertySelect.prototype.clearOptions = function() {
  this.elt.innerHTML = '';
  this.addOption('Select Property', null, true);
};

/**
 * Add an option to the options list
 * @param {String} name - text content of option
 * @param {Object} ruleFragment - associated rule fragment with either trigger
 * or property defined
 * @param {boolean} selected - if the option is selected
 */
PropertySelect.prototype.addOption = function(name, ruleFragment, selected) {
  const elt = document.createElement('div');
  elt.classList.add('property-select-option');
  if (selected) {
    elt.classList.add('selected');
  }
  elt.dataset.ruleFragment = JSON.stringify(ruleFragment);
  const nameElt = document.createElement('span');
  nameElt.classList.add('property-select-name');
  nameElt.textContent = name;
  nameElt.title = name;
  elt.appendChild(nameElt);

  if (!ruleFragment) {
    this.elt.appendChild(elt);
    return;
  }

  const property = getProperty(ruleFragment);
  if (!property) {
    this.updateOption(elt);
    this.elt.appendChild(elt);
    return;
  }

  const stopPropagation = (e) => {
    e.stopPropagation();
  };

  if (property.type === 'number') {
    let ltOption, gtOption;
    if (ruleFragment.trigger) {
      ltOption = document.createElement('option');
      ltOption.textContent = '<';
      ltOption.classList.add('lt-option');
      gtOption = document.createElement('option');
      gtOption.textContent = '>';
      gtOption.classList.add('gt-option');

      const select = document.createElement('select');
      select.appendChild(ltOption);
      select.appendChild(gtOption);
      select.addEventListener('click', stopPropagation);
      elt.appendChild(select);
    }

    const valueInput = document.createElement('input');
    valueInput.classList.add('value-input');
    valueInput.type = 'number';
    valueInput.addEventListener('click', stopPropagation);
    elt.appendChild(valueInput);

    elt.addEventListener('change', () => {
      if (ruleFragment.trigger) {
        if (ltOption.selected) {
          ruleFragment.trigger.levelType = 'LESS';
        } else if (gtOption.selected) {
          ruleFragment.trigger.levelType = 'GREATER';
        }
        ruleFragment.trigger.value = parseFloat(valueInput.value);
      } else {
        ruleFragment.effect.value = parseFloat(valueInput.value);
      }
      this.devicePropertyBlock.rulePart = ruleFragment;
      this.devicePropertyBlock.onRuleChange();
      elt.dataset.ruleFragment = JSON.stringify(ruleFragment);
    });
  } else if (property.type === 'string') {
    const valueInput = document.createElement('input');
    valueInput.classList.add('value-input');
    if (property.name === 'color') {
      valueInput.type = 'color';
    } else {
      valueInput.type = 'text';
    }
    valueInput.addEventListener('click', stopPropagation);
    elt.appendChild(valueInput);

    elt.addEventListener('change', () => {
      const dpbRulePart = this.devicePropertyBlock.rulePart;
      let selected = !dpbRulePart;
      if (ruleFragment.trigger) {
        ruleFragment.trigger.value = valueInput.value;
        selected = selected || (dpbRulePart.trigger &&
          dpbRulePart.trigger.type === ruleFragment.trigger.type);
      } else {
        ruleFragment.effect.value = valueInput.value;
        selected = selected || (dpbRulePart.effect &&
          dpbRulePart.effect.property.href ===
          ruleFragment.effect.property.href);
      }
      elt.dataset.ruleFragment = JSON.stringify(ruleFragment);

      if (selected) {
        this.devicePropertyBlock.rulePart = ruleFragment;
        this.devicePropertyBlock.onRuleChange();
      }
    });
  }

  this.updateOption(elt);

  this.elt.appendChild(elt);
};

PropertySelect.prototype.updateOption = function(optionElt) {
  const ruleFragment = JSON.parse(optionElt.dataset.ruleFragment);

  if (!ruleFragment) {
    return;
  }

  const property = getProperty(ruleFragment);
  if (!property) {
    return;
  }

  const fragmentValue = ruleFragment.trigger ?
    ruleFragment.trigger.value :
    ruleFragment.effect.value;

  const valueInput = optionElt.querySelector('.value-input');

  if (property.type === 'number') {
    if (ruleFragment.trigger) {
      if (ruleFragment.trigger.levelType === 'GREATER') {
        optionElt.querySelector('.gt-option').setAttribute('selected', '');
      } else {
        optionElt.querySelector('.lt-option').setAttribute('selected', '');
      }
    }
    valueInput.value = fragmentValue;
  } else if (property.name === 'color' || property.type === 'string') {
    valueInput.value = fragmentValue;
  }
};

/**
 * Updates available options based on the PropertySelector's
 * DevicePropertyBlock's role.
 * @param {'trigger'|'effect'} role
 */
PropertySelect.prototype.updateOptionsForRole = function(role) {
  if (this.role === role) {
    return;
  }
  this.role = role;

  this.clearOptions();

  for (const propName of Object.keys(this.thing.properties)) {
    const property = this.thing.properties[propName];
    if (!property.name) {
      property.name = propName;
    }
    const name = Utils.capitalize(property.name);
    if (role === 'trigger') {
      if (property.type === 'boolean') {
        const triggerOn = {
          type: 'BooleanTrigger',
          property: property,
          onValue: true,
        };
        const triggerOff = Object.assign({}, triggerOn, {
          onValue: false,
        });
        const onName = name;
        let offName = `Not ${onName}`;
        if (property.name === 'on') {
          offName = 'Off';
        }
        this.addOption(onName, {
          trigger: triggerOn,
        });
        this.addOption(offName, {
          trigger: triggerOff,
        });
      } else if (property.type === 'string') {
        let value = '';
        if (property.name === 'color') {
          value = '#ffffff';
        }
        this.addOption(name, {
          trigger: {
            type: 'EqualityTrigger',
            property: property,
            value: value,
          },
        });
      } else if (property.type === 'number') {
        const max = property.maximum || property.max || 0;
        const min = property.minimum || property.min || 0;
        const value = (max + min) / 2;

        this.addOption(name, {
          trigger: {
            type: 'LevelTrigger',
            property: property,
            levelType: 'LESS',
            value: value,
          },
        });
      }
    } else if (role === 'effect') {
      if (property.type === 'boolean') {
        const effectOn = {
          type: 'PulseEffect',
          property: property,
          value: true,
        };
        const effectOff = Object.assign({}, effectOn, {
          value: false,
        });
        const onName = name;
        let offName = `Not ${onName}`;
        if (property.name === 'on') {
          offName = 'Off';
        }
        this.addOption(onName, {
          effect: effectOn,
        });
        this.addOption(offName, {
          effect: effectOff,
        });
      } else if (property.name === 'color') {
        this.addOption(name, {
          effect: {
            type: 'PulseEffect',
            property: property,
            value: '#ffffff',
          },
        });
      } else if (property.type === 'string') {
        this.addOption(name, {
          effect: {
            type: 'PulseEffect',
            property: property,
            value: 'text',
          },
        });
      } else if (property.type === 'number') {
        const max = property.maximum || property.max || 0;
        const min = property.minimum || property.min || 0;
        const value = (max + min) / 2;
        this.addOption(name, {
          effect: {
            type: 'PulseEffect',
            property: property,
            value: value,
          },
        });
      }
    }
  }
  if (role === 'trigger') {
    this.addEventOptions();
  } else if (role === 'effect') {
    this.addActionOptions();
  }
};

PropertySelect.prototype.addEventOptions = function() {
  for (const name of Object.keys(this.thing.events)) {
    const eventTrigger = {
      type: 'EventTrigger',
      thing: {
        href: this.thing.href,
      },
      event: name,
    };
    this.addOption(`Event "${name}"`, {
      trigger: eventTrigger,
    });
  }
};

PropertySelect.prototype.addActionOptions = function() {
  for (const name of Object.keys(this.thing.actions)) {
    if (this.thing.actions[name].input) {
      continue;
    }
    const actionEffect = {
      type: 'ActionEffect',
      thing: {
        href: this.thing.href,
      },
      action: name,
      parameters: {},
    };
    this.addOption(`Action "${name}"`, {
      effect: actionEffect,
    });
  }
};

/**
 * Select an option when the user clicks it
 * @param {Event} e
 */
PropertySelect.prototype.onClick = function(e) {
  this.elt.classList.toggle('open');
  if (!this.elt.classList.contains('open')) {
    let target = e.target;
    if (!target.classList.contains('property-select-option')) {
      target = target.parentNode;
    }

    // We were open, so that was a click to select
    this.select(target);

    const rulePart = JSON.parse(target.dataset.ruleFragment);
    if (!rulePart) {
      return;
    }

    this.devicePropertyBlock.rulePart = rulePart;
    this.devicePropertyBlock.elt.classList.remove('open');
    this.devicePropertyBlock.onRuleChange();
  } else {
    this.devicePropertyBlock.elt.classList.add('open');
  }
};

function propertyEqual(a, b) {
  if ((!a) && (!b)) {
    return true;
  }

  return a && b &&
    a.type === b.type &&
    a.name === b.name &&
    a.href === b.href;
}

function getProperty(ruleFragment) {
  return ruleFragment.trigger ?
    ruleFragment.trigger.property :
    ruleFragment.effect.property;
}

function ruleFragmentEqual(a, b) {
  if ((!!a) !== (!!b)) {
    return false;
  }
  if (a.trigger && !b.trigger) {
    return false;
  }
  if (a.effect && !b.effect) {
    return false;
  }

  const aPart = a.trigger || a.effect;
  const bPart = b.trigger || b.effect;

  const aProperty = getProperty(a);
  const bProperty = getProperty(b);

  if (!propertyEqual(aProperty, bProperty)) {
    return false;
  }

  if (aPart.type.match(/(Set|Pulse)Effect/)) {
    if (!bPart.type.match(/(Set|Pulse)Effect/)) {
      return false;
    }
  } else if (aPart.type !== bPart.type) {
    return false;
  }

  if (aProperty && aProperty.type === 'boolean') {
    return aPart.onValue === bPart.onValue;
  }
  if (aPart.type === 'EventTrigger') {
    return aPart.event === bPart.event;
  }
  if (aPart.type === 'ActionEffect') {
    return aPart.action === bPart.action;
  }
  return true;
}

/**
 * Select an option by value
 * @param {Object} ruleFragment
 */
PropertySelect.prototype.selectByRuleFragment = function(ruleFragment) {
  const elements = this.elt.querySelectorAll('.property-select-option');
  for (const optionElt of elements) {
    const optionRuleFragment = JSON.parse(optionElt.dataset.ruleFragment);
    if (ruleFragmentEqual(optionRuleFragment, ruleFragment)) {
      optionElt.dataset.ruleFragment = JSON.stringify(ruleFragment);
      this.updateOption(optionElt);
      this.select(optionElt);
      return;
    }
  }
};

/**
 * Select a specific option
 * @param {Element} option - option's corresponding element
 */
PropertySelect.prototype.select = function(optionElt) {
  const selected = this.elt.querySelector('.selected');
  if (selected) {
    if (!JSON.parse(selected.dataset.ruleFragment)) {
      if (selected === optionElt) {
        return;
      }
      this.elt.removeChild(selected);
    } else {
      selected.classList.remove('selected');
    }
  }
  optionElt.classList.add('selected');
};

module.exports = PropertySelect;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
const httpError = require('http-errors');
const permissions = require('../controllers/permissions.js');
var elasticsearch = require('../controllers/elasticsearch');

const models = {
  task: require('../models/task'),
  discussion: require('../models/discussion'),
  project: require('../models/project'),
  office: require('../models/office'),
  folder: require('../models/folder'),
  document: require('../models/document'),
  templateDoc: require('../models/templateDoc')
};

module.exports = {
  update,
  recycle,
  remove
}

function update(req, res, next) {
  let entity = req.params.entity;
  let Model = models[entity];
  let ids = req.body.ids;
  let update = req.body.update;

  let {
    watchers,
    tags
  } = update;

  let changeWholeParameter = {
      status: update.status,
      due: update.due,
      assign: update.assign,
  };

  watchers = watchers || [];
  tags = tags || [];

  let set = clean(Object.assign({}, changeWholeParameter));

  let addToEach = {};
  if(watchers.length) addToEach.watchers = { $each: watchers };
  if(tags.length) addToEach.tags = { $each: tags };

  let addedAttributes = {};
  if(!_.isEmpty(set)) addedAttributes.$set = set;
  if(!_.isEmpty(addToEach)) addedAttributes.$addToSet = addToEach;

  Model.find({ _id: { $in: ids } })
  .then(docs => docs.length === 0 ? next() : docs )
  .then(docs => {
    if (checkBoldedPermissions(docs,req.user)) return docs ;
    else throw new Error("Permission Denied") ;
  })
  .then(function(docs) {
    if(!docs.length) throw new httpError(404);

    return Model.update({
        _id: { $in: ids }
    },
        addedAttributes,
    {
        multi: true
    })
  })
  .then(function (results) {
      return Model.find({_id: {$in: ids}})
  })
  .then(docs => {
    if(!docs.length) throw new httpError(404);
    docs.forEach(element => {
        console.log("saving elastic");
        element.watchers = [];
        elasticsearch.save(element, entity);
    })
    return docs;
  })
  .then(function(docs) {
    res.json(docs)
  })
  .catch(function(err) {
    next(err)
  })
}

function recycle(req, res, next) {
  let entity = req.params.entity;
  let Model = models[entity];
  let ids = req.body.ids;

  Model.find({ _id: { $in: ids } })
  .then(function(docs) {
    if(!docs.length) throw new httpError(404);
    return Model.update({
      _id: { $in: ids }
    }, {
      $set: {
        recycled: new Date
      }
    }, {
      multi: true
    })
  })
  .then(function (results) {
    return Model.find({_id: {$in: ids}})
  })
  .then(docs => {
    if(!docs.length) throw new httpError(404);
    docs.forEach(element => {
        console.log("saving elastic");
        element.watchers = [];
        elasticsearch.save(element, entity);
    })
    return docs;
  })
  .then(updatedItems=>{
      res.json(updatedItems)
  })
  .catch(function(err) {
      next(err)
  })
}

function clean(obj) {
    for (let propName in obj) {
        if (obj[propName] === null || obj[propName] === undefined) {
            delete obj[propName];
        }
    }
    return obj;
}

function remove(req, res, next) {
  //usused - remove this maybe
  let entity = req.params.entity;
  let Model = models[entity];
  let ids = req.body.ids;

  Model.find({ _id: { $in: ids } })
  .then(function(docs) {
    if(!docs.length) throw new httpError(404);
    return Model.remove({ _id: { $in: ids } })
  })
  .then(function(results) {
    res.end()
  })
  .catch(function(err) {
    next(err)
  })
}

function checkBoldedPermissions(docs,user) {
  let permissionDenied = docs.some((doc) => {
    let allowed = permissions.updateContent({user: user},doc, []) ;
    if(!allowed) {
      console.log("checkBoldedPermissions NOT ALLLOWED") ;
      return !allowed ;
    }
  }) ;

  if (permissionDenied) {
    return false ;
  }

  return true ;
}
<commit_msg>Revert "refactoring"<commit_after>'use strict';

var _ = require('lodash');
const httpError = require('http-errors');
const permissions = require('../controllers/permissions.js');
var elasticsearch = require('../controllers/elasticsearch');

const models = {
  task: require('../models/task'),
  discussion: require('../models/discussion'),
  project: require('../models/project'),
  office: require('../models/office'),
  folder: require('../models/folder'),
  document: require('../models/document'),
  templateDoc: require('../models/templateDoc')
};

module.exports = {
  update,
  recycle,
  remove
}

function update(req, res, next) {
  let entity = req.params.entity;
  let Model = models[entity];
  let ids = req.body.ids;
  let update = req.body.update;

  let {
    watchers,
    tag
  } = update;

  let changeWholeParameter = {
      status: update.status,
      due: update.due,
      assign: update.assign,
  };

  watchers = watchers || [];
  tags = tags || [];

  let set = clean(Object.assign({}, changeWholeParameter));

  let addToEach = {};
  if(watchers.length) addToEach.watchers = { $each: watchers };
  if(tags.length) addToEach.tags = { $each: tags };

  let addedAttributes = {};
  if(!_.isEmpty(set)) addedAttributes.$set = set;
  if(!_.isEmpty(addToEach)) addedAttributes.$addToSet = addToEach;

  Model.find({ _id: { $in: ids } })
  .then(docs => docs.length === 0 ? next() : docs )
  .then(docs => {
    if (checkBoldedPermissions(docs,req.user)) return docs ;
    else throw new Error("Permission Denied") ;
  })
  .then(function(docs) {
    if(!docs.length) throw new httpError(404);

    return Model.update({
        _id: { $in: ids }
    },
        addedAttributes,
    {
        multi: true
    })
  })
  .then(function (results) {
      return Model.find({_id: {$in: ids}})
  })
  .then(docs => {
    if(!docs.length) throw new httpError(404);
    docs.forEach(element => {
        console.log("saving elastic");
        element.watchers = [];
        elasticsearch.save(element, entity);
    })
    return docs;
  })
  .then(function(docs) {
    res.json(docs)
  })
  .catch(function(err) {
    next(err)
  })
}

function recycle(req, res, next) {
  let entity = req.params.entity;
  let Model = models[entity];
  let ids = req.body.ids;

  Model.find({ _id: { $in: ids } })
  .then(function(docs) {
    if(!docs.length) throw new httpError(404);
    return Model.update({
      _id: { $in: ids }
    }, {
      $set: {
        recycled: new Date
      }
    }, {
      multi: true
    })
  })
  .then(function (results) {
    return Model.find({_id: {$in: ids}})
  })
  .then(docs => {
    if(!docs.length) throw new httpError(404);
    docs.forEach(element => {
        console.log("saving elastic");
        element.watchers = [];
        elasticsearch.save(element, entity);
    })
    return docs;
  })
  .then(updatedItems=>{
      res.json(updatedItems)
  })
  .catch(function(err) {
      next(err)
  })
}

function clean(obj) {
    for (let propName in obj) {
        if (obj[propName] === null || obj[propName] === undefined) {
            delete obj[propName];
        }
    }
    return obj;
}

function remove(req, res, next) {
  //usused - remove this maybe
  let entity = req.params.entity;
  let Model = models[entity];
  let ids = req.body.ids;

  Model.find({ _id: { $in: ids } })
  .then(function(docs) {
    if(!docs.length) throw new httpError(404);
    return Model.remove({ _id: { $in: ids } })
  })
  .then(function(results) {
    res.end()
  })
  .catch(function(err) {
    next(err)
  })
}

function checkBoldedPermissions(docs,user) {
  let permissionDenied = docs.some((doc) => {
    let allowed = permissions.updateContent({user: user},doc, []) ;
    if(!allowed) {
      console.log("checkBoldedPermissions NOT ALLLOWED") ;
      return !allowed ;
    }
  }) ;

  if (permissionDenied) {
    return false ;
  }

  return true ;
}
<|endoftext|>


<commit_before>var bower = require("bower");

var Path = require("path")
var fs = require("fs")
var Q = require("q");
Q.longStackSupport = true;

_3setup = (function() {

  function _3setup() {}

  _3setup.setup = function(options){

    _3setup.scaffold(options)
    .then( _3setup.installNPM )
    .fail( function(err){ console.error(err); } );

  }

  _3setup.scaffold= function (options){

    var deferred = Q.defer();

    fs.mkdir( Path.join( process.cwd(), options.folder ), function(){} );
    fs.mkdir( Path.join( process.cwd(), options.folder , "apps" ), function(){} );
    fs.mkdir( Path.join( process.cwd(), options.folder , "apps", "dependencies" ), function(){} );
    fs.mkdir( Path.join( process.cwd(), options.folder , "tmp" ), function(){}) ;

    var templatesPath =  Path.join(Path.dirname(fs.realpathSync(__filename)), './templates');

    var _3votJSON = require( Path.join(  templatesPath, "_3vot.json" ));
    var gitIgnore = fs.readFileSync(  Path.join( templatesPath, "_.gitignore" ), "utf-8");
    var pckJSON = require( Path.join( templatesPath, "package.json" ));

    _3votJSON.key = options.key;
    _3votJSON.profile = options.profile;

    fs.writeFileSync( Path.join(process.cwd(), options.folder, "3vot.json"), JSON.stringify(_3votJSON, null, '\t') );
    fs.writeFileSync( Path.join(process.cwd(), options.folder, "package.json"), JSON.stringify(pckJSON, null, '\t') );
    fs.writeFile( Path.join(process.cwd(), options.folder, ".gitignore"), gitIgnore, function(){ deferred.resolve()  } );

    return deferred.promise

  }

  _3setup.installNPM= function(){
     var deferred = Q.defer();

     var npm = require("npm");

      npm.load(npm.config, function (er) {
        if (er) return handlError(er);
        npm.commands.install(["."], function (er, data) {
          if (er) return deferred.reject(er)
          return deferred.resolve()
          // command succeeded, and data might have some info
        });
        npm.on("log", function (message) { })
      });

      return deferred.promise;

  }

  return _3setup;

})();

module.exports = _3setup;<commit_msg>version bump<commit_after>var bower = require("bower");

var Path = require("path")
var fs = require("fs")
var Q = require("q");
Q.longStackSupport = true;

_3setup = (function() {

  function _3setup() {}

  _3setup.setup = function(options){

    _3setup.scaffold(options)
    .then( _3setup.installNPM )
    .fail( function(err){ console.error(err); } );

  }

  _3setup.scaffold= function (options){

    var deferred = Q.defer();

    fs.mkdir( Path.join( process.cwd(), options.folder ), function(){} );
    fs.mkdir( Path.join( process.cwd(), options.folder , "apps" ), function(){} );
    fs.mkdir( Path.join( process.cwd(), options.folder , "apps", "dependencies" ), function(){} );
    fs.mkdir( Path.join( process.cwd(), options.folder , "tmp" ), function(){}) ;

    var templatesPath =  Path.join(Path.dirname(fs.realpathSync(__filename)), './templates');

    var _3votJSON = require( Path.join(  templatesPath, "_3vot.json" ));
    var gitIgnore = fs.readFileSync(  Path.join( templatesPath, "_.gitignore" ), "utf-8");
    var pckJSON = require( Path.join( templatesPath, "package.json" ));

    _3votJSON.key = options.key;
    _3votJSON.profile = options.profile;

    fs.writeFileSync( Path.join(process.cwd(), options.folder, "3vot.json"), JSON.stringify(_3votJSON, null, '\t') );
    fs.writeFileSync( Path.join(process.cwd(), options.folder, "package.json"), JSON.stringify(pckJSON, null, '\t') );
    fs.writeFile( Path.join(process.cwd(), options.folder, ".gitignore"), gitIgnore, function(){ deferred.resolve( options )  } );

    return deferred.promise

  }

  _3setup.installNPM= function(options){
     var deferred = Q.defer();

     process.chdir( process.cwd() + options.folder);

     var npm = require("npm");

      npm.load(npm.config, function (er) {
        if (er) return handlError(er);
        npm.commands.install(["."], function (er, data) {
          if (er) return deferred.reject(er)
          return deferred.resolve()
          // command succeeded, and data might have some info
        });
        npm.on("log", function (message) { })
      });

      return deferred.promise;

  }

  return _3setup;

})();

module.exports = _3setup;<|endoftext|>


<commit_before>import {compose} from 'compose'
import PropTypes from 'prop-types'
import React from 'react'
import withForwardedRef from 'ref'

class AutoFocus extends React.Component {
    focusHolderRef = React.createRef()
    state = {completed: false}

    render() {
        const {completed} = this.state
        return completed ? null :

    }

    shouldComponentUpdate(nextProps, nextState, nextContext) {
        const {enabled} = nextProps
        return !!enabled
    }

    componentDidMount() {
        const {enabled} = this.props
        if (enabled) {
            const focusHolderElement = this.focusHolderRef.current
            focusHolderElement && focusHolderElement.focus()
            this.update()
        }
    }

    componentDidUpdate() {
        this.update()
    }

    update() {
        const {forwardedRef, enabled} = this.props
        const focusHolderElement = this.focusHolderRef.current
        const element = forwardedRef.current
        if (!enabled || !element || !focusHolderElement) {
            return
        }

        if (document.activeElement !== focusHolderElement) {
            this.setState({completed: true})
            return
        }

        element.focus()
        if (document.activeElement === element) {
            this.setState({completed: true})
        }
    }
}

export default compose(
    AutoFocus,
    withForwardedRef()
)

AutoFocus.propTypes = {
    enabled: PropTypes.any,
    forwardedRef: PropTypes.object
}

<commit_msg>Put AutoFocus focus holder in a portal.<commit_after>import {compose} from 'compose'
import PropTypes from 'prop-types'
import React from 'react'
import withForwardedRef from 'ref'
import Portal from 'widget/portal'

class AutoFocus extends React.Component {
    focusHolderRef = React.createRef()
    state = {completed: false}

    render() {
        const {completed} = this.state
        return completed
            ? null
            : <Portal type='global'>

            </Portal>
    }

    shouldComponentUpdate(nextProps, nextState, nextContext) {
        const {enabled} = nextProps
        return !!enabled
    }

    componentDidMount() {
        const {enabled} = this.props
        if (enabled) {
            const focusHolderElement = this.focusHolderRef.current
            focusHolderElement && focusHolderElement.focus()
            this.update()
        }
    }

    componentDidUpdate() {
        this.update()
    }

    update() {
        const {forwardedRef, enabled} = this.props
        const focusHolderElement = this.focusHolderRef.current
        const element = forwardedRef.current
        if (!enabled || !element || !focusHolderElement) {
            return
        }

        if (document.activeElement !== focusHolderElement) {
            this.setState({completed: true})
            return
        }

        element.focus()
        if (document.activeElement === element) {
            this.setState({completed: true})
        }
    }
}

export default compose(
    AutoFocus,
    withForwardedRef()
)

AutoFocus.propTypes = {
    enabled: PropTypes.any,
    forwardedRef: PropTypes.object
}

<|endoftext|>


<commit_before>import React, {Component} from 'react'
import ApiTabs from '../Components/ApiTabs'
import NavHeader from '../Components/NavHeader'
import {
  SIDE_BAR_WIDTH,
  YAML_CONTENT_TYPE,
  RESPONSE_FORMATS
} from '../Constant'
import {
  postApiRequest,
  objectToYaml,
  saveBlobToFile,
  saveYamlOnServer,
  getEmptyApiDefinition,
  exportFile,
  getDefaultApiDefinition,
  parseObjectAsYamlConfig,
  getEmptyTestData,
  setDataInLocalStorage,
  getDataFromLocalStorage,
  googleChartLoader
} from '../Utils'

//API URL host name. For testing only. later user will define this in setup configuration.
export const apiUriHostName = 'http://localhost:8000'

export class MainContainer extends Component {
  constructor(props) {
    super(props)
    this.state = {
      apiDefinition: [],
      testData: [],
      selectedApiIndex: 0,
      openAPIs: []
    }
  }

  initializeStates = () => {
    if (!this.state.apiDefinition.length) {
      let tempApiDef = [getEmptyApiDefinition()]
      this.setState({apiDefinition: tempApiDef})
    }

    if (!this.state.testData.length) {
      let tempTestData = [getEmptyTestData()]
      this.setState({testData: tempTestData})
    }

    if (!this.state.openAPIs.length) {
      this.setState({openAPIs: [0]})
    }
    this.setState({selectedApiIndex: this.state.selectedApiIndex || 0})
  }

  componentWillMount() {
    let localStorageData = getDataFromLocalStorage('hidash')
    if (localStorageData) {
      Object.keys(localStorageData).map((key) => {
      this.setState({[key]: localStorageData[key]}, () => {
          this.initializeStates()
        })
      })
    } else {
      this.initializeStates()
    }
    googleChartLoader()
  }

  onChangeApiDefinition = (variableName, value) => {
    let tempAPIDef = this.state.apiDefinition.slice()
    tempAPIDef[this.state.selectedApiIndex][variableName] = value
    this.setState({apiDefinition: tempAPIDef})
  }

  apiTabRenameHandler = (value, index) => {
    let apiDefinition = this.state.apiDefinition.slice()
    apiDefinition[index]['apiName'] = value
    if (apiDefinition[index]['urlName'] === '') {
      apiDefinition[index]['urlName'] = value.replace(/\s+/g, '-').toLowerCase()
    }
    this.setState({apiDefinition: apiDefinition})
  }

  onChangeTestData = (value) => {
    let tempTestData = this.state.testData.slice()
    tempTestData[this.state.selectedApiIndex].apiParams = value
    this.setState({testData: tempTestData})
  }

  apiCloseHandler = (index) => {
    let apiDefinition = this.state.apiDefinition.slice(),
      newOpenAPIs = this.state.openAPIs.slice()
    apiDefinition[index].open = false
    let openAPIsIndex = newOpenAPIs.indexOf(index)
    newOpenAPIs.splice(openAPIsIndex, 1)
    let selectedApiIndex

    if (newOpenAPIs.length) {
      selectedApiIndex = openAPIsIndex ? newOpenAPIs[openAPIsIndex - 1] : 0
    } else {
      selectedApiIndex = 0
    }

    this.setState({apiDefinition: apiDefinition, selectedApiIndex: selectedApiIndex, openAPIs: newOpenAPIs})
  }

  apiOpenHandler = (index) => {
    let apiDefinition = this.state.apiDefinition.slice(),
      newOpenAPIs = this.state.openAPIs.slice()
    newOpenAPIs.push(index)
    apiDefinition[index].open = true
    let selectedApiIndex = index
    this.setState({apiDefinition: apiDefinition, selectedApiIndex: selectedApiIndex, openAPIs: newOpenAPIs})
  }

  apiParamToggleHandler = () => {
    let newUiPreferences = Object.assign({}, this.state.uiPreferences)
    newUiPreferences.apiParamsVisible = !this.state.uiPreferences.apiParamsVisible
    this.setState({uiPreferences: newUiPreferences})
  }

  onSuccessTest = (response, format) => {
    let newTestData = this.state.testData.slice()
    let newAPIdef = this.state.apiDefinition.slice()
    let onSuccessTestData =  {
      apiResponse: response,
      apiSuccess: true,
      apiError: false,
      apiParams: newTestData[this.state.selectedApiIndex].apiParams,
      selectedFormat: format
    }
    newTestData[this.state.selectedApiIndex] = Object.assign(newTestData[this.state.selectedApiIndex], onSuccessTestData)
    if(format === RESPONSE_FORMATS.table.value){
      response.columns.map((column) => {
        newAPIdef[this.state.selectedApiIndex].columns[column.name] = {
          type: (column.col_type)?column.col_type:'dimension',
          data_type: (column.data_type)?column.data_type:'string'
        }
      })
    }
    this.setState({testData: newTestData, apiDefinition: newAPIdef},()=>{
      this.onChangeApiDefinition('format', format)
    })
  }

  onErrorTest = (error) => {
    let tempTestData = this.state.testData.slice()
    let onErrorTestData = {
      apiResponse: JSON.parse(error.responseText),
      apiSuccess: false,
      apiError: true,
      apiParams: tempTestData[this.state.selectedApiIndex].apiParams,
      selectedFormat: tempTestData[this.state.selectedApiIndex].selectedFormat
    }
    tempTestData[this.state.selectedApiIndex] = Object.assign(tempTestData[this.state.selectedApiIndex], onErrorTestData)
    this.setState({testData: tempTestData})
  }

  exportConfigAsYaml = () => {
    let apiDefinition =this.state.apiDefinition.slice()
    let fileName = 'hidash-api'
    let yamlData = exportFile(apiDefinition, YAML_CONTENT_TYPE)
    saveBlobToFile(yamlData, fileName+'.yaml')
  }

  processParamDef = (definitions) => {
    let appliedDef = {}
    if (definitions.length) {
      definitions.map((data) => {
        appliedDef[data.name] = {
          type: data.type,
          optional: data.optional,
          default_values: data.default_values,
          isParamDefCustom: data.isParamDefCustom
        }
        if (data.hasOwnProperty('kwargs')) {
          appliedDef[data.name].kwargs = data.kwargs
        }
      })
    }
    return appliedDef
  }

  onHandleTestButton = (event, format=null) => {
    let tempParam = this.state.testData[this.state.selectedApiIndex].apiParams || {}
    let paramObj = {}
    try {
      paramObj = typeof tempParam === 'string' ? JSON.parse(tempParam) : tempParam
    } catch (e) {
      console.log(e)
      console.log('please check your object syntax. Object key and value should be wrapped up in double quotes. Expected input: {"objKey": "objVal"}')
    }
    let paramDef = this.processParamDef(this.state.apiDefinition[this.state.selectedApiIndex].paramDefinition)
    format = format || 'table'
    let payloadObj = {
      config: {
        query: this.state.apiDefinition[this.state.selectedApiIndex].sqlQuery
      },
      transformations: this.state.apiDefinition[this.state.selectedApiIndex].transformations,
      format: format,
      params: paramObj.params,
      parameters: paramDef,
      user: paramObj.session,
      validations: this.state.apiDefinition[this.state.selectedApiIndex].validations,
      columns: this.state.apiDefinition[this.state.selectedApiIndex].columns,
      connection: this.state.selectedDB
    }
    postApiRequest(apiUriHostName+'/test/', payloadObj,
                   this.onSuccessTest, this.onErrorTest, format)
  }

  //Removes the API definition from the curret API list
  apiDeletionHandler = (index) => {
    if(this.state.apiDefinition.length > 1) {
      let newApiDefinitions = this.state.apiDefinition.slice(),
          newTestData = this.state.testData.slice(),
          newOpenAPIs = this.state.openAPIs.slice()
      newApiDefinitions.splice(index, 1)
      newTestData.splice(index, 1)

      let openAPIsIndex = newOpenAPIs.indexOf(index),
          newSelectedApiIndex = -1
      newOpenAPIs.splice(openAPIsIndex, 1)

      if (newOpenAPIs.length) {
        newSelectedApiIndex = openAPIsIndex ? newOpenAPIs[openAPIsIndex - 1] : 0
      } else {
        newSelectedApiIndex = 0
      }

      //Decrement the selectedApiIndex only if the selectedIndex is not zero
      let selectedApiIndex = this.state.selectedApiIndex
      selectedApiIndex = selectedApiIndex===0 ? 0 : newSelectedApiIndex

      let untitledApiIndex = 0
      newApiDefinitions.map((api) => {
        if(api.apiName.includes('Untitled API')) {
          api.apiName = 'Untitled API '+ (untitledApiIndex++)
          api.urlName = api.apiName.replace(/\s+/g, '-').toLowerCase()
        }
      })

      this.setState({
        apiDefinition: newApiDefinitions,
        testData: newTestData,
        selectedApiIndex: selectedApiIndex,
        newOpenAPIs: newOpenAPIs
      })

    } else {
      let tempApiDef = [getEmptyApiDefinition()],
          tempTestData = [getEmptyTestData()],
          tempOpenApis = [0]
      this.setState({apiDefinition: tempApiDef, testData: tempTestData, openAPIs: tempOpenApis})
    }

  }

  //Appends an empty API definition object to current API Definitions
  apiAdditionHandler = () => {
    let newApiDefinitions = this.state.apiDefinition.slice(),
      newTestData = this.state.testData.slice(),
      selectedApiIndex = newApiDefinitions.length,
      newOpenAPIs = this.state.openAPIs.slice()
    newApiDefinitions.push(getDefaultApiDefinition(newApiDefinitions.length))
    newTestData.push(getEmptyTestData())
    newOpenAPIs.push(selectedApiIndex)
    this.setState({
      apiDefinition: newApiDefinitions,
      testData: newTestData,
      selectedApiIndex: selectedApiIndex,
      openAPIs: newOpenAPIs
    })
  }

  //Changes the selected API index to the one which was clicked from the API list
  apiSelectionHandler = (index) => {
    this.setState({selectedApiIndex: index})
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (nextState !== nextProps) {
      //FIXME: Need to change hardcode localstorage key name. Later we will save as project name
      setDataInLocalStorage('hidash', nextState)
      return true
    }
  }

  dbUpdationHandler = (selectedDB) => {
    this.setState({selectedDB: selectedDB.value})
  }

  componentDidUpdate(prevProps, prevState) {
    let apiDefinition =this.state.apiDefinition.slice()
    let yamlData = saveYamlOnServer(apiDefinition)
    let data = {yamlData: yamlData}
    postApiRequest(apiUriHostName+'/yaml-generator/', data, ()=>{},()=>{}, null)
  }

  render () {
    return (
      <div>
      <NavHeader apiDefinition={this.state.apiDefinition} apiOpenHandler={this.apiOpenHandler} apiAdditionHandler={this.apiAdditionHandler} exportConfigAsYaml={this.exportConfigAsYaml}/>
      <ApiTabs
        {...this.state}
        apiCloseHandler={this.apiCloseHandler}
        apiTabRenameHandler={this.apiTabRenameHandler}
        onChangeApiDefinition={this.onChangeApiDefinition}
        onHandleTestButton={this.onHandleTestButton}
        apiDeletionHandler={this.apiDeletionHandler}
        apiAdditionHandler={this.apiAdditionHandler}
        apiSelectionHandler={this.apiSelectionHandler}
        apiParamToggleHandler={this.apiParamToggleHandler}
        onChangeTestData={this.onChangeTestData}
        dbUpdationHandler={this.dbUpdationHandler}
      />
      </div>
    )
  }
}
<commit_msg>HIR-322: Fixed last API delet issue<commit_after>import React, {Component} from 'react'
import ApiTabs from '../Components/ApiTabs'
import NavHeader from '../Components/NavHeader'
import {
  SIDE_BAR_WIDTH,
  YAML_CONTENT_TYPE,
  RESPONSE_FORMATS
} from '../Constant'
import {
  postApiRequest,
  objectToYaml,
  saveBlobToFile,
  saveYamlOnServer,
  getEmptyApiDefinition,
  exportFile,
  getDefaultApiDefinition,
  parseObjectAsYamlConfig,
  getEmptyTestData,
  setDataInLocalStorage,
  getDataFromLocalStorage,
  googleChartLoader
} from '../Utils'

//API URL host name. For testing only. later user will define this in setup configuration.
export const apiUriHostName = 'http://localhost:8000'

export class MainContainer extends Component {
  constructor(props) {
    super(props)
    this.state = {
      apiDefinition: [],
      testData: [],
      selectedApiIndex: 0,
      openAPIs: []
    }
  }

  initializeStates = () => {
    if (!this.state.apiDefinition.length) {
      let tempApiDef = [getEmptyApiDefinition()]
      this.setState({apiDefinition: tempApiDef})
    }

    if (!this.state.testData.length) {
      let tempTestData = [getEmptyTestData()]
      this.setState({testData: tempTestData})
    }

    if (!this.state.openAPIs.length) {
      this.setState({openAPIs: [0]})
    }
    this.setState({selectedApiIndex: this.state.selectedApiIndex || 0})
  }

  componentWillMount() {
    let localStorageData = getDataFromLocalStorage('hidash')
    if (localStorageData) {
      Object.keys(localStorageData).map((key) => {
      this.setState({[key]: localStorageData[key]}, () => {
          this.initializeStates()
        })
      })
    } else {
      this.initializeStates()
    }
    googleChartLoader()
  }

  onChangeApiDefinition = (variableName, value) => {
    let tempAPIDef = this.state.apiDefinition.slice()
    tempAPIDef[this.state.selectedApiIndex][variableName] = value
    this.setState({apiDefinition: tempAPIDef})
  }

  apiTabRenameHandler = (value, index) => {
    let apiDefinition = this.state.apiDefinition.slice()
    apiDefinition[index]['apiName'] = value
    if (apiDefinition[index]['urlName'] === '') {
      apiDefinition[index]['urlName'] = value.replace(/\s+/g, '-').toLowerCase()
    }
    this.setState({apiDefinition: apiDefinition})
  }

  onChangeTestData = (value) => {
    let tempTestData = this.state.testData.slice()
    tempTestData[this.state.selectedApiIndex].apiParams = value
    this.setState({testData: tempTestData})
  }

  apiCloseHandler = (index) => {
    let apiDefinition = this.state.apiDefinition.slice(),
      newOpenAPIs = this.state.openAPIs.slice()
    apiDefinition[index].open = false
    let openAPIsIndex = newOpenAPIs.indexOf(index)
    newOpenAPIs.splice(openAPIsIndex, 1)
    let selectedApiIndex

    if (newOpenAPIs.length) {
      selectedApiIndex = openAPIsIndex ? newOpenAPIs[openAPIsIndex - 1] : 0
    } else {
      selectedApiIndex = 0
    }

    this.setState({apiDefinition: apiDefinition, selectedApiIndex: selectedApiIndex, openAPIs: newOpenAPIs})
  }

  apiOpenHandler = (index) => {
    let apiDefinition = this.state.apiDefinition.slice(),
      newOpenAPIs = this.state.openAPIs.slice()
    newOpenAPIs.push(index)
    apiDefinition[index].open = true
    let selectedApiIndex = index
    this.setState({apiDefinition: apiDefinition, selectedApiIndex: selectedApiIndex, openAPIs: newOpenAPIs})
  }

  apiParamToggleHandler = () => {
    let newUiPreferences = Object.assign({}, this.state.uiPreferences)
    newUiPreferences.apiParamsVisible = !this.state.uiPreferences.apiParamsVisible
    this.setState({uiPreferences: newUiPreferences})
  }

  onSuccessTest = (response, format) => {
    let newTestData = this.state.testData.slice()
    let newAPIdef = this.state.apiDefinition.slice()
    let onSuccessTestData =  {
      apiResponse: response,
      apiSuccess: true,
      apiError: false,
      apiParams: newTestData[this.state.selectedApiIndex].apiParams,
      selectedFormat: format
    }
    newTestData[this.state.selectedApiIndex] = Object.assign(newTestData[this.state.selectedApiIndex], onSuccessTestData)
    if(format === RESPONSE_FORMATS.table.value){
      response.columns.map((column) => {
        newAPIdef[this.state.selectedApiIndex].columns[column.name] = {
          type: (column.col_type)?column.col_type:'dimension',
          data_type: (column.data_type)?column.data_type:'string'
        }
      })
    }
    this.setState({testData: newTestData, apiDefinition: newAPIdef},()=>{
      this.onChangeApiDefinition('format', format)
    })
  }

  onErrorTest = (error) => {
    let tempTestData = this.state.testData.slice()
    let onErrorTestData = {
      apiResponse: JSON.parse(error.responseText),
      apiSuccess: false,
      apiError: true,
      apiParams: tempTestData[this.state.selectedApiIndex].apiParams,
      selectedFormat: tempTestData[this.state.selectedApiIndex].selectedFormat
    }
    tempTestData[this.state.selectedApiIndex] = Object.assign(tempTestData[this.state.selectedApiIndex], onErrorTestData)
    this.setState({testData: tempTestData})
  }

  exportConfigAsYaml = () => {
    let apiDefinition =this.state.apiDefinition.slice()
    let fileName = 'hidash-api'
    let yamlData = exportFile(apiDefinition, YAML_CONTENT_TYPE)
    saveBlobToFile(yamlData, fileName+'.yaml')
  }

  processParamDef = (definitions) => {
    let appliedDef = {}
    if (definitions.length) {
      definitions.map((data) => {
        appliedDef[data.name] = {
          type: data.type,
          optional: data.optional,
          default_values: data.default_values,
          isParamDefCustom: data.isParamDefCustom
        }
        if (data.hasOwnProperty('kwargs')) {
          appliedDef[data.name].kwargs = data.kwargs
        }
      })
    }
    return appliedDef
  }

  onHandleTestButton = (event, format=null) => {
    let tempParam = this.state.testData[this.state.selectedApiIndex].apiParams || {}
    let paramObj = {}
    try {
      paramObj = typeof tempParam === 'string' ? JSON.parse(tempParam) : tempParam
    } catch (e) {
      console.log(e)
      console.log('please check your object syntax. Object key and value should be wrapped up in double quotes. Expected input: {"objKey": "objVal"}')
    }
    let paramDef = this.processParamDef(this.state.apiDefinition[this.state.selectedApiIndex].paramDefinition)
    format = format || 'table'
    let payloadObj = {
      config: {
        query: this.state.apiDefinition[this.state.selectedApiIndex].sqlQuery
      },
      transformations: this.state.apiDefinition[this.state.selectedApiIndex].transformations,
      format: format,
      params: paramObj.params,
      parameters: paramDef,
      user: paramObj.session,
      validations: this.state.apiDefinition[this.state.selectedApiIndex].validations,
      columns: this.state.apiDefinition[this.state.selectedApiIndex].columns,
      connection: this.state.selectedDB
    }
    postApiRequest(apiUriHostName+'/test/', payloadObj,
                   this.onSuccessTest, this.onErrorTest, format)
  }

  //Removes the API definition from the curret API list
  apiDeletionHandler = (index) => {
    if(this.state.apiDefinition.length > 1) {
      let newApiDefinitions = this.state.apiDefinition.slice(),
          newTestData = this.state.testData.slice(),
          newOpenAPIs = this.state.openAPIs.slice()
      newApiDefinitions.splice(index, 1)
      newTestData.splice(index, 1)

      let openAPIsIndex = newOpenAPIs.indexOf(index),
          newSelectedApiIndex = -1
      newOpenAPIs.splice(openAPIsIndex, 1)

      if (newOpenAPIs.length) {
        newSelectedApiIndex = openAPIsIndex ? newOpenAPIs[openAPIsIndex - 1] : 0
      } else {
        newSelectedApiIndex = 0
      }

      //Decrement the selectedApiIndex only if the selectedIndex is not zero
      let selectedApiIndex = this.state.selectedApiIndex
      selectedApiIndex = selectedApiIndex===0 ? 0 : newSelectedApiIndex

      let untitledApiIndex = 0
      newApiDefinitions.map((api) => {
        if(api.apiName.includes('Untitled API')) {
          api.apiName = 'Untitled API '+ (untitledApiIndex++)
          api.urlName = api.apiName.replace(/\s+/g, '-').toLowerCase()
        }
      })

      this.setState({
        apiDefinition: newApiDefinitions,
        testData: newTestData,
        selectedApiIndex: selectedApiIndex,
        newOpenAPIs: newOpenAPIs
      })

    } else {
      let tempApiDef = [],
          tempTestData = [],
          tempOpenApis = []
      this.setState({apiDefinition: tempApiDef, testData: tempTestData, openAPIs: tempOpenApis}, () => this.apiCloseHandler(0))

    }

  }

  //Appends an empty API definition object to current API Definitions
  apiAdditionHandler = () => {
    let newApiDefinitions = this.state.apiDefinition.slice(),
      newTestData = this.state.testData.slice(),
      selectedApiIndex = newApiDefinitions.length,
      newOpenAPIs = this.state.openAPIs.slice()
    newApiDefinitions.push(getDefaultApiDefinition(newApiDefinitions.length))
    newTestData.push(getEmptyTestData())
    newOpenAPIs.push(selectedApiIndex)
    this.setState({
      apiDefinition: newApiDefinitions,
      testData: newTestData,
      selectedApiIndex: selectedApiIndex,
      openAPIs: newOpenAPIs
    })
  }

  //Changes the selected API index to the one which was clicked from the API list
  apiSelectionHandler = (index) => {
    this.setState({selectedApiIndex: index})
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (nextState !== nextProps) {
      //FIXME: Need to change hardcode localstorage key name. Later we will save as project name
      setDataInLocalStorage('hidash', nextState)
      return true
    }
  }

  dbUpdationHandler = (selectedDB) => {
    this.setState({selectedDB: selectedDB.value})
  }

  componentDidUpdate(prevProps, prevState) {
    let apiDefinition =this.state.apiDefinition.slice()
    let yamlData = saveYamlOnServer(apiDefinition)
    let data = {yamlData: yamlData}
    postApiRequest(apiUriHostName+'/yaml-generator/', data, ()=>{},()=>{}, null)
  }

  render () {
    return (
      <div>
      <NavHeader apiDefinition={this.state.apiDefinition} apiOpenHandler={this.apiOpenHandler} apiAdditionHandler={this.apiAdditionHandler} exportConfigAsYaml={this.exportConfigAsYaml}/>
      <ApiTabs
        {...this.state}
        apiCloseHandler={this.apiCloseHandler}
        apiTabRenameHandler={this.apiTabRenameHandler}
        onChangeApiDefinition={this.onChangeApiDefinition}
        onHandleTestButton={this.onHandleTestButton}
        apiDeletionHandler={this.apiDeletionHandler}
        apiAdditionHandler={this.apiAdditionHandler}
        apiSelectionHandler={this.apiSelectionHandler}
        apiParamToggleHandler={this.apiParamToggleHandler}
        onChangeTestData={this.onChangeTestData}
        dbUpdationHandler={this.dbUpdationHandler}
      />
      </div>
    )
  }
}
<|endoftext|>


<commit_before>import { Dictionary } from './dictionary.js';
import { Queue } from './queue.js';
import Serializer from './serializer.js';
import { ConnectionManager } from './connectionmanager.js';
import * as msg from './messaging.js'
import { FavoritesTracker } from './favoritestracker.js';

/**
 * This class is supposed to be a general-purpose cache for HTTP requests with different HTTP methods.
 */
export class Cache {

  /**
   * Constructs a new Cache object
   * @param fileSystem A reference to the filesystem. Needed to process queued filesystem requests.
   */
  constructor(fileSystem) {
    this._managesFavorits = true;
    this._dictionary = new Dictionary('response-cache');
    this._queue = new Dictionary('request-cache');
    this._favoritesTracker = new FavoritesTracker();
    this._connectionManager = new ConnectionManager();
    this._fileSystem = fileSystem;

    // Register for network status changes
    this._connectionManager.addListener('statusChanged', (status) => {
      if(status.isOnline) {
        // We're back online after being online
        // Process all queued requests
        this._processQueued();
      }
    });

    // Define which HTTP methods need result caching, and which need request queueing
    this._cacheMethods = ['OPTIONS', 'GET'];
    this._queueMethods = ['PUT', 'POST', 'DELETE'];
  }

  /**
   * Fetches a request from the cache or network, according to the caching strategy.
   * To be used e.g. in `event.respondWith(...)`.
   * @param request The request to respond to
   * @param doNetworkRequest A function to call if we need to send out a network request
   */
  fetch(request, doNetworkRequest) {
    if (this._managesFavorits) {
      this._favoritesTracker.update(request.url);
    }

    if (this._connectionManager.isOnline) {
      return this._onlineResponse(request, doNetworkRequest);
    } else {
      return this._offlineResponse(request);
    }
  }

  /**
   * Returns a response for online devices
   * @param request The request to respond to
   * @param doNetworkRequest A function to call if we need to send out a network request
   */
  _onlineResponse(request, doNetworkRequest) {
    // When online, handle requests normaly and store the result
    return doNetworkRequest().then((response) => {
      // Currently, we only store OPTIONS and GET requests in the cache
      if (this._cacheMethods.includes(request.method)) {
        this._put(request, response);
      }
      return response;
    });
  }

  /**
   * Returns a response for offline devices
   * @param request The request to respond to
   */
  _offlineResponse(request) {
    // When offline, check the cache or put request in queue
    if (this._cacheMethods.includes(request.method)) {
      // Check if the request is in the cache
      return this._match(request).then((response) => {
        if (response) {
          //msg.broadcast('Fulfilled request from cache.', 'warning');
          return Serializer.deserialize(response.value);
        } else {
          msg.broadcast('Could not fulfil request from cache.', 'error');
          console.error(`Not in cache: ${request.url}`);
          // At this point we know we are offline, so sending out the request is useless
          // Just create a fake error Response
          return this._buildNotCachedResponse();
        }
      })
    } else if (this._queueMethods.includes(request.method)) {
      this._enqueue(request);
      msg.broadcast('Queued write request.', 'warning');
      return this._buildEnqueuedResponse();
    }
  }

  /**
   * Checks if a request is in the cache
   * @return Promise
   */
  _match(request) {
    return this._dictionary.match(this._buildKey(request));
  }

  /**
   * Puts a response for a request in the cache
   * @return Response
   */
  _put(request, response) {
    Serializer.serialize(response).then((serializedResponse) => {
      this._dictionary.put(this._buildKey(request), serializedResponse);
    })
    return response;
  }

  /**
   * Checks if a request is in the queue
   * @return void
   */
  _enqueue(request) {
    // Serialize the Request object
    Serializer.serialize(request).then((serializedRequest) => {
      // Put the serialized request in the queue
      this._queue.put(this._buildKey(request), serializedRequest);

      // Update the cache content to pretend that the data has already been saved
      /* TODO: Directories (on PUT and DELETE)
       * - Check if containing Directory is cached
       * - Update cached content
       */
      let fileURL = new URL(serializedRequest.url)
      let filePathParts = fileURL.pathname.split('/');
      let fileName = filePathParts.pop();
      let folderURL = serializedRequest.url.slice(0, -fileName.length);

      // Check if we have the folder content cached
      const folderKey = `OPTIONS ${folderURL}`;
      this._dictionary.match(folderKey).then((response) => {
        if(response) {
          // We have the folder in the cache - update it
          let reader = new FileReader();
          reader.onload = () => {
            let folderJSON = JSON.parse(reader.result);
            let folderContainsFile = folderJSON.contents.find(e => e.name === fileName);
            if(!folderContainsFile && serializedRequest.method === 'PUT') {
              // Prepare new directory JSON
              folderJSON.contents.push({
                name: fileName,
                size: 0,
                type: 'file'
              });
              let folderBlob = new Blob([JSON.stringify(folderJSON)], {type : 'text/plain'});

              // Update cached directory JSON
              this._dictionary.match(folderKey).then((response) => {
                if(response) {
                  response.value.body = folderBlob;
                  this._dictionary.put(folderKey, response.value);
                }
              })
            } else if(folderContainsFile && serializedRequest.method === 'DELETE') {
              // Prepare new directory JSON
              folderJSON.contents = folderJSON.contents.filter(e => e.name !== fileName);
              let folderBlob = new Blob([JSON.stringify(folderJSON)], {type : 'text/plain'});

              // Update cached directory JSON
              this._dictionary.match(folderKey).then((response) => {
                if(response) {
                  response.value.body = folderBlob;
                  this._dictionary.put(folderKey, response.value);
                }
              })
            }
          }
          reader.readAsText(response.value.body);
        }
      });

      // Update file content
      const key = `GET ${serializedRequest.url}`;
      this._dictionary.match(key).then((response) => {
        if(response) {
          response.value.body = serializedRequest.body;
          this._dictionary.put(key, response.value);
        }
      })
    })
  }

  /**
   * Processes all queued requests by sending them in the same order
   */
  _processQueued() {
    console.warn("Should process queued");
    let processNext = () => {
      // Get oldest entry
      this._queue.pop().then((serializedRequest) => {
        // Check if we are done
        if(!serializedRequest) {
          return;
        }

        // Send request
        Serializer.deserialize(serializedRequest).then((request) => {
          let url = new URL(request.url);
          if(url.hostname === 'lively4') {
            this._fileSystem.handle(request, url).then(processNext);
          } else {
            fetch(request).then(processNext);
          }
        });
      });
    }

    // Start processing queued requests
    processNext();
  }

  /**
   * Builds a key for the cache from a request
   * @return String key
   */
  _buildKey(request) {
    // Ignore params when loading start.html
    // The file always has the same content, and we want to boot offline whenever possible
    let requestUrl = new URL(request.url);
    if(requestUrl.origin == self.location.origin && requestUrl.pathname.endsWith('start.html')) {
      return `${request.method} ${requestUrl.origin}/${requestUrl.pathname}`;
    }

    return `${request.method} ${request.url}`;
  }

  /**
   * Builds a fake success Response to return when a Request is enqueued
   * @return Response
   */
  _buildEnqueuedResponse() {
    return new Response(null, {
      status: 202,
      statusText: 'Accepted'
    });
  }

  /**
   * Builds a fake error Response to return when offline and not cached
   * @return Response
   */
  _buildNotCachedResponse() {
    let errorText = 'You are offline and the requested file was not found in the cache.';
    return new Response(errorText, {
      status: 503,
      statusText: 'Service Unavailable'
    });
  }
}

/* Old methods used by filesystems */
function open(cache_name) {
  return caches.open(cache_name)
}

export function put(request, response) {
  let blob_text = [Date.now().toString()]
  let blob_type = {type : 'text/html'}
  let blob = new Blob(blob_text, blob_type)
  let resp = new Response(blob)
  open('lively4-cache-line-ages').then((cache) => cache.put(request, resp))
  return open('lively4').then((cache) => cache.put(request, response))
}

export function purge(request) {
  open('lively4-cache-line-ages').then((cache) => cache.delete(request))
  return open('lively4').then((cache) => cache.delete(request))
}

export async function match(request, timeout=-1) {
  try {
    if (timeout != -1) {
      let age = await getAgeOf(request)
      if (!age) return Promise.resolve(undefined)

      let age_v = await age.text()

      if (age && Date.now() - parseInt(age_v) >= timeout) {
        purge(request)
        return Promise.resolve(undefined)
      }
    }

    let cache = await open('lively4')
    let match = await cache.match(request)

    return match
  } catch(e) {
    console.warn('Error happened while matching cache:', e)

    return Promise.resolve(undefined)
  }
}

export function getAgeOf(request) {
  return open('lively4-cache-line-ages').then((cache) => cache.match(request))
}

<commit_msg>Remove queue dependency from cache.js<commit_after>import { Dictionary } from './dictionary.js';
import Serializer from './serializer.js';
import { ConnectionManager } from './connectionmanager.js';
import * as msg from './messaging.js'
import { FavoritesTracker } from './favoritestracker.js';

/**
 * This class is supposed to be a general-purpose cache for HTTP requests with different HTTP methods.
 */
export class Cache {

  /**
   * Constructs a new Cache object
   * @param fileSystem A reference to the filesystem. Needed to process queued filesystem requests.
   */
  constructor(fileSystem) {
    this._managesFavorits = true;
    this._dictionary = new Dictionary('response-cache');
    this._queue = new Dictionary('request-cache');
    this._favoritesTracker = new FavoritesTracker();
    this._connectionManager = new ConnectionManager();
    this._fileSystem = fileSystem;

    // Register for network status changes
    this._connectionManager.addListener('statusChanged', (status) => {
      if(status.isOnline) {
        // We're back online after being online
        // Process all queued requests
        this._processQueued();
      }
    });

    // Define which HTTP methods need result caching, and which need request queueing
    this._cacheMethods = ['OPTIONS', 'GET'];
    this._queueMethods = ['PUT', 'POST', 'DELETE'];
  }

  /**
   * Fetches a request from the cache or network, according to the caching strategy.
   * To be used e.g. in `event.respondWith(...)`.
   * @param request The request to respond to
   * @param doNetworkRequest A function to call if we need to send out a network request
   */
  fetch(request, doNetworkRequest) {
    if (this._managesFavorits) {
      this._favoritesTracker.update(request.url);
    }

    if (this._connectionManager.isOnline) {
      return this._onlineResponse(request, doNetworkRequest);
    } else {
      return this._offlineResponse(request);
    }
  }

  /**
   * Returns a response for online devices
   * @param request The request to respond to
   * @param doNetworkRequest A function to call if we need to send out a network request
   */
  _onlineResponse(request, doNetworkRequest) {
    // When online, handle requests normaly and store the result
    return doNetworkRequest().then((response) => {
      // Currently, we only store OPTIONS and GET requests in the cache
      if (this._cacheMethods.includes(request.method)) {
        this._put(request, response);
      }
      return response;
    });
  }

  /**
   * Returns a response for offline devices
   * @param request The request to respond to
   */
  _offlineResponse(request) {
    // When offline, check the cache or put request in queue
    if (this._cacheMethods.includes(request.method)) {
      // Check if the request is in the cache
      return this._match(request).then((response) => {
        if (response) {
          //msg.broadcast('Fulfilled request from cache.', 'warning');
          return Serializer.deserialize(response.value);
        } else {
          msg.broadcast('Could not fulfil request from cache.', 'error');
          console.error(`Not in cache: ${request.url}`);
          // At this point we know we are offline, so sending out the request is useless
          // Just create a fake error Response
          return this._buildNotCachedResponse();
        }
      })
    } else if (this._queueMethods.includes(request.method)) {
      this._enqueue(request);
      msg.broadcast('Queued write request.', 'warning');
      return this._buildEnqueuedResponse();
    }
  }

  /**
   * Checks if a request is in the cache
   * @return Promise
   */
  _match(request) {
    return this._dictionary.match(this._buildKey(request));
  }

  /**
   * Puts a response for a request in the cache
   * @return Response
   */
  _put(request, response) {
    Serializer.serialize(response).then((serializedResponse) => {
      this._dictionary.put(this._buildKey(request), serializedResponse);
    })
    return response;
  }

  /**
   * Checks if a request is in the queue
   * @return void
   */
  _enqueue(request) {
    // Serialize the Request object
    Serializer.serialize(request).then((serializedRequest) => {
      // Put the serialized request in the queue
      this._queue.put(this._buildKey(request), serializedRequest);

      // Update the cache content to pretend that the data has already been saved
      /* TODO: Directories (on PUT and DELETE)
       * - Check if containing Directory is cached
       * - Update cached content
       */
      let fileURL = new URL(serializedRequest.url)
      let filePathParts = fileURL.pathname.split('/');
      let fileName = filePathParts.pop();
      let folderURL = serializedRequest.url.slice(0, -fileName.length);

      // Check if we have the folder content cached
      const folderKey = `OPTIONS ${folderURL}`;
      this._dictionary.match(folderKey).then((response) => {
        if(response) {
          // We have the folder in the cache - update it
          let reader = new FileReader();
          reader.onload = () => {
            let folderJSON = JSON.parse(reader.result);
            let folderContainsFile = folderJSON.contents.find(e => e.name === fileName);
            if(!folderContainsFile && serializedRequest.method === 'PUT') {
              // Prepare new directory JSON
              folderJSON.contents.push({
                name: fileName,
                size: 0,
                type: 'file'
              });
              let folderBlob = new Blob([JSON.stringify(folderJSON)], {type : 'text/plain'});

              // Update cached directory JSON
              this._dictionary.match(folderKey).then((response) => {
                if(response) {
                  response.value.body = folderBlob;
                  this._dictionary.put(folderKey, response.value);
                }
              })
            } else if(folderContainsFile && serializedRequest.method === 'DELETE') {
              // Prepare new directory JSON
              folderJSON.contents = folderJSON.contents.filter(e => e.name !== fileName);
              let folderBlob = new Blob([JSON.stringify(folderJSON)], {type : 'text/plain'});

              // Update cached directory JSON
              this._dictionary.match(folderKey).then((response) => {
                if(response) {
                  response.value.body = folderBlob;
                  this._dictionary.put(folderKey, response.value);
                }
              })
            }
          }
          reader.readAsText(response.value.body);
        }
      });

      // Update file content
      const key = `GET ${serializedRequest.url}`;
      this._dictionary.match(key).then((response) => {
        if(response) {
          response.value.body = serializedRequest.body;
          this._dictionary.put(key, response.value);
        }
      })
    })
  }

  /**
   * Processes all queued requests by sending them in the same order
   */
  _processQueued() {
    console.warn("Should process queued");
    let processNext = () => {
      // Get oldest entry
      this._queue.pop().then((serializedRequest) => {
        // Check if we are done
        if(!serializedRequest) {
          return;
        }

        // Send request
        Serializer.deserialize(serializedRequest).then((request) => {
          let url = new URL(request.url);
          if(url.hostname === 'lively4') {
            this._fileSystem.handle(request, url).then(processNext);
          } else {
            fetch(request).then(processNext);
          }
        });
      });
    }

    // Start processing queued requests
    processNext();
  }

  /**
   * Builds a key for the cache from a request
   * @return String key
   */
  _buildKey(request) {
    // Ignore params when loading start.html
    // The file always has the same content, and we want to boot offline whenever possible
    let requestUrl = new URL(request.url);
    if(requestUrl.origin == self.location.origin && requestUrl.pathname.endsWith('start.html')) {
      return `${request.method} ${requestUrl.origin}/${requestUrl.pathname}`;
    }

    return `${request.method} ${request.url}`;
  }

  /**
   * Builds a fake success Response to return when a Request is enqueued
   * @return Response
   */
  _buildEnqueuedResponse() {
    return new Response(null, {
      status: 202,
      statusText: 'Accepted'
    });
  }

  /**
   * Builds a fake error Response to return when offline and not cached
   * @return Response
   */
  _buildNotCachedResponse() {
    let errorText = 'You are offline and the requested file was not found in the cache.';
    return new Response(errorText, {
      status: 503,
      statusText: 'Service Unavailable'
    });
  }
}

/* Old methods used by filesystems */
function open(cache_name) {
  return caches.open(cache_name)
}

export function put(request, response) {
  let blob_text = [Date.now().toString()]
  let blob_type = {type : 'text/html'}
  let blob = new Blob(blob_text, blob_type)
  let resp = new Response(blob)
  open('lively4-cache-line-ages').then((cache) => cache.put(request, resp))
  return open('lively4').then((cache) => cache.put(request, response))
}

export function purge(request) {
  open('lively4-cache-line-ages').then((cache) => cache.delete(request))
  return open('lively4').then((cache) => cache.delete(request))
}

export async function match(request, timeout=-1) {
  try {
    if (timeout != -1) {
      let age = await getAgeOf(request)
      if (!age) return Promise.resolve(undefined)

      let age_v = await age.text()

      if (age && Date.now() - parseInt(age_v) >= timeout) {
        purge(request)
        return Promise.resolve(undefined)
      }
    }

    let cache = await open('lively4')
    let match = await cache.match(request)

    return match
  } catch(e) {
    console.warn('Error happened while matching cache:', e)

    return Promise.resolve(undefined)
  }
}

export function getAgeOf(request) {
  return open('lively4-cache-line-ages').then((cache) => cache.match(request))
}

<|endoftext|>


<commit_before>#!/usr/bin/env node
const isProdRelease = process.env.BRANCH && process.env.BRANCH === "master"
const spawn = require('cross-spawn');
const exec = require('child_process').execSync;

const spawnWithErrorHandling = (...args) => {
  const results = spawn.sync(...args);
  if (results.error) {
    throw results.error;
  }
};

// Set dist-tag
const tasks = [
  'run',
  'lerna',
  'publish',
  '--',
  '--yes'
]

if (!isProdRelease) { // pre-release
  tasks.push('--skip-git')
  tasks.push('--cd-version=prerelease')
}

// Publish packages to npm registry
spawnWithErrorHandling('npm', tasks, { stdio: 'inherit' });

// console.log('Pushing commit...');
// exec('git checkout staging');
// exec('git add .');
// exec(`git commit -m publish packages`);
// exec(`git push origin ${process.env.BRANCH}`);

// console.log('Done!');
<commit_msg>omit skip git<commit_after>#!/usr/bin/env node
const isProdRelease = process.env.BRANCH && process.env.BRANCH === "master"
const spawn = require('cross-spawn');
const exec = require('child_process').execSync;

const spawnWithErrorHandling = (...args) => {
  const results = spawn.sync(...args);
  if (results.error) {
    throw results.error;
  }
};

// Set dist-tag
const tasks = [
  'run',
  'lerna',
  'publish',
  '--',
  '--yes'
]

if (!isProdRelease) { // pre-release
  // tasks.push('--skip-git')
  tasks.push('--cd-version=prerelease')
}

// Publish packages to npm registry
spawnWithErrorHandling('npm', tasks, { stdio: 'inherit' });

// console.log('Pushing commit...');
// exec('git checkout staging');
// exec('git add .');
// exec(`git commit -m publish packages`);
// exec(`git push origin ${process.env.BRANCH}`);

// console.log('Done!');
<|endoftext|>


<commit_before>var word = ""
var arr = []
$(document).keypress(function(e) {
    if(e.which == 13) {

      word = $('#givenWord').val();
      word = word.toLowerCase();
      word = $.trim(word);
      console.log(word.split(" "));
      word = word.split(" ")[0];
      console.log(word);

      var ans = new Array;
      $.get('preceed_with_an.txt', function(data){
        var ans = data.split('\n');
        var answer = $("#answer");
        for (var i = 0; i < ans.length; i++){
          if (ans[i] === word && word != ""){
            $(answer).text("An");
            break;
          }else{
            $(answer).text("A");
          }
        }
      });
    }
});

function resizeInput() {

    //Firstly take the content or placeholder if content is missing.
    var content =
        $(this).val().length > 0 ? $(this).val() : $(this).prop("placeholder");

    //Create testing element with same content as input.
    var widthTester = $(""+content+"").hide();

    //Place testing element into DOM after input (so it inherits same formatting as input does).
    widthTester.insertAfter($(this));

    //Set inputs width; you may want to use outerWidth() or innerWidth()
    //depending whether you want to count padding and border or not.
    $(this).css("width",widthTester.width()+"px");

    //Remove the element from the DOM
    widthTester.remove();
 }

$('input[type="text"]')
    // event handler
    .keyup(resizeInput)
    // resize on page load
    .each(resizeInput);
<commit_msg>cursor now actually starts at end<commit_after>var word = ""
var arr = []
$(document).keypress(function(e) {
    if(e.which == 13) {

      word = $('#givenWord').val();
      word = word.toLowerCase();
      word = $.trim(word);
      console.log(word.split(" "));
      word = word.split(" ")[0];
      console.log(word);

      var ans = new Array;
      $.get('preceed_with_an.txt', function(data){
        var ans = data.split('\n');
        var answer = $("#answer");
        for (var i = 0; i < ans.length; i++){
          if (ans[i] === word && word != ""){
            $(answer).text("An");
            break;
          }else{
            $(answer).text("A");
          }
        }
      });
    }
});

function resizeInput() {

    //Firstly take the content or placeholder if content is missing.
    var content =
        $(this).val().length > 0 ? $(this).val() : $(this).prop("placeholder");

    //Create testing element with same content as input.
    var widthTester = $(""+content+"").hide();

    //Place testing element into DOM after input (so it inherits same formatting as input does).
    widthTester.insertAfter($(this));

    //Set inputs width; you may want to use outerWidth() or innerWidth()
    //depending whether you want to count padding and border or not.
    $(this).css("width",widthTester.width()+"px");

    //Remove the element from the DOM
    widthTester.remove();
 }

$('input[type="text"]')
    // event handler
    .keyup(resizeInput)
    // resize on page load
    .each(resizeInput);

$('#givenWord').focus(function() {
    setTimeout((function(el) {
        var strLength = el.value.length;
        return function() {
            if(el.setSelectionRange !== undefined) {
                el.setSelectionRange(strLength, strLength);
            } else {
                $(el).val(el.value);
            }
    }}(this)), 0);
});
<|endoftext|>