<commit_before>'use strict';
import _ from 'lodash';
import Promise from 'native-promise-only';
import { checkCondition } from '../../../utils/utils';
import Component from '../component/Component';
import Components from '../../Components';

export default class NestedComponent extends Component {
  static schema(...extend) {
    return Component.schema({
      tree: true
    }, ...extend);
  }

  constructor(component, options, data) {
    super(component, options, data);
    this.type = 'components';
    this._collapsed = !!this.component.collapsed;
    this.components = [];
  }

  get defaultSchema() {
    return NestedComponent.schema();
  }

  get schema() {
    const schema = super.schema;
    schema.components = [];
    this.eachComponent((component) => schema.components.push(component.schema));
    return schema;
  }

  get collapsed() {
    return this._collapsed;
  }

  set collapsed(value) {
    this._collapsed = value;
    this.redraw();
  }

  set visible(value) {
    super.visible = value;
    const isVisible = this.visible;
    const forceShow = this.options.show && this.options.show[this.component.key];
    const forceHide = this.options.hide && this.options.hide[this.component.key];
    this.components.forEach(component => {
      const conditionallyVisible = component.conditionallyVisible();
      if (forceShow || (!isVisible && conditionallyVisible)) {
        component.visible = true;
      }
      else if (forceHide || (isVisible && !conditionallyVisible)) {
        component.visible = false;
      }
      component.parentVisible = isVisible;
    });
  }

  get visible() {
    return super.visible;
  }

  set parentVisible(value) {
    super.parentVisible = value;
    this.components.forEach(component => {
      component.parentVisible = this.visible;
    });
  }

  get parentVisible() {
    return super.parentVisible;
  }

  getComponents() {
    return this.components || [];
  }

  getAllComponents() {
    return this.getComponents().reduce((components, component) => {
      let result = component;

      if (component && component.getAllComponents) {
        result = component.getAllComponents();
      }

      return components.concat(result);
    }, []);
  }

  /**
   * Perform a deep iteration over every component, including those
   * within other container based components.
   *
   * @param {function} fn - Called for every component.
   */
  everyComponent(fn) {
    const components = this.getComponents();
    _.each(components, (component, index) => {
      if (fn(component, components, index) === false) {
        return false;
      }

      if (typeof component.everyComponent === 'function') {
        if (component.everyComponent(fn) === false) {
          return false;
        }
      }
    });
  }

  /**
   * Perform an iteration over each component within this container component.
   *
   * @param {function} fn - Called for each component
   */
  eachComponent(fn) {
    _.each(this.getComponents(), (component, index) => {
      if (fn(component, index) === false) {
        return false;
      }
    });
  }

  /**
   * Returns a component provided a key. This performs a deep search within the
   * component tree.
   *
   * @param {string} key - The key of the component to retrieve.
   * @param {function} fn - Called with the component once found.
   * @return {Object} - The component that is located.
   */
  getComponent(key, fn) {
    let comp = null;
    this.everyComponent((component, components) => {
      if (component.component.key === key) {
        comp = component;
        if (fn) {
          fn(component, components);
        }
        return false;
      }
    });
    return comp;
  }

  /**
   * Return a component provided the Id of the component.
   *
   * @param {string} id - The Id of the component.
   * @param {function} fn - Called with the component once it is retrieved.
   * @return {Object} - The component retrieved.
   */
  getComponentById(id, fn) {
    let comp = null;
    this.everyComponent((component, components) => {
      if (component.id === id) {
        comp = component;
        if (fn) {
          fn(component, components);
        }
        return false;
      }
    });
    return comp;
  }

  /**
   * Create a new component and add it to the components array.
   *
   * @param component
   * @param data
   */
  createComponent(component, options, data, before) {
    options = options || this.options;
    data = data || this.data;
    const comp = Components.create(component, options, data, true);
    comp.parent = this;
    comp.root = this.root || this;
    comp.parentVisible = this.visible;
    comp.isBuilt = true;
    if (component.internal) {
      return comp;
    }

    if (before) {
      const index = _.findIndex(this.components, { id: before.id });
      if (index !== -1) {
        this.components.splice(index, 0, comp);
      }
      else {
        this.components.push(comp);
      }
    }
    else {
      this.components.push(comp);
    }
    return comp;
  }

  getContainer() {
    return this.element;
  }

  get componentComponents() {
    return this.component.components || [];
  }

  get nestedKey() {
    return `nested-${this.key}`;
  }

  get templateName() {
    return 'container';
  }

  init() {
    this.components = this.components || [];
    this.addComponents();
  }

  /**
   *
   * @param element
   * @param data
   */
  addComponents(data) {
    data = data || this.data;
    this.componentComponents.forEach((component) => this.addComponent(component, data));
  }

  /**
   * Add a new component to the components array.
   *
   * @param {Object} component - The component JSON schema to add.
   * @param {Object} data - The submission data object to house the data for this component.
   * @param {HTMLElement} before - A DOM element to insert this element before.
   * @return {Component} - The created component instance.
   */
  addComponent(component, data, before, noAdd) {
    data = data || this.data;
    const comp = this.createComponent(component, this.options, data, before ? before.component : null);
    if (noAdd) {
      return comp;
    }
    return comp;
  }

  render(children) {
    // If already rendering, don't re-render.
    return super.render(children || this.renderTemplate(this.templateName, {
      children: this.renderComponents(),
      nestedKey: this.nestedKey,
      collapsed: this.collapsed,
    }));
  }

  renderComponents(components) {
    components = components || this.components;
    const children = components.map(component => component.render());
    return this.renderTemplate('components', {
      children,
      components,
    });
  }

  attach(element) {
    this.loadRefs(element, {
      header: 'single',
      collapsed: this.collapsed,
      [this.nestedKey]: 'single',
    });

    if (this.refs[this.nestedKey]) {
      this.attachComponents(this.refs[this.nestedKey]);
    }

    if (this.component.collapsible && this.refs.header) {
      this.addEventListener(this.refs.header, 'click', () => {
        this.collapsed = !this.collapsed;
      });
    }

    super.attach(element);
  }

  attachComponents(element, components, container) {
    components = components || this.components;
    container = container || this.component.components;

    element = this.hook('attachComponents', element, components, container, this);
    if (!element) {
      return;
    }

    let index = 0;
    Array.prototype.slice.call(element.children).forEach(child => {
      if (!child.getAttribute('data-noattach') && components[index]) {
        components[index].attach(child);
        index++;
      }
    });
  }

  /**
   * Remove a component from the components array.
   *
   * @param {Component} component - The component to remove from the components.
   * @param {Array<Component>} components - An array of components to remove this component from.
   */
  removeComponent(component, components) {
    component.destroy();
    _.remove(components, { id: component.id });
  }

  /**
   * Removes a component provided the API key of that component.
   *
   * @param {string} key - The API key of the component to remove.
   * @param {function} fn - Called once the component is removed.
   * @return {null}
   */
  removeComponentByKey(key, fn) {
    const comp = this.getComponent(key, (component, components) => {
      this.removeComponent(component, components);
      if (fn) {
        fn(component, components);
      }
    });
    if (!comp) {
      if (fn) {
        fn(null);
      }
      return null;
    }
  }

  /**
   * Removes a component provided the Id of the component.
   *
   * @param {string} id - The Id of the component to remove.
   * @param {function} fn - Called when the component is removed.
   * @return {null}
   */
  removeComponentById(id, fn) {
    const comp = this.getComponentById(id, (component, components) => {
      this.removeComponent(component, components);
      if (fn) {
        fn(component, components);
      }
    });
    if (!comp) {
      if (fn) {
        fn(null);
      }
      return null;
    }
  }

  updateValue(flags) {
    let changed = false;
    _.each(this.components, (comp) => {
      changed |= comp.updateValue(flags);
    });
    return changed;
  }

  hasChanged() {
    return false;
  }

  /**
   * A more performant way to check the conditions, calculations, and validity of
   * a submission once it has been changed.
   *
   * @param data
   * @param flags
   */
  checkData(data, flags) {
    flags = flags || {};
    let valid = true;
    if (flags.noCheck) {
      return;
    }

    // Update the value.
    let changed = this.updateValue({
      noUpdateEvent: true
    });

    // Iterate through all components and check conditions, and calculate values.
    _.each(this.getComponents(), (comp) => {
      changed |= comp.calculateValue(data, {
        noUpdateEvent: true
      });
      comp.checkConditions(data);
      if (!flags.noValidate) {
        valid &= comp.checkValidity(data);
      }
    });

    // Trigger the change if the values changed.
    if (changed) {
      this.triggerChange(flags);
    }

    // Return if the value is valid.
    return valid;
  }

  checkConditions(data) {
    this.getComponents().forEach(comp => comp.checkConditions(data));
    return super.checkConditions(data);
  }

  clearOnHide(show) {
    super.clearOnHide(show);
    this.getComponents().forEach(component => component.clearOnHide(show));
  }

  /**
   * Allow components to hook into the next page trigger to perform their own logic.
   *
   * @return {*}
   */
  beforeNext() {
    const ops = [];
    _.each(this.getComponents(), (comp) => ops.push(comp.beforeNext()));
    return Promise.all(ops);
  }

  /**
   * Allow components to hook into the submission to provide their own async data.
   *
   * @return {*}
   */
  beforeSubmit() {
    const ops = [];
    _.each(this.getComponents(), (comp) => ops.push(comp.beforeSubmit()));
    return Promise.all(ops);
  }

  calculateValue(data, flags) {
    let changed = super.calculateValue(data, flags);
    _.each(this.getComponents(), (comp) => {
      changed |= comp.calculateValue(data, flags);
    });
    return changed;
  }

  isValid(data, dirty) {
    let valid = super.isValid(data, dirty);
    _.each(this.getComponents(), (comp) => {
      valid &= comp.isValid(data, dirty);
    });
    return valid;
  }

  checkValidity(data, dirty) {
    if (!checkCondition(this.component, data, this.data, this.root ? this.root._form : {}, this)) {
      this.setCustomValidity('');
      return true;
    }

    let check = super.checkValidity(data, dirty);
    _.each(this.getComponents(), (comp) => {
      check &= comp.checkValidity(data, dirty);
    });
    return check;
  }

  setPristine(pristine) {
    super.setPristine(pristine);
    _.each(this.getComponents(), (comp) => (comp.setPristine(pristine)));
  }

  detach() {
    this.components.forEach(component => {
      component.detach();
    });
    super.detach();
  }

  destroy() {
    this.destroyComponents();
    super.destroy();
  }

  destroyComponents() {
    const components = _.clone(this.components);
    _.each(components, (comp) => this.removeComponent(comp, this.components));
    this.components = [];
  }

  setCustomValidity(message, dirty) {
    super.setCustomValidity(message, dirty);
    _.each(this.getComponents(), (comp) => comp.setCustomValidity(message, dirty));
  }

  set disabled(disabled) {
    _.each(this.components, (component) => (component.disabled = disabled));
  }

  get errors() {
    let errors = [];
    _.each(this.getAllComponents(), (comp) => {
      const compErrors = comp.errors;
      if (compErrors.length) {
        errors = errors.concat(compErrors);
      }
    });
    return errors;
  }

  get value() {
    return this.data;
  }

  getValue() {
    return this.data;
  }

  resetValue() {
    _.each(this.getComponents(), (comp) => (comp.resetValue()));
    _.unset(this.data, this.key);
    this.setPristine(true);
  }

  setValue(value, flags) {
    if (!value) {
      return false;
    }
    flags = this.getFlags.apply(this, arguments);
    let changed = false;
    this.getComponents().forEach(component => {
      if (component.type === 'button') {
        return;
      }

      if (component.type === 'components') {
        changed |= component.setValue(value, flags);
      }
      else if (value && component.hasValue(value)) {
        changed |= component.setValue(_.get(value, component.key), flags);
      }
      else {
        flags.noValidate = true;
        changed |= component.setValue(component.defaultValue, flags);
      }
    });
    return changed;
  }
}
<commit_msg>FOR-1500: Nested component improvements.<commit_after>'use strict';
import _ from 'lodash';
import Promise from 'native-promise-only';
import { checkCondition } from '../../../utils/utils';
import Component from '../component/Component';
import Components from '../../Components';

export default class NestedComponent extends Component {
  static schema(...extend) {
    return Component.schema({
      tree: true
    }, ...extend);
  }

  constructor(component, options, data) {
    super(component, options, data);
    this.type = 'components';
    this._collapsed = !!this.component.collapsed;
    this.components = [];
  }

  get defaultSchema() {
    return NestedComponent.schema();
  }

  get schema() {
    const schema = super.schema;
    schema.components = [];
    this.eachComponent((component) => schema.components.push(component.schema));
    return schema;
  }

  get collapsed() {
    return this._collapsed;
  }

  set collapsed(value) {
    this._collapsed = value;
    this.redraw();
  }

  set visible(value) {
    super.visible = value;
    const isVisible = this.visible;
    const forceShow = this.options.show && this.options.show[this.component.key];
    const forceHide = this.options.hide && this.options.hide[this.component.key];
    this.componentComponents.forEach(component => {
      const conditionallyVisible = component.conditionallyVisible();
      if (forceShow || (!isVisible && conditionallyVisible)) {
        component.visible = true;
      }
      else if (forceHide || (isVisible && !conditionallyVisible)) {
        component.visible = false;
      }
      component.parentVisible = isVisible;
    });
  }

  get visible() {
    return super.visible;
  }

  set parentVisible(value) {
    super.parentVisible = value;
    this.components.forEach(component => {
      component.parentVisible = this.visible;
    });
  }

  get parentVisible() {
    return super.parentVisible;
  }

  getComponents() {
    return this.components || [];
  }

  getAllComponents() {
    return this.getComponents().reduce((components, component) => {
      let result = component;

      if (component && component.getAllComponents) {
        result = component.getAllComponents();
      }

      return components.concat(result);
    }, []);
  }

  /**
   * Perform a deep iteration over every component, including those
   * within other container based components.
   *
   * @param {function} fn - Called for every component.
   */
  everyComponent(fn) {
    const components = this.getComponents();
    _.each(components, (component, index) => {
      if (fn(component, components, index) === false) {
        return false;
      }

      if (typeof component.everyComponent === 'function') {
        if (component.everyComponent(fn) === false) {
          return false;
        }
      }
    });
  }

  /**
   * Perform an iteration over each component within this container component.
   *
   * @param {function} fn - Called for each component
   */
  eachComponent(fn) {
    _.each(this.getComponents(), (component, index) => {
      if (fn(component, index) === false) {
        return false;
      }
    });
  }

  /**
   * Returns a component provided a key. This performs a deep search within the
   * component tree.
   *
   * @param {string} key - The key of the component to retrieve.
   * @param {function} fn - Called with the component once found.
   * @return {Object} - The component that is located.
   */
  getComponent(key, fn) {
    let comp = null;
    this.everyComponent((component, components) => {
      if (component.component.key === key) {
        comp = component;
        if (fn) {
          fn(component, components);
        }
        return false;
      }
    });
    return comp;
  }

  /**
   * Return a component provided the Id of the component.
   *
   * @param {string} id - The Id of the component.
   * @param {function} fn - Called with the component once it is retrieved.
   * @return {Object} - The component retrieved.
   */
  getComponentById(id, fn) {
    let comp = null;
    this.everyComponent((component, components) => {
      if (component.id === id) {
        comp = component;
        if (fn) {
          fn(component, components);
        }
        return false;
      }
    });
    return comp;
  }

  /**
   * Create a new component and add it to the components array.
   *
   * @param component
   * @param data
   */
  createComponent(component, options, data, before) {
    options = options || this.options;
    data = data || this.data;
    const comp = Components.create(component, options, data, true);
    comp.parent = this;
    comp.root = this.root || this;
    comp.parentVisible = this.visible;
    comp.isBuilt = true;
    if (component.internal) {
      return comp;
    }

    if (before) {
      const index = _.findIndex(this.components, { id: before.id });
      if (index !== -1) {
        this.components.splice(index, 0, comp);
      }
      else {
        this.components.push(comp);
      }
    }
    else {
      this.components.push(comp);
    }
    return comp;
  }

  getContainer() {
    return this.element;
  }

  get componentComponents() {
    return this.component.components || [];
  }

  get nestedKey() {
    return `nested-${this.key}`;
  }

  get templateName() {
    return 'container';
  }

  init() {
    this.components = this.components || [];
    this.addComponents();
  }

  /**
   *
   * @param element
   * @param data
   */
  addComponents(data) {
    data = data || this.data;
    this.componentComponents.forEach((component) => this.addComponent(component, data));
  }

  /**
   * Add a new component to the components array.
   *
   * @param {Object} component - The component JSON schema to add.
   * @param {Object} data - The submission data object to house the data for this component.
   * @param {HTMLElement} before - A DOM element to insert this element before.
   * @return {Component} - The created component instance.
   */
  addComponent(component, data, before, noAdd) {
    data = data || this.data;
    const comp = this.createComponent(component, this.options, data, before ? before.component : null);
    if (noAdd) {
      return comp;
    }
    return comp;
  }

  render(children) {
    // If already rendering, don't re-render.
    return super.render(children || this.renderTemplate(this.templateName, {
      children: this.renderComponents(),
      nestedKey: this.nestedKey,
      collapsed: this.collapsed,
    }));
  }

  renderComponents(components) {
    components = components || this.components;
    const children = components.map(component => component.render());
    return this.renderTemplate('components', {
      children,
      components,
    });
  }

  attach(element) {
    this.loadRefs(element, {
      header: 'single',
      collapsed: this.collapsed,
      [this.nestedKey]: 'single',
    });

    if (this.refs[this.nestedKey]) {
      this.attachComponents(this.refs[this.nestedKey]);
    }

    if (this.component.collapsible && this.refs.header) {
      this.addEventListener(this.refs.header, 'click', () => {
        this.collapsed = !this.collapsed;
      });
    }

    super.attach(element);
  }

  attachComponents(element, components, container) {
    components = components || this.components;
    container = container || this.component.components;

    element = this.hook('attachComponents', element, components, container, this);
    if (!element) {
      return;
    }

    let index = 0;
    Array.prototype.slice.call(element.children).forEach(child => {
      if (!child.getAttribute('data-noattach') && components[index]) {
        components[index].attach(child);
        index++;
      }
    });
  }

  /**
   * Remove a component from the components array.
   *
   * @param {Component} component - The component to remove from the components.
   * @param {Array<Component>} components - An array of components to remove this component from.
   */
  removeComponent(component, components) {
    component.destroy();
    _.remove(components, { id: component.id });
  }

  /**
   * Removes a component provided the API key of that component.
   *
   * @param {string} key - The API key of the component to remove.
   * @param {function} fn - Called once the component is removed.
   * @return {null}
   */
  removeComponentByKey(key, fn) {
    const comp = this.getComponent(key, (component, components) => {
      this.removeComponent(component, components);
      if (fn) {
        fn(component, components);
      }
    });
    if (!comp) {
      if (fn) {
        fn(null);
      }
      return null;
    }
  }

  /**
   * Removes a component provided the Id of the component.
   *
   * @param {string} id - The Id of the component to remove.
   * @param {function} fn - Called when the component is removed.
   * @return {null}
   */
  removeComponentById(id, fn) {
    const comp = this.getComponentById(id, (component, components) => {
      this.removeComponent(component, components);
      if (fn) {
        fn(component, components);
      }
    });
    if (!comp) {
      if (fn) {
        fn(null);
      }
      return null;
    }
  }

  updateValue(flags) {
    return this.componentComponents.reduce((changed, comp) => comp.updateValue(flags) || changed, false);
  }

  hasChanged() {
    return false;
  }

  /**
   * A more performant way to check the conditions, calculations, and validity of
   * a submission once it has been changed.
   *
   * @param data
   * @param flags
   */
  checkData(data, flags) {
    flags = flags || {};
    let valid = true;
    if (flags.noCheck) {
      return;
    }

    // Update the value.
    let changed = this.updateValue({
      noUpdateEvent: true
    });

    // Iterate through all components and check conditions, and calculate values.
    this.getComponents().forEach((comp) => {
      changed |= comp.calculateValue(data, {
        noUpdateEvent: true
      });
      comp.checkConditions(data);
      if (!flags.noValidate) {
        valid &= comp.checkValidity(data);
      }
    });

    // Trigger the change if the values changed.
    if (changed) {
      this.triggerChange(flags);
    }

    // Return if the value is valid.
    return valid;
  }

  checkConditions(data) {
    this.getComponents().forEach(comp => comp.checkConditions(data));
    return super.checkConditions(data);
  }

  clearOnHide(show) {
    super.clearOnHide(show);
    this.getComponents().forEach(component => component.clearOnHide(show));
  }

  /**
   * Allow components to hook into the next page trigger to perform their own logic.
   *
   * @return {*}
   */
  beforeNext() {
    return Promise.all(this.getComponents().map((comp) => comp.beforeNext()));
  }

  /**
   * Allow components to hook into the submission to provide their own async data.
   *
   * @return {*}
   */
  beforeSubmit() {
    return Promise.all(this.getComponents().map((comp) => comp.beforeSubmit()));
  }

  calculateValue(data, flags) {
    return this.getComponents().reduce(
      (changed, comp) => comp.calculateValue(data, flags) || changed,
      super.calculateValue(data, flags)
    );
  }

  isValid(data, dirty) {
    return this.getComponents().reduce(
      (valid, comp) => comp.isValid(data, dirty) && valid,
      super.isValid(data, dirty)
    );
  }

  checkValidity(data, dirty) {
    if (!checkCondition(this.component, data, this.data, this.root ? this.root._form : {}, this)) {
      this.setCustomValidity('');
      return true;
    }

    return this.getComponents().reduce(
      (check, comp) => comp.checkValidity(data, dirty) && check,
      super.checkValidity(data, dirty)
    );
  }

  setPristine(pristine) {
    super.setPristine(pristine);
    this.getComponents().forEach((comp) => comp.setPristine(pristine));
  }

  detach() {
    this.components.forEach(component => {
      component.detach();
    });
    super.detach();
  }

  destroy() {
    this.destroyComponents();
    super.destroy();
  }

  destroyComponents() {
    const components = this.componentComponents.slice();
    components.forEach((comp) => this.removeComponent(comp, this.components));
    this.components = [];
  }

  setCustomValidity(message, dirty) {
    super.setCustomValidity(message, dirty);
    _.each(this.getComponents(), (comp) => comp.setCustomValidity(message, dirty));
  }

  set disabled(disabled) {
    this.componentComponents.forEach((component) => component.disabled = disabled);
  }

  get errors() {
    return this.getAllComponents().reduce((errors, comp) => errors.concat(comp.errors || []), []);
  }

  get value() {
    return this.data;
  }

  getValue() {
    return this.data;
  }

  resetValue() {
    this.getComponents().forEach((comp) => comp.resetValue());
    _.unset(this.data, this.key);
    this.setPristine(true);
  }

  setValue(value, flags) {
    if (!value) {
      return false;
    }
    flags = this.getFlags.apply(this, arguments);
    return this.getComponents().reduce((changed, component) => {
      if (component.type === 'button') {
        return;
      }

      if (component.type === 'components') {
        return component.setValue(value, flags) || changed;
      }
      else if (value && component.hasValue(value)) {
        return component.setValue(_.get(value, component.key), flags) || changed;
      }
      else {
        flags.noValidate = true;
        return component.setValue(component.defaultValue, flags) || changed;
      }
    }, false);
  }
}
<|endoftext|>


<commit_before>/*   RSence
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** HImageView is a control unit intended to display images on the screen
  ** through the HTML <IMG> tag. The HImageView class is a container
  ** to visualize
  ** images loaded via URL. It supports scaling via two class methods,
  ** scaleToFit and scaleToOriginal. If the image is unable to be loaded,
  ** a default blank image will be rendered.
  **
  ***/
var//RSence.Controls
HImageView = HControl.extend({

  controlDefaults: (HControlDefaults.extend({
    scaleToFit: true,
    value: null,
    constructor: function(_ctrl){
      if(this.value===null){
        // default to a blank image
        this.value = _ctrl.getThemeGfxPath() + "/blank.gif";
      }
    }
  })),

  _makeScaleToFit: function(_parentId){
    var _value = (this.value!==null)?this.value:(this.options.valueObj?this.options.valueObj.value:this.options.value);
    this.elemId = ELEM.make(_parentId,'img');
    ELEM.setAttr(this.elemId,'src',_value);
    ELEM.setAttr(this.elemId,'alt',this.label);
    ELEM.setAttr(this.elemId,'title',this.label);
  },
  _makeScaleToOriginal: function(_parentId){
    var _value = (this.value!==null)?this.value:(this.options.valueObj?this.options.valueObj.value:this.options.value);
    this.elemId = ELEM.make(_parentId,'div');
    ELEM.setStyle(this.elemId,'background-image','url('+_value+')');
    ELEM.setStyle(this.elemId,'background-position','0px 0px');
    ELEM.setStyle(this.elemId,'background-repeat','no-repeat');
    ELEM.setAttr(this.elemId,'title',this.label);
  },
  _makeElem: function(_parentId){
    if(this.options.scaleToFit){
      this._makeScaleToFit(_parentId);
    }
    else {
      this._makeScaleToOriginal(_parentId);
    }
  },

/** = Description
  * Used to refresh HImageView if the this.value is changed.
  *
  **/
  refreshValue: function(){
    if(this.options.scaleToFit){
      ELEM.setAttr(this.elemId,'src',this.value);
    }
    else{
      ELEM.setStyle(this.elemId,'background-image','url('+this.value+')');
    }
  },

/** = Description
  * Refreshesh the label of HImageView.
  *
  **/
  refreshLabel: function(){
    if(this.options.scaleToFit){
      ELEM.setAttr(this.elemId,'alt',this.label);
    }
    ELEM.setAttr(this.elemId,'title',this.label);
  },

/** = Description
  * Changes the size of the image element so that it fits in the rectangle of
  * the view.
  *
  **/
  scaleToFit: function() {
    if(!this.options.scaleToFit){
      ELEM.del(this.elemId);
      this._makeScaleToFit(this._getParentElemId());
      this.options.scaleToFit=true;
    }
  },

/** = Description
  * Resizes the image element to its original dimesions. If the image is larger
  * than the rectangle of this view, clipping will occur.
  *
  **/
  scaleToOriginal: function() {
    if(this.options.scaleToFit){
      ELEM.del(this.elemId);
      this._makeScaleToOriginal(this._getParentElemId());
      this.options.scaleToFit=false;
    }
  }

});
<commit_msg>better source checking<commit_after>/*   RSence
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** HImageView is a control unit intended to display images on the screen
  ** through the HTML <IMG> tag. The HImageView class is a container
  ** to visualize
  ** images loaded via URL. It supports scaling via two class methods,
  ** scaleToFit and scaleToOriginal. If the image is unable to be loaded,
  ** a default blank image will be rendered.
  **
  ***/
var//RSence.Controls
HImageView = HControl.extend({

  getImgSrc: function(){
    var _value = (this.value!==null)?this.value:(this.options.valueObj?this.options.valueObj.value:this.options.value);
    if (!_value){
      _value = this.getThemeGfxPath() + "/blank.gif";
    }
    return _value;
  },

  controlDefaults: (HControlDefaults.extend({
    scaleToFit: true,
    value: null
  })),

  _makeScaleToFit: function(_parentId){
    this.elemId = ELEM.make(_parentId,'img');
    ELEM.setAttr(this.elemId,'src',this.getImgSrc());
    ELEM.setAttr(this.elemId,'alt',this.label);
    ELEM.setAttr(this.elemId,'title',this.label);
  },
  _makeScaleToOriginal: function(_parentId){
    this.elemId = ELEM.make(_parentId,'div');
    ELEM.setStyle(this.elemId,'background-image','url('+this.getImgSrc()+')');
    ELEM.setStyle(this.elemId,'background-position','0px 0px');
    ELEM.setStyle(this.elemId,'background-repeat','no-repeat');
    ELEM.setAttr(this.elemId,'title',this.label);
  },
  _makeElem: function(_parentId){
    if(this.options.scaleToFit){
      this._makeScaleToFit(_parentId);
    }
    else {
      this._makeScaleToOriginal(_parentId);
    }
  },

/** = Description
  * Used to refresh HImageView if the this.value is changed.
  *
  **/
  refreshValue: function(){
    if(this.options.scaleToFit){
      ELEM.setAttr(this.elemId,'src',this.getImgSrc());
    }
    else{
      ELEM.setStyle(this.elemId,'background-image','url('+this.getImgSrc()+')');
    }
  },

/** = Description
  * Refreshesh the label of HImageView.
  *
  **/
  refreshLabel: function(){
    if(this.options.scaleToFit){
      ELEM.setAttr(this.elemId,'alt',this.label);
    }
    ELEM.setAttr(this.elemId,'title',this.label);
  },

/** = Description
  * Changes the size of the image element so that it fits in the rectangle of
  * the view.
  *
  **/
  scaleToFit: function() {
    if(!this.options.scaleToFit){
      ELEM.del(this.elemId);
      this._makeScaleToFit(this._getParentElemId());
      this.options.scaleToFit=true;
    }
  },

/** = Description
  * Resizes the image element to its original dimesions. If the image is larger
  * than the rectangle of this view, clipping will occur.
  *
  **/
  scaleToOriginal: function() {
    if(this.options.scaleToFit){
      ELEM.del(this.elemId);
      this._makeScaleToOriginal(this._getParentElemId());
      this.options.scaleToFit=false;
    }
  }

});
<|endoftext|>


<commit_before>"use strict";

const should = require("should");

const Parser = require("../lib/Parser");
const BasicEvaluatedExpression = require("../lib/BasicEvaluatedExpression");

describe("Parser", () => {
	const testCases = {
		"call ident": [
			function() {
				abc("test");
			}, {
				abc: ["test"]
			}
		],
		"call member": [
			function() {
				cde.abc("membertest");
			}, {
				cdeabc: ["membertest"]
			}
		],
		"call member using bracket notation": [
			function() {
				cde["abc"]("membertest");
			}, {
				cdeabc: ["membertest"]
			}
		],
		"call inner member": [
			function() {
				cde.ddd.abc("inner");
			}, {
				cdedddabc: ["inner"]
			}
		],
		"call inner member using bracket notation": [
			function() {
				cde.ddd["abc"]("inner");
			}, {
				cdedddabc: ["inner"]
			}
		],
		"expression": [
			function() {
				fgh;
			}, {
				fgh: [""]
			}
		],
		"expression sub": [
			function() {
				fgh.sub;
			}, {
				fghsub: ["notry"]
			}
		],
		"member expression": [
			function() {
				test[memberExpr]
				test[+memberExpr]
			}, {
				expressions: ["memberExpr", "memberExpr"]
			}
		],
		"in function definition": [
			function() {
				(function(abc, cde, fgh) {
					abc("test");
					cde.abc("test");
					cde.ddd.abc("test");
					fgh;
					fgh.sub;
				})();
			}, {}
		],
		"const definition": [
			function() {
				let abc, cde, fgh;
				abc("test");
				cde.abc("test");
				cde.ddd.abc("test");
				fgh;
				fgh.sub;
			}, {}
		],
		"function definition": [
			function() {
				function abc() {}

				function cde() {}

				function fgh() {}
				abc("test");
				cde.abc("test");
				cde.ddd.abc("test");
				fgh;
				fgh.sub;
			}, {}
		],
		"in try": [
			function() {
				try {
					fgh.sub;
					fgh;

					function test(ttt) {
						fgh.sub;
						fgh;
					}
				} catch(e) {
					fgh.sub;
					fgh;
				}
			}, {
				fghsub: ["try", "notry", "notry"],
				fgh: ["", "test ttt", "test e"]
			}
		],
		"renaming with const": [
			function() {
				const xyz = abc;
				xyz("test");
			}, {
				abc: ["test"]
			}
		],
		"renaming with assignment": [
			function() {
				const xyz = abc;
				xyz("test");
			}, {
				abc: ["test"]
			}
		],
		"renaming with IIFE": [
			function() {
				! function(xyz) {
					xyz("test");
				}(abc);
			}, {
				abc: ["test"]
			}
		],
		"renaming with IIFE (called)": [
			function() {
				! function(xyz) {
					xyz("test");
				}.call(fgh, abc);
			}, {
				abc: ["test"],
				fgh: [""]
			}
		],
	};

	Object.keys(testCases).forEach((name) => {
		it("should parse " + name, () => {
			let source = testCases[name][0].toString();
			source = source.substr(13, source.length - 14).trim();
			const state = testCases[name][1];

			const testParser = new Parser({});
			testParser.plugin("can-rename abc", (expr) => true);
			testParser.plugin("call abc", (expr) => {
				if(!testParser.state.abc) testParser.state.abc = [];
				testParser.state.abc.push(testParser.parseString(expr.arguments[0]));
				return true;
			});
			testParser.plugin("call cde.abc", (expr) => {
				if(!testParser.state.cdeabc) testParser.state.cdeabc = [];
				testParser.state.cdeabc.push(testParser.parseString(expr.arguments[0]));
				return true;
			});
			testParser.plugin("call cde.ddd.abc", (expr) => {
				if(!testParser.state.cdedddabc) testParser.state.cdedddabc = [];
				testParser.state.cdedddabc.push(testParser.parseString(expr.arguments[0]));
				return true;
			});
			testParser.plugin("expression fgh", (expr) => {
				if(!testParser.state.fgh) testParser.state.fgh = [];
				testParser.state.fgh.push(testParser.scope.definitions.join(" "));
				return true;
			});
			testParser.plugin("expression fgh.sub", (expr) => {
				if(!testParser.state.fghsub) testParser.state.fghsub = [];
				testParser.state.fghsub.push(testParser.scope.inTry ? "try" : "notry");
				return true;
			});
			testParser.plugin("expression memberExpr", (expr) => {
				if(!testParser.state.expressions) testParser.state.expressions = [];
				testParser.state.expressions.push(expr.name);
				return true;
			});
			const actual = testParser.parse(source);
			should.strictEqual(typeof actual, "object");
			actual.should.be.eql(state);
		});
	});

	it("should parse comments", () => {
		const source = "//comment1\n/*comment2*/";
		const state = [{
			type: "Line",
			value: "comment1"
		}, {
			type: "Block",
			value: "comment2"
		}];

		const testParser = new Parser({});

		testParser.plugin("program", (ast, comments) => {
			if(!testParser.state.comments) testParser.state.comments = comments;
			return true;
		});

		const actual = testParser.parse(source);
		should.strictEqual(typeof actual, "object");
		should.strictEqual(typeof actual.comments, "object");
		actual.comments.forEach((element, index) => {
			should.strictEqual(typeof element.type, "string");
			should.strictEqual(typeof element.value, "string");
			element.type.should.be.eql(state[index].type);
			element.value.should.be.eql(state[index].value);
		});
	});

	describe("expression evaluation", () => {
		function evaluateInParser(source) {
			const parser = new Parser();
			parser.plugin("call test", (expr) => {
				parser.state.result = parser.evaluateExpression(expr.arguments[0]);
			});
			parser.plugin("evaluate Identifier aString", (expr) =>
				new BasicEvaluatedExpression().setString("aString").setRange(expr.range));
			parser.plugin("evaluate Identifier b.Number", (expr) =>
				new BasicEvaluatedExpression().setNumber(123).setRange(expr.range));
			return parser.parse("test(" + source + ");").result;
		}

		const testCases = {
			"\"strrring\"": "string=strrring",
			"\"strr\" + \"ring\"": "string=strrring",
			"\"s\" + (\"trr\" + \"rin\") + \"g\"": "string=strrring",
			"'S' + (\"strr\" + \"ring\") + 'y'": "string=Sstrrringy",
			"1": "number=1",
			"1 + 3": "number=4",
			"'pre' + a": "wrapped=['pre' string=pre]+[null]",
			"a + 'post'": "wrapped=[null]+['post' string=post]",
			"'pre' + a + 'post'": "wrapped=['pre' string=pre]+['post' string=post]",
			"1 + a + 2": "",
			"1 + a + 'post'": "wrapped=[null]+['post' string=post]",
			"'' + 1 + a + 2": "wrapped=['' + 1 string=1]+[2 string=2]",
			"'' + 1 + a + 2 + 3": "wrapped=['' + 1 string=1]+[2 + 3 string=23]",
			"'' + 1 + a + (2 + 3)": "wrapped=['' + 1 string=1]+[2 + 3 string=5]",
			"'pre' + (1 + a) + (2 + 3)": "wrapped=['pre' string=pre]+[2 + 3 string=5]",
			"a ? 'o1' : 'o2'": "options=['o1' string=o1],['o2' string=o2]",
			"a ? 'o1' : b ? 'o2' : 'o3'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3]",
			"a ? (b ? 'o1' : 'o2') : 'o3'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3]",
			"a ? (b ? 'o1' : 'o2') : c ? 'o3' : 'o4'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3],['o4' string=o4]",
			"a ? 'o1' : b ? 'o2' : c ? 'o3' : 'o4'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3],['o4' string=o4]",
			"a ? 'o1' : b ? b : c ? 'o3' : c": "options=['o1' string=o1],[b],['o3' string=o3],[c]",
			"['i1', 'i2', 3, a, b ? 4 : 5]": "items=['i1' string=i1],['i2' string=i2],[3 number=3],[a],[b ? 4 : 5 options=[4 number=4],[5 number=5]]",
			"typeof 'str'": "string=string",
			"typeof aString": "string=string",
			"typeof b.Number": "string=number",
			"typeof b['Number']": "string=number",
			"typeof b[Number]": "",
			"b.Number": "number=123",
			"b['Number']": "number=123",
			"b[Number]": "",
			"'abc'.substr(1)": "string=bc",
			"'abc'[\"substr\"](1)": "string=bc",
			"'abc'[substr](1)": "",
		};

		Object.keys(testCases).forEach((key) => {

			function evalExprToString(evalExpr) {
				if(!evalExpr) {
					return "null";
				} else {
					const result = [];
					if(evalExpr.isString()) result.push("string=" + evalExpr.string);
					if(evalExpr.isNumber()) result.push("number=" + evalExpr.number);
					if(evalExpr.isRegExp()) result.push("regExp=" + evalExpr.regExp);
					if(evalExpr.isConditional()) result.push("options=[" + evalExpr.options.map(evalExprToString).join("],[") + "]");
					if(evalExpr.isArray()) result.push("items=[" + evalExpr.items.map(evalExprToString).join("],[") + "]");
					if(evalExpr.isWrapped()) result.push("wrapped=[" + evalExprToString(evalExpr.prefix) + "]+[" + evalExprToString(evalExpr.postfix) + "]");
					if(evalExpr.range) {
						const start = evalExpr.range[0] - 5;
						const end = evalExpr.range[1] - 5;
						return key.substr(start, end - start) + (result.length > 0 ? " " + result.join(" ") : "");
					}
					return result.join(" ");
				}
			}

			it("should eval " + key, () => {
				const evalExpr = evaluateInParser(key);
				evalExprToString(evalExpr).should.be.eql(testCases[key] ? key + " " + testCases[key] : key);
			});
		});
	});

	describe("async/await support", () => {
		describe("should accept", () => {
			const cases = {
				"async function": "async function x() {}",
				"async arrow function": "async () => {}",
				"await expression": "async function x(y) { await y }"
			};
			const parser = new Parser();
			Object.keys(cases).forEach((name) => {
				const expr = cases[name];
				it(name, () => {
					const actual = parser.parse(expr);
					should.strictEqual(typeof actual, "object");
				});
			});
		});
		describe("should parse await", () => {
			const cases = {
				"require": [
					"async function x() { await require('y'); }", {
						param: "y"
					}
				],
				"System.import": [
					"async function x() { const y = await System.import('z'); }", {
						param: "z"
					}
				]
			};

			const parser = new Parser();
			parser.plugin("call require", (expr) => {
				const param = parser.evaluateExpression(expr.arguments[0]);
				parser.state.param = param.string;
			});
			parser.plugin("call System.import", (expr) => {
				const param = parser.evaluateExpression(expr.arguments[0]);
				parser.state.param = param.string;
			});

			Object.keys(cases).forEach((name) => {
				it(name, () => {
					const actual = parser.parse(cases[name][0]);
					actual.should.be.eql(cases[name][1]);
				});
			});
		});
	});
});
<commit_msg>added tests for var<commit_after>"use strict";

const should = require("should");

const Parser = require("../lib/Parser");
const BasicEvaluatedExpression = require("../lib/BasicEvaluatedExpression");

describe("Parser", () => {
	const testCases = {
		"call ident": [
			function() {
				abc("test");
			}, {
				abc: ["test"]
			}
		],
		"call member": [
			function() {
				cde.abc("membertest");
			}, {
				cdeabc: ["membertest"]
			}
		],
		"call member using bracket notation": [
			function() {
				cde["abc"]("membertest");
			}, {
				cdeabc: ["membertest"]
			}
		],
		"call inner member": [
			function() {
				cde.ddd.abc("inner");
			}, {
				cdedddabc: ["inner"]
			}
		],
		"call inner member using bracket notation": [
			function() {
				cde.ddd["abc"]("inner");
			}, {
				cdedddabc: ["inner"]
			}
		],
		"expression": [
			function() {
				fgh;
			}, {
				fgh: [""]
			}
		],
		"expression sub": [
			function() {
				fgh.sub;
			}, {
				fghsub: ["notry"]
			}
		],
		"member expression": [
			function() {
				test[memberExpr]
				test[+memberExpr]
			}, {
				expressions: ["memberExpr", "memberExpr"]
			}
		],
		"in function definition": [
			function() {
				(function(abc, cde, fgh) {
					abc("test");
					cde.abc("test");
					cde.ddd.abc("test");
					fgh;
					fgh.sub;
				})();
			}, {}
		],
		"const definition": [
			function() {
				let abc, cde, fgh;
				abc("test");
				cde.abc("test");
				cde.ddd.abc("test");
				fgh;
				fgh.sub;
			}, {}
		],
		"var definition": [
			function() {
				let abc, cde, fgh;
				abc("test");
				cde.abc("test");
				cde.ddd.abc("test");
				fgh;
				fgh.sub;
			}, {}
		],
		"function definition": [
			function() {
				function abc() {}

				function cde() {}

				function fgh() {}
				abc("test");
				cde.abc("test");
				cde.ddd.abc("test");
				fgh;
				fgh.sub;
			}, {}
		],
		"in try": [
			function() {
				try {
					fgh.sub;
					fgh;

					function test(ttt) {
						fgh.sub;
						fgh;
					}
				} catch(e) {
					fgh.sub;
					fgh;
				}
			}, {
				fghsub: ["try", "notry", "notry"],
				fgh: ["", "test ttt", "test e"]
			}
		],
		"renaming with const": [
			function() {
				const xyz = abc;
				xyz("test");
			}, {
				abc: ["test"]
			}
		],
		"renaming with var": [
			function() {
				const xyz = abc;
				xyz("test");
			}, {
				abc: ["test"]
			}
		],
		"renaming with assignment": [
			function() {
				const xyz = abc;
				xyz("test");
			}, {
				abc: ["test"]
			}
		],
		"renaming with IIFE": [
			function() {
				! function(xyz) {
					xyz("test");
				}(abc);
			}, {
				abc: ["test"]
			}
		],
		"renaming with IIFE (called)": [
			function() {
				! function(xyz) {
					xyz("test");
				}.call(fgh, abc);
			}, {
				abc: ["test"],
				fgh: [""]
			}
		],
	};

	Object.keys(testCases).forEach((name) => {
		it("should parse " + name, () => {
			let source = testCases[name][0].toString();
			source = source.substr(13, source.length - 14).trim();
			const state = testCases[name][1];

			const testParser = new Parser({});
			testParser.plugin("can-rename abc", (expr) => true);
			testParser.plugin("call abc", (expr) => {
				if(!testParser.state.abc) testParser.state.abc = [];
				testParser.state.abc.push(testParser.parseString(expr.arguments[0]));
				return true;
			});
			testParser.plugin("call cde.abc", (expr) => {
				if(!testParser.state.cdeabc) testParser.state.cdeabc = [];
				testParser.state.cdeabc.push(testParser.parseString(expr.arguments[0]));
				return true;
			});
			testParser.plugin("call cde.ddd.abc", (expr) => {
				if(!testParser.state.cdedddabc) testParser.state.cdedddabc = [];
				testParser.state.cdedddabc.push(testParser.parseString(expr.arguments[0]));
				return true;
			});
			testParser.plugin("expression fgh", (expr) => {
				if(!testParser.state.fgh) testParser.state.fgh = [];
				testParser.state.fgh.push(testParser.scope.definitions.join(" "));
				return true;
			});
			testParser.plugin("expression fgh.sub", (expr) => {
				if(!testParser.state.fghsub) testParser.state.fghsub = [];
				testParser.state.fghsub.push(testParser.scope.inTry ? "try" : "notry");
				return true;
			});
			testParser.plugin("expression memberExpr", (expr) => {
				if(!testParser.state.expressions) testParser.state.expressions = [];
				testParser.state.expressions.push(expr.name);
				return true;
			});
			const actual = testParser.parse(source);
			should.strictEqual(typeof actual, "object");
			actual.should.be.eql(state);
		});
	});

	it("should parse comments", () => {
		const source = "//comment1\n/*comment2*/";
		const state = [{
			type: "Line",
			value: "comment1"
		}, {
			type: "Block",
			value: "comment2"
		}];

		const testParser = new Parser({});

		testParser.plugin("program", (ast, comments) => {
			if(!testParser.state.comments) testParser.state.comments = comments;
			return true;
		});

		const actual = testParser.parse(source);
		should.strictEqual(typeof actual, "object");
		should.strictEqual(typeof actual.comments, "object");
		actual.comments.forEach((element, index) => {
			should.strictEqual(typeof element.type, "string");
			should.strictEqual(typeof element.value, "string");
			element.type.should.be.eql(state[index].type);
			element.value.should.be.eql(state[index].value);
		});
	});

	describe("expression evaluation", () => {
		function evaluateInParser(source) {
			const parser = new Parser();
			parser.plugin("call test", (expr) => {
				parser.state.result = parser.evaluateExpression(expr.arguments[0]);
			});
			parser.plugin("evaluate Identifier aString", (expr) =>
				new BasicEvaluatedExpression().setString("aString").setRange(expr.range));
			parser.plugin("evaluate Identifier b.Number", (expr) =>
				new BasicEvaluatedExpression().setNumber(123).setRange(expr.range));
			return parser.parse("test(" + source + ");").result;
		}

		const testCases = {
			"\"strrring\"": "string=strrring",
			"\"strr\" + \"ring\"": "string=strrring",
			"\"s\" + (\"trr\" + \"rin\") + \"g\"": "string=strrring",
			"'S' + (\"strr\" + \"ring\") + 'y'": "string=Sstrrringy",
			"1": "number=1",
			"1 + 3": "number=4",
			"'pre' + a": "wrapped=['pre' string=pre]+[null]",
			"a + 'post'": "wrapped=[null]+['post' string=post]",
			"'pre' + a + 'post'": "wrapped=['pre' string=pre]+['post' string=post]",
			"1 + a + 2": "",
			"1 + a + 'post'": "wrapped=[null]+['post' string=post]",
			"'' + 1 + a + 2": "wrapped=['' + 1 string=1]+[2 string=2]",
			"'' + 1 + a + 2 + 3": "wrapped=['' + 1 string=1]+[2 + 3 string=23]",
			"'' + 1 + a + (2 + 3)": "wrapped=['' + 1 string=1]+[2 + 3 string=5]",
			"'pre' + (1 + a) + (2 + 3)": "wrapped=['pre' string=pre]+[2 + 3 string=5]",
			"a ? 'o1' : 'o2'": "options=['o1' string=o1],['o2' string=o2]",
			"a ? 'o1' : b ? 'o2' : 'o3'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3]",
			"a ? (b ? 'o1' : 'o2') : 'o3'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3]",
			"a ? (b ? 'o1' : 'o2') : c ? 'o3' : 'o4'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3],['o4' string=o4]",
			"a ? 'o1' : b ? 'o2' : c ? 'o3' : 'o4'": "options=['o1' string=o1],['o2' string=o2],['o3' string=o3],['o4' string=o4]",
			"a ? 'o1' : b ? b : c ? 'o3' : c": "options=['o1' string=o1],[b],['o3' string=o3],[c]",
			"['i1', 'i2', 3, a, b ? 4 : 5]": "items=['i1' string=i1],['i2' string=i2],[3 number=3],[a],[b ? 4 : 5 options=[4 number=4],[5 number=5]]",
			"typeof 'str'": "string=string",
			"typeof aString": "string=string",
			"typeof b.Number": "string=number",
			"typeof b['Number']": "string=number",
			"typeof b[Number]": "",
			"b.Number": "number=123",
			"b['Number']": "number=123",
			"b[Number]": "",
			"'abc'.substr(1)": "string=bc",
			"'abc'[\"substr\"](1)": "string=bc",
			"'abc'[substr](1)": "",
		};

		Object.keys(testCases).forEach((key) => {

			function evalExprToString(evalExpr) {
				if(!evalExpr) {
					return "null";
				} else {
					const result = [];
					if(evalExpr.isString()) result.push("string=" + evalExpr.string);
					if(evalExpr.isNumber()) result.push("number=" + evalExpr.number);
					if(evalExpr.isRegExp()) result.push("regExp=" + evalExpr.regExp);
					if(evalExpr.isConditional()) result.push("options=[" + evalExpr.options.map(evalExprToString).join("],[") + "]");
					if(evalExpr.isArray()) result.push("items=[" + evalExpr.items.map(evalExprToString).join("],[") + "]");
					if(evalExpr.isWrapped()) result.push("wrapped=[" + evalExprToString(evalExpr.prefix) + "]+[" + evalExprToString(evalExpr.postfix) + "]");
					if(evalExpr.range) {
						const start = evalExpr.range[0] - 5;
						const end = evalExpr.range[1] - 5;
						return key.substr(start, end - start) + (result.length > 0 ? " " + result.join(" ") : "");
					}
					return result.join(" ");
				}
			}

			it("should eval " + key, () => {
				const evalExpr = evaluateInParser(key);
				evalExprToString(evalExpr).should.be.eql(testCases[key] ? key + " " + testCases[key] : key);
			});
		});
	});

	describe("async/await support", () => {
		describe("should accept", () => {
			const cases = {
				"async function": "async function x() {}",
				"async arrow function": "async () => {}",
				"await expression": "async function x(y) { await y }"
			};
			const parser = new Parser();
			Object.keys(cases).forEach((name) => {
				const expr = cases[name];
				it(name, () => {
					const actual = parser.parse(expr);
					should.strictEqual(typeof actual, "object");
				});
			});
		});
		describe("should parse await", () => {
			const cases = {
				"require": [
					"async function x() { await require('y'); }", {
						param: "y"
					}
				],
				"System.import": [
					"async function x() { const y = await System.import('z'); }", {
						param: "z"
					}
				]
			};

			const parser = new Parser();
			parser.plugin("call require", (expr) => {
				const param = parser.evaluateExpression(expr.arguments[0]);
				parser.state.param = param.string;
			});
			parser.plugin("call System.import", (expr) => {
				const param = parser.evaluateExpression(expr.arguments[0]);
				parser.state.param = param.string;
			});

			Object.keys(cases).forEach((name) => {
				it(name, () => {
					const actual = parser.parse(cases[name][0]);
					actual.should.be.eql(cases[name][1]);
				});
			});
		});
	});
});
<|endoftext|>


<commit_before>/**
 *  https://github.com/PowerKiKi/ie_expand_select_width
 */
(function( $ ){

	$.fn.ieExpandSelectWidth = function() {
		this.filter('select')
		.bind('mouseenter focus',function(event){
			open($(this), event.type == 'mouseenter');
		});

		return this;
	};

	/**
	 * Open the expanded select
	 * @param select jQuery object for the original select element
	 */
	function open(select, openedViaMouse)
	{
		// Allow only one select to be opened at any given time
		// and only select in 'single choice' mode
		if ($.data(document.body, 'ie_expand_select_width_lock')
			|| select.is('.ie_expand_select_width')
			|| select.attr('multiple')
			|| select.attr('size') > 1)
		{
			return;
		}
		$.data(document.body, 'ie_expand_select_width_lock', true);

		// Clone the select to keep the layout intact
		var selectClone = select.clone();
		selectClone.val(select.val());

		var style = getComputedStyleMap(select);
		style['min-width'] = select.width(); // Cannot be shorter than current width
		style['width'] = 'auto';
		style['z-index'] = 9999; // be sure it's always on top of everything else
		selectClone.css(style);

		// Insert the clone at the very end of document, so it does not break layout
		selectClone.appendTo('body');

		// Move the clone as an overlay on top of the original
		reposition(select, selectClone);

		if (!openedViaMouse)
		{
			selectClone.focus();
		}

		// Bind events to close
		selectClone
		.bind('keydown keyup', function(event){
			selectClone.data('ie_expand_select_width_key_is_down', event.type == 'keydown');
		})
		.bind('mousedown mouseup', function(event){
			selectClone.data('ie_expand_select_width_mouse_is_down', event.type == 'mousedown');
		})
		.bind('blur', function(){
			close(select, selectClone);
		})
		.bind('change', function(){
			// Only close if the change was made via mouse
			if (!selectClone.data('ie_expand_select_width_key_is_down'))
				close(select, selectClone);
		});

		// Only close if we are doing a simple hover and not an a choice in a expanded select
		if (openedViaMouse)
		{
			selectClone.bind('mouseleave', function(){
				if (!selectClone.is(':focus'))
					close(select, selectClone);
			});
		}

		$(window).bind('resize.ie_expand_select_width', function() { reposition(select, selectClone); });

		$.data(document.body, 'ie_expand_select_width_lock', false);
	}

	/**
	 * Close the expanded select
	 * @param select jQuery object for the original select element
	 */
	function close(select, selectClone)
	{
		if (!selectClone || $.data(document.body, 'ie_expand_select_width_lock'))
		{
			return;
		}

		select.removeClass('ie_expand_select_width');
		select.val(selectClone.val()).change();
		selectClone.remove();
		select.data('ie_expand_select_width_clone', null);

		$(window).unbind('resize.ie_expand_select_width');
	}

	/**
	 * Reposition overlays on top of their clones
	 */
	function reposition(select, selectClone)
	{
		// Move the clone as an overlay on top of the original
		selectClone.position({
			my : 'left',
			at : 'left',
			of : select,
			collision: 'none'
		});
	}

	/**
	 * Returns a map of computed CSS style for the fiven element
	 * Highly inspired from http://stackoverflow.com/a/6416477/37706
	 */
	function getComputedStyleMap(element) {
		var dom = element.get(0);
		var style;
		var result = {};
		if (window.getComputedStyle) {
			style = window.getComputedStyle(dom, null);
			for (var i = 0; i < style.length; i++) {
				var prop = style[i];
				result[prop] = style.getPropertyValue(prop);
			}
		}
		else if (dom.currentStyle) {
			style = dom.currentStyle;
			for(var prop in style) {
				result[prop] = style[prop];
			}
		}

		return result;
	}

})( jQuery );
<commit_msg>Only raise change() event if the value actually changed<commit_after>/**
 *  https://github.com/PowerKiKi/ie_expand_select_width
 */
(function( $ ){

	$.fn.ieExpandSelectWidth = function() {
		this.filter('select')
		.bind('mouseenter focus',function(event){
			open($(this), event.type == 'mouseenter');
		});

		return this;
	};

	/**
	 * Open the expanded select
	 * @param select jQuery object for the original select element
	 */
	function open(select, openedViaMouse)
	{
		// Allow only one select to be opened at any given time
		// and only select in 'single choice' mode
		if ($.data(document.body, 'ie_expand_select_width_lock')
			|| select.is('.ie_expand_select_width')
			|| select.attr('multiple')
			|| select.attr('size') > 1)
		{
			return;
		}
		$.data(document.body, 'ie_expand_select_width_lock', true);

		// Clone the select to keep the layout intact
		var selectClone = select.clone();
		selectClone.val(select.val());

		var style = getComputedStyleMap(select);
		style['min-width'] = select.width(); // Cannot be shorter than current width
		style['width'] = 'auto';
		style['z-index'] = 9999; // be sure it's always on top of everything else
		selectClone.css(style);

		// Insert the clone at the very end of document, so it does not break layout
		selectClone.appendTo('body');

		// Move the clone as an overlay on top of the original
		reposition(select, selectClone);

		if (!openedViaMouse)
		{
			selectClone.focus();
		}

		// Bind events to close
		selectClone
		.bind('keydown keyup', function(event){
			selectClone.data('ie_expand_select_width_key_is_down', event.type == 'keydown');
		})
		.bind('mousedown mouseup', function(event){
			selectClone.data('ie_expand_select_width_mouse_is_down', event.type == 'mousedown');
		})
		.bind('blur', function(){
			close(select, selectClone);
		})
		.bind('change', function(){
			// Only close if the change was made via mouse
			if (!selectClone.data('ie_expand_select_width_key_is_down'))
				close(select, selectClone);
		});

		// Only close if we are doing a simple hover and not an a choice in a expanded select
		if (openedViaMouse)
		{
			selectClone.bind('mouseleave', function() {
				if (!selectClone.is(':focus'))
					close(select, selectClone);
			});
		}

		$(window).bind('resize.ie_expand_select_width', function() { reposition(select, selectClone); });

		$.data(document.body, 'ie_expand_select_width_lock', false);
	}

	/**
	 * Close the expanded select
	 * @param select jQuery object for the original select element
	 */
	function close(select, selectClone)
	{
		if (!selectClone || $.data(document.body, 'ie_expand_select_width_lock'))
		{
			return;
		}

		select.removeClass('ie_expand_select_width');

		// Update value if different
		var cloneValue = selectClone.val();
		if (cloneValue != select.val())
			select.val(cloneValue).change();

		selectClone.remove();
		select.data('ie_expand_select_width_clone', null);

		$(window).unbind('resize.ie_expand_select_width');
	}

	/**
	 * Reposition overlays on top of their clones
	 */
	function reposition(select, selectClone)
	{
		// Move the clone as an overlay on top of the original
		selectClone.position({
			my : 'left',
			at : 'left',
			of : select,
			collision: 'none'
		});
	}

	/**
	 * Returns a map of computed CSS style for the fiven element
	 * Highly inspired from http://stackoverflow.com/a/6416477/37706
	 */
	function getComputedStyleMap(element) {
		var dom = element.get(0);
		var style;
		var result = {};
		if (window.getComputedStyle) {
			style = window.getComputedStyle(dom, null);
			for (var i = 0; i < style.length; i++) {
				var prop = style[i];
				result[prop] = style.getPropertyValue(prop);
			}
		}
		else if (dom.currentStyle) {
			style = dom.currentStyle;
			for(var prop in style) {
				result[prop] = style[prop];
			}
		}

		return result;
	}

})( jQuery );
<|endoftext|>


<commit_before>// See if we want to provide multi-language org src samples.
let lang = 'php'
const srcNodes = () => document.querySelectorAll('.src')

const filterSrcNodes = () => {
  const re = new RegExp(lang, 'gi')
  const xs = srcNodes()
  const x = xs.length
  const unmatch = lang === 'all' ? 'block' : 'none'

  for (let i = 0; i < x; i++) {
    const s = xs[i].getAttribute('class')
    if (!re.test(s)) {
      xs[i].style.display = unmatch
    } else {
      xs[i].style.display = 'block'
    }
  }
}

function changeLang (e) {
  lang = e.target.value
  filterSrcNodes()
}

function removeItalics () {
  const nodes = document.querySelectorAll('span')
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].innerHTML === '(' ||
      nodes[i].innerHTML === ')' ||
      nodes[i].innerHTML === '[]' ||
      nodes[i].innerHTML === '()' ||
      nodes[i].innerHTML === '{}' ||
      nodes[i].innerHTML === '[' ||
      nodes[i].innerHTML === ']' ||
      nodes[i].innerHTML === '{' ||
      nodes[i].innerHTML === '}') {
      nodes[i].style.fontStyle = 'normal'
      nodes[i].style.fontWeight = 'normal'
      nodes[i].style.marginLeft = '2px'
      nodes[i].style.marginRight = '2px'
      nodes[i].style.opacity = '0.8'
    }
  }
}

// Hmm...what does this do, I wonder...
function cleanUp () {
  const nodes = document.querySelectorAll('iframe[sandbox]')

  for (let i = nodes.length - 1; i > -1; i--) {
    nodes[i].remove()
  }
}

function newTargetLinks () {
  const nodes = document.querySelectorAll('a')

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

    if (/^http/.test(x.getAttribute('href')))
      nodes[i].setAttribute('target', '_blank')
  }
}

window.onload = () => {
  const maybeChangeLang = document.getElementById('change-lang')
  if (maybeChangeLang) {
    document.getElementById('change-lang').addEventListener('change', changeLang)
  }
  removeItalics()
  setInterval(cleanUp, 1e3)
  newTargetLinks()
}

console.log('fin')
<commit_msg>Fix unneeded hack<commit_after>// See if we want to provide multi-language org src samples.
let lang = 'php'
const srcNodes = () => document.querySelectorAll('.src')

const filterSrcNodes = () => {
  const re = new RegExp(lang, 'gi')
  const xs = srcNodes()
  const x = xs.length
  const unmatch = lang === 'all' ? 'block' : 'none'

  for (let i = 0; i < x; i++) {
    const s = xs[i].getAttribute('class')
    if (!re.test(s)) {
      xs[i].style.display = unmatch
    } else {
      xs[i].style.display = 'block'
    }
  }
}

function changeLang (e) {
  lang = e.target.value
  filterSrcNodes()
}

function removeItalics () {
  const nodes = document.querySelectorAll('span')
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].innerHTML === '(' ||
      nodes[i].innerHTML === ')' ||
      nodes[i].innerHTML === '[]' ||
      nodes[i].innerHTML === '()' ||
      nodes[i].innerHTML === '{}' ||
      nodes[i].innerHTML === '[' ||
      nodes[i].innerHTML === ']' ||
      nodes[i].innerHTML === '{' ||
      nodes[i].innerHTML === '}') {
      nodes[i].style.fontStyle = 'normal'
      nodes[i].style.fontWeight = 'normal'
      nodes[i].style.marginLeft = '2px'
      nodes[i].style.marginRight = '2px'
      nodes[i].style.opacity = '0.8'
    }
  }
}

// Hmm...what does this do, I wonder...
// function cleanUp () {
//   const nodes = document.querySelectorAll('iframe[sandbox]')

//   for (let i = nodes.length - 1; i > -1; i--) {
//     nodes[i].remove()
//   }
// }

function newTargetLinks () {
  const nodes = document.querySelectorAll('a')

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

    if (/^http/.test(x.getAttribute('href')))
      nodes[i].setAttribute('target', '_blank')
  }
}

window.onload = () => {
  const maybeChangeLang = document.getElementById('change-lang')
  if (maybeChangeLang) {
    document.getElementById('change-lang').addEventListener('change', changeLang)
  }
  removeItalics()
  // setInterval(cleanUp, 1e3)
  newTargetLinks()
}

console.log('fin')
<|endoftext|>


<commit_before>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    var self = this;
    Events.call(self, config);

    self.updateLinks = function () {
        self.emit("getUserInfo", function (err, data) {
            debugger;
        });
    };
};
<commit_msg>Show/remove elements with class that represent the roles.<commit_after>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    var self = this;
    Events.call(self, config);

    for (var i in config.roles) {
        $("." + config.roles[i]).hide();
    }

    self.updateLinks = function (data) {
        data = data || {};
        data.role = data.role || config.publicRole;

        $("." + data.role).fadeIn();
        var index = config.roles.indexOf(data.role);

        config.roles.slice(index, index + 1);
        for (var i in config.roles) {
            $("." + config.roles[i]).remove();
        }
    };
};
<|endoftext|>


<commit_before>import React from 'react-native';
import Cell from './Cell';
const {
  StyleSheet,
  View,
  Component,
} = React;

class Row extends Component {
	render(){
		return (
			<View style={[styles.row, this.props.style]}>
				{this.props.children}
			</View>
		)
	};
}

class Grid extends Component {
  constructor(props) {
    super(props)
    this.state = {
      columnWidths: [],
      gridRows: [],
      viewWidth: null,
    };

    this.temporaryColumnWidths = [];
    this.checkedAllCells = [];
    this.numCellsChecked = 0;
    this.finalizedColumns = false;

    this.receiveDimensionsFromCells = this.receiveDimensionsFromCells.bind(this);
    this.renderIndividualCell = this.renderIndividualCell.bind(this);
    this.finalizeColumnWidths = this.finalizeColumnWidths.bind(this);
  }

  componentWillMount() {
    let self = this;
    let childCellData = this.props.allCells;

    let row = [];
    let rows = [];

    function isFullRow(){

      let span = 0;
      row.map(function(cell){
        span += cell.colSpan;
      })

      if(span===24) return true;
      return false;
    }

    childCellData.map(function(child, index){
      row.push(child);

      if(isFullRow()){
        rows.push(row);
        row = [];
      }
    });

    var tempIndex = 0;
    while(tempIndex < this.props.numColumns) {
      this.temporaryColumnWidths.push(null);
      tempIndex++;
    }

    this.props.allCells.map((cell) => {
      this.checkedAllCells.push(false);
    })

    this.setState({
      gridRows: rows,
      columnWidths: this.temporaryColumnWidths,
     });
  }

  receiveDimensionsFromCells(width,height,index,row,column) {
    //collect and check column widths
    if(this.finalizedColumns === false) {
      if(this.temporaryColumnWidths[column-1] === null) { //zero-indexed
        this.temporaryColumnWidths[column-1] = width;
      }
      else if(this.temporaryColumnWidths[column-1] < width) {
        this.temporaryColumnWidths[column-1] = width;
      }

      if(this.checkedAllCells[index] === false) {
        this.checkedAllCells[index] = true;
        this.numCellsChecked++;
      }

      if(this.numCellsChecked === this.props.allCells.length) {
        this.finalizedColumns = true;
        this.finalizeColumnWidths();
        this.setState({
          columnWidths: this.temporaryColumnWidths,
        });

        return;
      }
    }
  }

  finalizeColumnWidths() {
    var viewWidth = this.state.viewWidth;
    if(viewWidth === null) {
      console.log("error, this should not happen, viewWidth can't be null at this point.");
      return;
    }

    var totalWidth = 0;
    this.temporaryColumnWidths = this.temporaryColumnWidths.map((width) => {
      width = width + 2; //make it 2 pixels wider
      totalWidth += width;
      return width;
    });

    if(totalWidth <= viewWidth) {
      return;
    }

    //reduce the widest column
    var maxValueSoFar = 0;
    var maxIndex = -1;
    this.temporaryColumnWidths.map((width,index) => {
      if(width > maxValueSoFar) {
        maxValueSoFar = width;
        maxIndex = index;
      }
    })
    this.temporaryColumnWidths[maxIndex] = this.temporaryColumnWidths[maxIndex] - (totalWidth - viewWidth);
    return;

  }

  renderIndividualCell(cell) {
    return (
      <Cell key={cell.text + String(cell.index)}
            colIndex={cell.colIndex}
            rowIndex={cell.rowIndex}
            cellIndex={cell.index}
            span={cell.colSpan}
            gridCallback={this.receiveDimensionsFromCells}
            style={{ padding: 5 }}
            columnWidths={this.state.columnWidths}
            content={cell.content} /> );
  }

	render() {
    let self = this;

		let GridComponent = self.state.gridRows.map((row,index) => {
			let content = row.map((cell) => {return (self.renderIndividualCell(cell)) });

      return (
				<Row  key={index}
              style={[styles.row, self.props.style, {
                borderTopWidth: 0,
                borderBottomWidth: 1,
                borderColor: 'gray',
              }]} >
					{content}
				</Row>
			)
		});

		if(GridComponent.length>1){
			return (
				<View
          onLayout={(event) => {
            var {x, y, width, height} = event.nativeEvent.layout;

            if(this.state.viewWidth !== width) {
              console.log('width: ' + width);
              console.log('height: ' + height);

              this.setState({
                viewWidth: width,
              });
            }
          }}
          style={[this.props.style, {flex: 1}]}>
            <View style={{alignSelf: 'center'}}>
  					  {GridComponent}
            </View>
				</View>
			);
		}else if(GridComponent.length===1){
			return GridComponent[0];
		}else{
			return null;
		}
	}
}

const styles = StyleSheet.create({
	row: {
		flexDirection: 'row'
	}
});

module.exports = Grid
<commit_msg>removing grid.js with lowercase g<commit_after><|endoftext|>


<commit_before>module('lively.ast.StackReification').requires('lively.ast.AcornInterpreter').toRun(function() {

lively.Closure.subclass('lively.ast.RewrittenClosure',
'initializing', {

    initialize: function($super, func, varMapping, source) {
        $super(func, varMapping, source);
        this.ast = null;
    }

},
'accessing', {

    getRewrittenFunc: function() {
        var func = this.recreateFuncFromSource(this.getRewrittenSource());
        func.livelyDebuggingEnabled = true;
        return func;
    },

    getRewrittenSource: function() {
        return this.ast && escodegen.generate(this.ast);
    },

    getOriginalFunc: function() {
        return this.addClosureInformation(this.getFunc());
    }

},
'rewriting', {

    rewrite: function(astRegistry) {
        var src = this.getFuncSource(),
            ast = lively.ast.acorn.parseFunction(src);
        return this.ast = lively.ast.Rewriting.rewriteFunction(ast, astRegistry);
    }

});

Object.extend(lively.ast.StackReification, {

    debugReplacements: {
        Function: {
            bind: {},
            call: {},
            applyt: {}
        },
        Array: {
            sort: {
                dbg: NativeArrayFunctions.sort
            },
            filter: {
                dbg: NativeArrayFunctions.filter
            },
            forEach: {
                dbg: NativeArrayFunctions.forEach
            },
            some: {
                dbg: NativeArrayFunctions.some
            },
            every: {
                dbg: NativeArrayFunctions.every
            },
            map: {
                dbg: NativeArrayFunctions.map
            },
            reduce: {
                dbg: NativeArrayFunctions.reduce
            },
            reduceRight: {
                dbg: NativeArrayFunctions.reduceRight
            }
        },
        String: {
            // TODO: second parameter can be function (replaceValue)
            replace: {}
        },
        Array: {
            // TODO: second parameter can be function (replacer)
            stringify: {}
        }
    },

    enableDebugSupport: function(astRegistry) {
        // FIXME currently only takes care of Array
        try {
            var replacements = lively.ast.StackReification.debugReplacements;
            for (var method in replacements.Array) {
                if (!replacements.Array.hasOwnProperty(method)) continue;
                var spec = replacements.Array[method],
                    dbgVersion = spec.dbg.stackCaptureMode(null, astRegistry);
                if (!spec.original) spec.original = Array.prototype[method];
                Array.prototype[method] = dbgVersion;
            }
        } catch(e) {
            this.disableDebugSupport();
            throw e;
        }
    },

    disableDebugSupport: function() {
        var replacements = lively.ast.StackReification.debugReplacements;
        for (var method in replacements.Array) {
            var spec = replacements.Array[method],
                original = spec.original || Array.prototype[method];
            Array.prototype[method] = original;
        }
    },

    halt: function() {
        var frame = lively.ast.Interpreter.Frame.create();
        throw {isUnwindException: true, lastFrame: frame, topFrame: frame}
    },

    run: function(func, astRegistry, optMapping) {
        lively.ast.StackReification.enableDebugSupport(astRegistry);
        if (!func.livelyDebuggingEnabled)
            func = func.stackCaptureMode(optMapping, astRegistry);
        try {
            return {isContinuation: false, returnValue: func()};
        } catch (e) {
            // e will always be an UnwindException
            if (e.error instanceof Error)
                throw e.error;
            else
                return lively.ast.Continuation.fromUnwindException(e);
        } finally {
            lively.ast.StackReification.disableDebugSupport(astRegistry);
        }
    }

});

Object.extend(Global, {
    catchUnwind: lively.ast.StackReification.run,
    halt: lively.ast.StackReification.halt,
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    __createClosure: function(idx, f) {
        f._cachedAst = lively.ast.Rewriting.getCurrentASTRegistry()[idx];
        f.livelyDebuggingEnabled = true;
        return f;
    },

    __getClosure: function(idx) {
        return lively.ast.Rewriting.getCurrentASTRegistry()[idx];
    }
});

Object.extend(Function.prototype, {

    asRewrittenClosure: function(varMapping, astRegistry) {
        var closure = new lively.ast.RewrittenClosure(this, varMapping);
        closure.rewrite(astRegistry);
        return closure;
    },

    stackCaptureMode: function(varMapping, astRegistry) {
        var closure = this.asRewrittenClosure(varMapping, astRegistry),
            rewrittenFunc = closure.getRewrittenFunc();
        if (!rewrittenFunc) throw new Error('Cannot rewrite ' + this);
        return rewrittenFunc;
    },

    stackCaptureSource: function(varMapping, astRegistry) {
        return this.asRewrittenClosure(astRegistry).getRewrittenSource();
    }

});

Object.subclass('lively.ast.Continuation',
'settings', {
    isContinuation: true
},
'initializing', {
    initialize: function(frame) {
        this.currentFrame = frame; // the frame in which the the unwind was triggered
    },

    copy: function() {
        return new this.constructor(this.currentFrame.copy());
    }
},
'accessing', {
    frames: function() {
        var frame = this.currentFrame, result = [];
        do { result.push(frame); } while (frame = frame.getParentFrame());
        return result;
    }
},
'resuming', {
    resume: function() {
        // FIXME: outer context usually does not have original AST
        // attaching the program node would possibly be right (otherwise the pc's context is missing)
        if (!this.currentFrame.getOriginalAst())
            throw new Error('Cannot resume because frame has no AST!');
        if (!this.currentFrame.pc)
            throw new Error('Cannot resume because frame has no pc!');

        var interpreter = new lively.ast.AcornInterpreter.Interpreter();

        // go through all frames on the stack. beginning with the top most,
        // resume each of them
        var result = this.frames().reduce(function(result, frame, i) {
            if (result.error) {
                result.error.shiftFrame(frame);
                return result;
            }

            frame.alreadyComputed[frame.pc.astIndex] = result.val;

            // FIXME frames hold on to function ASTs but resuming from a
            // function is not supported right now. So we set the resumable
            // node to the functions body here as a quick fix
            var resumeNode = frame.getOriginalAst().body;
            try {
                return { val: interpreter.runWithFrameAndResult(resumeNode, frame, result.val) };
            } catch (ex) {
                if (!ex.isUnwindException)
                    throw ex;

                // FIXME: adjust already computed!
                // frame.setAlreadyComputed(alreadyComputed);
                var pc = acorn.walk.findNodeByAstIndex(frame.getOriginalAst(), ex.error.astIndex);
                frame.setPC(pc);
                ex.shiftFrame(frame);
                return { error: ex };
            }
        }, {});

        if (result.error)
            return lively.ast.Continuation.fromUnwindException(result.error);
        else
            return result.val;
    }
});

Object.extend(lively.ast.Continuation, {
    fromUnwindException: function(e) {
        if (!e.isUnwindException) console.error("No unwind exception?");
        return new this(e.top);
    }
});

Object.subclass('lively.ast.Rewriting.UnwindException',
'settings', {

    isUnwindException: true

},
'initializing', {

    initialize: function(error) {
        this.error = error;
    }

},
'printing', {

    toString: function() {
        return '[UNWIND] ' + this.error.toString();
    }

},
'frames', {

    createAndShiftFrame: function(thiz, frameState, lastNodeAstIndex, pointerToOriginalAst) {
        var alreadyComputed = frameState[0],
            // varMapping = frameState[1],
            parentFrameState = frameState[2],
            frame = lively.ast.AcornInterpreter.Frame.create(
                __getClosure(pointerToOriginalAst) /*, varMapping */),
            pc;
        frame.setThis(thiz);
        frame.setAlreadyComputed(alreadyComputed);
        if (!this.top) {
            pc = this.error && this.error.astIndex ?
                acorn.walk.findNodeByAstIndex(frame.getOriginalAst(), this.error.astIndex) :
                null;
        } else {
            if (frame.isAlreadyComputed(lastNodeAstIndex)) lastNodeAstIndex++;
            pc = acorn.walk.findNodeByAstIndex(frame.getOriginalAst(), lastNodeAstIndex);
        }
        frame.setPC(pc);

        var scope, topScope, newScope,
            fState = frameState;
        do {
            newScope = new lively.ast.AcornInterpreter.Scope(fState[1]); // varMapping
            if (scope)
                scope.setParentScope(newScope);
            else
                topScope = newScope;
            scope = newScope
            fState = fState[2]; // parentFrameState
        } while (fState && fState != Global);
        frame.setScope(topScope);

        return this.shiftFrame(frame);
    },

    shiftFrame: function(frame) {
        if (!this.top) {
            this.top = this.last = frame;
        } else {
            this.last.setParentFrame(frame);
            this.last = frame;
        }
        return frame;
    }

});

}) // end of module
<commit_msg>debugger: Fixing name<commit_after>module('lively.ast.StackReification').requires('lively.ast.AcornInterpreter').toRun(function() {

lively.Closure.subclass('lively.ast.RewrittenClosure',
'initializing', {

    initialize: function($super, func, varMapping, source) {
        $super(func, varMapping, source);
        this.ast = null;
    }

},
'accessing', {

    getRewrittenFunc: function() {
        var func = this.recreateFuncFromSource(this.getRewrittenSource());
        func.livelyDebuggingEnabled = true;
        return func;
    },

    getRewrittenSource: function() {
        return this.ast && escodegen.generate(this.ast);
    },

    getOriginalFunc: function() {
        return this.addClosureInformation(this.getFunc());
    }

},
'rewriting', {

    rewrite: function(astRegistry) {
        var src = this.getFuncSource(),
            ast = lively.ast.acorn.parseFunction(src);
        return this.ast = lively.ast.Rewriting.rewriteFunction(ast, astRegistry);
    }

});

Object.extend(lively.ast.StackReification, {

    debugReplacements: {
        Function: {
            bind: {},
            call: {},
            applyt: {}
        },
        Array: {
            sort: {
                dbg: NativeArrayFunctions.sort
            },
            filter: {
                dbg: NativeArrayFunctions.filter
            },
            forEach: {
                dbg: NativeArrayFunctions.forEach
            },
            some: {
                dbg: NativeArrayFunctions.some
            },
            every: {
                dbg: NativeArrayFunctions.every
            },
            map: {
                dbg: NativeArrayFunctions.map
            },
            reduce: {
                dbg: NativeArrayFunctions.reduce
            },
            reduceRight: {
                dbg: NativeArrayFunctions.reduceRight
            }
        },
        String: {
            // TODO: second parameter can be function (replaceValue)
            replace: {}
        },
        JSON: {
            // TODO: second parameter can be function (replacer)
            stringify: {}
        }
    },

    enableDebugSupport: function(astRegistry) {
        // FIXME currently only takes care of Array
        try {
            var replacements = lively.ast.StackReification.debugReplacements;
            for (var method in replacements.Array) {
                if (!replacements.Array.hasOwnProperty(method)) continue;
                var spec = replacements.Array[method],
                    dbgVersion = spec.dbg.stackCaptureMode(null, astRegistry);
                if (!spec.original) spec.original = Array.prototype[method];
                Array.prototype[method] = dbgVersion;
            }
        } catch(e) {
            this.disableDebugSupport();
            throw e;
        }
    },

    disableDebugSupport: function() {
        var replacements = lively.ast.StackReification.debugReplacements;
        for (var method in replacements.Array) {
            var spec = replacements.Array[method],
                original = spec.original || Array.prototype[method];
            Array.prototype[method] = original;
        }
    },

    halt: function() {
        var frame = lively.ast.Interpreter.Frame.create();
        throw {isUnwindException: true, lastFrame: frame, topFrame: frame}
    },

    run: function(func, astRegistry, optMapping) {
        lively.ast.StackReification.enableDebugSupport(astRegistry);
        if (!func.livelyDebuggingEnabled)
            func = func.stackCaptureMode(optMapping, astRegistry);
        try {
            return {isContinuation: false, returnValue: func()};
        } catch (e) {
            // e will always be an UnwindException
            if (e.error instanceof Error)
                throw e.error;
            else
                return lively.ast.Continuation.fromUnwindException(e);
        } finally {
            lively.ast.StackReification.disableDebugSupport(astRegistry);
        }
    }

});

Object.extend(Global, {
    catchUnwind: lively.ast.StackReification.run,
    halt: lively.ast.StackReification.halt,
    // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

    __createClosure: function(idx, f) {
        f._cachedAst = lively.ast.Rewriting.getCurrentASTRegistry()[idx];
        f.livelyDebuggingEnabled = true;
        return f;
    },

    __getClosure: function(idx) {
        return lively.ast.Rewriting.getCurrentASTRegistry()[idx];
    }
});

Object.extend(Function.prototype, {

    asRewrittenClosure: function(varMapping, astRegistry) {
        var closure = new lively.ast.RewrittenClosure(this, varMapping);
        closure.rewrite(astRegistry);
        return closure;
    },

    stackCaptureMode: function(varMapping, astRegistry) {
        var closure = this.asRewrittenClosure(varMapping, astRegistry),
            rewrittenFunc = closure.getRewrittenFunc();
        if (!rewrittenFunc) throw new Error('Cannot rewrite ' + this);
        return rewrittenFunc;
    },

    stackCaptureSource: function(varMapping, astRegistry) {
        return this.asRewrittenClosure(astRegistry).getRewrittenSource();
    }

});

Object.subclass('lively.ast.Continuation',
'settings', {
    isContinuation: true
},
'initializing', {
    initialize: function(frame) {
        this.currentFrame = frame; // the frame in which the the unwind was triggered
    },

    copy: function() {
        return new this.constructor(this.currentFrame.copy());
    }
},
'accessing', {
    frames: function() {
        var frame = this.currentFrame, result = [];
        do { result.push(frame); } while (frame = frame.getParentFrame());
        return result;
    }
},
'resuming', {
    resume: function() {
        // FIXME: outer context usually does not have original AST
        // attaching the program node would possibly be right (otherwise the pc's context is missing)
        if (!this.currentFrame.getOriginalAst())
            throw new Error('Cannot resume because frame has no AST!');
        if (!this.currentFrame.pc)
            throw new Error('Cannot resume because frame has no pc!');

        var interpreter = new lively.ast.AcornInterpreter.Interpreter();

        // go through all frames on the stack. beginning with the top most,
        // resume each of them
        var result = this.frames().reduce(function(result, frame, i) {
            if (result.error) {
                result.error.shiftFrame(frame);
                return result;
            }

            frame.alreadyComputed[frame.pc.astIndex] = result.val;

            // FIXME frames hold on to function ASTs but resuming from a
            // function is not supported right now. So we set the resumable
            // node to the functions body here as a quick fix
            var resumeNode = frame.getOriginalAst().body;
            try {
                return { val: interpreter.runWithFrameAndResult(resumeNode, frame, result.val) };
            } catch (ex) {
                if (!ex.isUnwindException)
                    throw ex;

                // FIXME: adjust already computed!
                // frame.setAlreadyComputed(alreadyComputed);
                var pc = acorn.walk.findNodeByAstIndex(frame.getOriginalAst(), ex.error.astIndex);
                frame.setPC(pc);
                ex.shiftFrame(frame);
                return { error: ex };
            }
        }, {});

        if (result.error)
            return lively.ast.Continuation.fromUnwindException(result.error);
        else
            return result.val;
    }
});

Object.extend(lively.ast.Continuation, {
    fromUnwindException: function(e) {
        if (!e.isUnwindException) console.error("No unwind exception?");
        return new this(e.top);
    }
});

Object.subclass('lively.ast.Rewriting.UnwindException',
'settings', {

    isUnwindException: true

},
'initializing', {

    initialize: function(error) {
        this.error = error;
    }

},
'printing', {

    toString: function() {
        return '[UNWIND] ' + this.error.toString();
    }

},
'frames', {

    createAndShiftFrame: function(thiz, frameState, lastNodeAstIndex, pointerToOriginalAst) {
        var alreadyComputed = frameState[0],
            // varMapping = frameState[1],
            parentFrameState = frameState[2],
            frame = lively.ast.AcornInterpreter.Frame.create(
                __getClosure(pointerToOriginalAst) /*, varMapping */),
            pc;
        frame.setThis(thiz);
        frame.setAlreadyComputed(alreadyComputed);
        if (!this.top) {
            pc = this.error && this.error.astIndex ?
                acorn.walk.findNodeByAstIndex(frame.getOriginalAst(), this.error.astIndex) :
                null;
        } else {
            if (frame.isAlreadyComputed(lastNodeAstIndex)) lastNodeAstIndex++;
            pc = acorn.walk.findNodeByAstIndex(frame.getOriginalAst(), lastNodeAstIndex);
        }
        frame.setPC(pc);

        var scope, topScope, newScope,
            fState = frameState;
        do {
            newScope = new lively.ast.AcornInterpreter.Scope(fState[1]); // varMapping
            if (scope)
                scope.setParentScope(newScope);
            else
                topScope = newScope;
            scope = newScope
            fState = fState[2]; // parentFrameState
        } while (fState && fState != Global);
        frame.setScope(topScope);

        return this.shiftFrame(frame);
    },

    shiftFrame: function(frame) {
        if (!this.top) {
            this.top = this.last = frame;
        } else {
            this.last.setParentFrame(frame);
            this.last = frame;
        }
        return frame;
    }

});

}) // end of module
<|endoftext|>