<commit_before>// eslint-disable-next-line max-classes-per-file
import {
  get,
  forEach,
  identity,
  reduce,
  kebabCase,
  find,
  filter,
  isNil,
} from 'lodash'

import MJMLParser from 'mjml-parser-xml'

import shorthandParser, { borderParser } from './helpers/shorthandParser'
import formatAttributes from './helpers/formatAttributes'
import jsonToXML from './helpers/jsonToXML'

export function initComponent({ initialDatas, name }) {
  const Component = initialDatas.context.components[name]

  if (Component) {
    const component = new Component(initialDatas)

    if (component.headStyle) {
      component.context.addHeadStyle(name, component.headStyle)
    }
    if (component.componentHeadStyle) {
      component.context.addComponentHeadSyle(component.componentHeadStyle)
    }

    return component
  }

  return null
}

class Component {
  static getTagName() {
    return this.componentName || kebabCase(this.name)
  }

  static isRawElement() {
    return !!this.rawElement
  }

  static defaultAttributes = {}

  constructor(initialDatas = {}) {
    const {
      attributes = {},
      children = [],
      content = '',
      context = {},
      props = {},
      globalAttributes = {},
    } = initialDatas

    this.props = {
      ...props,
      children,
      content,
    }

    this.attributes = formatAttributes(
      {
        ...this.constructor.defaultAttributes,
        ...globalAttributes,
        ...attributes,
      },
      this.constructor.allowedAttributes,
    )
    this.context = context

    return this
  }

  getChildContext() {
    return this.context
  }

  getAttribute(name) {
    return this.attributes[name]
  }

  getContent() {
    return this.props.content.trim()
  }

  renderMJML(mjml, options = {}) {
    if (typeof mjml === 'string') {
      // supports returning siblings elements from a custom component
      const partialMjml = MJMLParser(`<fragment>${mjml}</fragment>`, {
        ...options,
        components: this.context.components,
        ignoreIncludes: true,
      })
      return partialMjml.children
        .map((child) => this.context.processing(child, this.context))
        .join('')
    }

    return this.context.processing(mjml, this.context)
  }
}

export class BodyComponent extends Component {
  // eslint-disable-next-line class-methods-use-this
  getStyles() {
    return {}
  }

  getShorthandAttrValue(attribute, direction) {
    const mjAttributeDirection = this.getAttribute(`${attribute}-${direction}`)
    const mjAttribute = this.getAttribute(attribute)

    if (mjAttributeDirection) {
      return parseInt(mjAttributeDirection, 10)
    }

    if (!mjAttribute) {
      return 0
    }

    return shorthandParser(mjAttribute, direction)
  }

  getShorthandBorderValue(direction) {
    const borderDirection =
      direction && this.getAttribute(`border-${direction}`)
    const border = this.getAttribute('border')

    return borderParser(borderDirection || border || '0')
  }

  getBoxWidths() {
    const { containerWidth } = this.context
    const parsedWidth = parseInt(containerWidth, 10)

    const paddings =
      this.getShorthandAttrValue('padding', 'right') +
      this.getShorthandAttrValue('padding', 'left')

    const borders =
      this.getShorthandBorderValue('right') +
      this.getShorthandBorderValue('left')

    return {
      totalWidth: parsedWidth,
      borders,
      paddings,
      box: parsedWidth - paddings - borders,
    }
  }

  htmlAttributes(attributes) {
    const specialAttributes = {
      style: (v) => this.styles(v),
      default: identity,
    }

    return reduce(
      attributes,
      (output, v, name) => {
        const value = (specialAttributes[name] || specialAttributes.default)(v)

        if (!isNil(value)) {
          return `${output} ${name}="${value}"`
        }

        return output
      },
      '',
    )
  }

  styles(styles) {
    let stylesObject

    if (styles) {
      if (typeof styles === 'string') {
        stylesObject = get(this.getStyles(), styles)
      } else {
        stylesObject = styles
      }
    }

    return reduce(
      stylesObject,
      (output, value, name) => {
        if (!isNil(value)) {
          return `${output}${name}:${value};`
        }
        return output
      },
      '',
    )
  }

  renderChildren(children, options = {}) {
    const {
      props = {},
      renderer = (component) => component.render(),
      attributes = {},
      rawXML = false,
    } = options

    children = children || this.props.children

    if (rawXML) {
      return children.map((child) => jsonToXML(child)).join('\n')
    }

    const sibling = children.length

    const rawComponents = filter(this.context.components, (c) =>
      c.isRawElement(),
    )
    const nonRawSiblings = children.filter(
      (child) => !find(rawComponents, (c) => c.getTagName() === child.tagName),
    ).length

    let output = ''
    let index = 0

    forEach(children, (children) => {
      const component = initComponent({
        name: children.tagName,
        initialDatas: {
          ...children,
          attributes: {
            ...attributes,
            ...children.attributes,
          },
          context: this.getChildContext(),
          props: {
            ...props,
            first: index === 0,
            index,
            last: index + 1 === sibling,
            sibling,
            nonRawSiblings,
          },
        },
      })

      if (component !== null) {
        output += renderer(component)
      }

      index++ // eslint-disable-line no-plusplus
    })

    return output
  }
}

export class HeadComponent extends Component {
  static getTagName() {
    return this.componentName || kebabCase(this.name)
  }

  handlerChildren() {
    const children = this.props.children

    return children.map((children) => {
      const component = initComponent({
        name: children.tagName,
        initialDatas: {
          ...children,
          context: this.getChildContext(),
        },
      })

      if (!component) {
        // eslint-disable-next-line no-console
        console.error(`No matching component for tag : ${children.tagName}`)
        return null
      }

      if (component.handler) {
        component.handler()
      }

      if (component.render) {
        return component.render()
      }
      return null
    })
  }
}
<commit_msg>Fix lint<commit_after>// eslint-disable-next-line max-classes-per-file
import {
  get,
  forEach,
  identity,
  reduce,
  kebabCase,
  find,
  filter,
  isNil,
} from 'lodash'

import MJMLParser from 'mjml-parser-xml'

import shorthandParser, { borderParser } from './helpers/shorthandParser'
import formatAttributes from './helpers/formatAttributes'
import jsonToXML from './helpers/jsonToXML'

export function initComponent({ initialDatas, name }) {
  const Component = initialDatas.context.components[name]

  if (Component) {
    const component = new Component(initialDatas)

    if (component.headStyle) {
      component.context.addHeadStyle(name, component.headStyle)
    }
    if (component.componentHeadStyle) {
      component.context.addComponentHeadSyle(component.componentHeadStyle)
    }

    return component
  }

  return null
}

class Component {
  static getTagName() {
    return this.componentName || kebabCase(this.name)
  }

  static isRawElement() {
    return !!this.rawElement
  }

  static defaultAttributes = {}

  constructor(initialDatas = {}) {
    const {
      attributes = {},
      children = [],
      content = '',
      context = {},
      props = {},
      globalAttributes = {},
    } = initialDatas

    this.props = {
      ...props,
      children,
      content,
    }

    this.attributes = formatAttributes(
      {
        ...this.constructor.defaultAttributes,
        ...globalAttributes,
        ...attributes,
      },
      this.constructor.allowedAttributes,
    )
    this.context = context

    return this
  }

  getChildContext() {
    return this.context
  }

  getAttribute(name) {
    return this.attributes[name]
  }

  getContent() {
    return this.props.content.trim()
  }

  renderMJML(mjml, options = {}) {
    if (typeof mjml === 'string') {
      // supports returning siblings elements from a custom component
      const partialMjml = MJMLParser(`<fragment>${mjml}</fragment>`, {
        ...options,
        components: this.context.components,
        ignoreIncludes: true,
      })
      return partialMjml.children
        .map((child) => this.context.processing(child, this.context))
        .join('')
    }

    return this.context.processing(mjml, this.context)
  }
}

export class BodyComponent extends Component {
  // eslint-disable-next-line class-methods-use-this
  getStyles() {
    return {}
  }

  getShorthandAttrValue(attribute, direction) {
    const mjAttributeDirection = this.getAttribute(`${attribute}-${direction}`)
    const mjAttribute = this.getAttribute(attribute)

    if (mjAttributeDirection) {
      return parseInt(mjAttributeDirection, 10)
    }

    if (!mjAttribute) {
      return 0
    }

    return shorthandParser(mjAttribute, direction)
  }

  getShorthandBorderValue(direction) {
    const borderDirection =
      direction && this.getAttribute(`border-${direction}`)
    const border = this.getAttribute('border')

    return borderParser(borderDirection || border || '0')
  }

  getBoxWidths() {
    const { containerWidth } = this.context
    const parsedWidth = parseInt(containerWidth, 10)

    const paddings =
      this.getShorthandAttrValue('padding', 'right') +
      this.getShorthandAttrValue('padding', 'left')

    const borders =
      this.getShorthandBorderValue('right') +
      this.getShorthandBorderValue('left')

    return {
      totalWidth: parsedWidth,
      borders,
      paddings,
      box: parsedWidth - paddings - borders,
    }
  }

  htmlAttributes(attributes) {
    const specialAttributes = {
      style: (v) => this.styles(v),
      default: identity,
    }

    return reduce(
      attributes,
      (output, v, name) => {
        const value = (specialAttributes[name] || specialAttributes.default)(v)

        if (!isNil(value)) {
          return `${output} ${name}="${value}"`
        }

        return output
      },
      '',
    )
  }

  styles(styles) {
    let stylesObject

    if (styles) {
      if (typeof styles === 'string') {
        stylesObject = get(this.getStyles(), styles)
      } else {
        stylesObject = styles
      }
    }

    return reduce(
      stylesObject,
      (output, value, name) => {
        if (!isNil(value)) {
          return `${output}${name}:${value};`
        }
        return output
      },
      '',
    )
  }

  renderChildren(children, options = {}) {
    const {
      props = {},
      renderer = (component) => component.render(),
      attributes = {},
      rawXML = false,
    } = options

    children = children || this.props.children

    if (rawXML) {
      return children.map((child) => jsonToXML(child)).join('\n')
    }

    const sibling = children.length

    const rawComponents = filter(this.context.components, (c) =>
      c.isRawElement(),
    )
    const nonRawSiblings = children.filter(
      (child) => !find(rawComponents, (c) => c.getTagName() === child.tagName),
    ).length

    let output = ''
    let index = 0

    forEach(children, (children) => {
      const component = initComponent({
        name: children.tagName,
        initialDatas: {
          ...children,
          attributes: {
            ...attributes,
            ...children.attributes,
          },
          context: this.getChildContext(),
          props: {
            ...props,
            first: index === 0,
            index,
            last: index + 1 === sibling,
            sibling,
            nonRawSiblings,
          },
        },
      })

      if (component !== null) {
        output += renderer(component)
      }

      index++ // eslint-disable-line no-plusplus
    })

    return output
  }
}

export class HeadComponent extends Component {
  static getTagName() {
    return this.componentName || kebabCase(this.name)
  }

  handlerChildren() {
    const { children } = this.props

    return children.map((children) => {
      const component = initComponent({
        name: children.tagName,
        initialDatas: {
          ...children,
          context: this.getChildContext(),
        },
      })

      if (!component) {
        // eslint-disable-next-line no-console
        console.error(`No matching component for tag : ${children.tagName}`)
        return null
      }

      if (component.handler) {
        component.handler()
      }

      if (component.render) {
        return component.render()
      }
      return null
    })
  }
}
<|endoftext|>


<commit_before>import Ember from "ember-metal/core";
import { tryCatchFinally } from "ember-metal/utils";

/**
  The purpose of the Ember Instrumentation module is
  to provide efficient, general-purpose instrumentation
  for Ember.

  Subscribe to a listener by using `Ember.subscribe`:

  ```javascript
  Ember.subscribe("render", {
    before: function(name, timestamp, payload) {

    },

    after: function(name, timestamp, payload) {

    }
  });
  ```

  If you return a value from the `before` callback, that same
  value will be passed as a fourth parameter to the `after`
  callback.

  Instrument a block of code by using `Ember.instrument`:

  ```javascript
  Ember.instrument("render.handlebars", payload, function() {
    // rendering logic
  }, binding);
  ```

  Event names passed to `Ember.instrument` are namespaced
  by periods, from more general to more specific. Subscribers
  can listen for events by whatever level of granularity they
  are interested in.

  In the above example, the event is `render.handlebars`,
  and the subscriber listened for all events beginning with
  `render`. It would receive callbacks for events named
  `render`, `render.handlebars`, `render.container`, or
  even `render.handlebars.layout`.

  @class Instrumentation
  @namespace Ember
  @static
*/
export var subscribers = [];
var cache = {};

var populateListeners = function(name) {
  var listeners = [];
  var subscriber;

  for (var i=0, l=subscribers.length; i<l; i++) {
    subscriber = subscribers[i];
    if (subscriber.regex.test(name)) {
      listeners.push(subscriber.object);
    }
  }

  cache[name] = listeners;
  return listeners;
};

var time = (function() {
  var perf = 'undefined' !== typeof window ? window.performance || {} : {};
  var fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow;
  // fn.bind will be available in all the browsers that support the advanced window.performance... ;-)
  return fn ? fn.bind(perf) : function() { return +new Date(); };
})();

/**
  Notifies event's subscribers, calls `before` and `after` hooks.

  @method instrument
  @namespace Ember.Instrumentation

  @param {String} [name] Namespaced event name.
  @param {Object} payload
  @param {Function} callback Function that you're instrumenting.
  @param {Object} binding Context that instrument function is called with.
*/
export function instrument(name, _payload, callback, binding) {
  if (subscribers.length === 0) {
    return callback.call(binding);
  }
  var payload = _payload || {};
  var finalizer = _instrumentStart(name, function () {
    return payload;
  });
  if (finalizer) {
    var tryable = function _instrumenTryable() {
      return callback.call(binding);
    };
    var catchable = function _instrumentCatchable(e) {
      payload.exception = e;
    };
    return tryCatchFinally(tryable, catchable, finalizer);
  } else {
    return callback.call(binding);
  }
}

// private for now
export function _instrumentStart(name, _payload) {
  var listeners = cache[name];

  if (!listeners) {
    listeners = populateListeners(name);
  }

  if (listeners.length === 0) {
    return;
  }

  var payload = _payload();

  var STRUCTURED_PROFILE = Ember.STRUCTURED_PROFILE;
  var timeName;
  if (STRUCTURED_PROFILE) {
    timeName = name + ": " + payload.object;
    console.time(timeName);
  }

  var l = listeners.length;
  var beforeValues = new Array(l);
  var i, listener;
  var timestamp = time();
  for (i=0; i<l; i++) {
    listener = listeners[i];
    beforeValues[i] = listener.before(name, timestamp, payload);
  }

  return function _instrumentEnd() {
    var i, l, listener;
    var timestamp = time();
    for (i=0, l=listeners.length; i<l; i++) {
      listener = listeners[i];
      listener.after(name, timestamp, payload, beforeValues[i]);
    }

    if (STRUCTURED_PROFILE) {
      console.timeEnd(timeName);
    }
  };
}

/**
  Subscribes to a particular event or instrumented block of code.

  @method subscribe
  @namespace Ember.Instrumentation

  @param {String} [pattern] Namespaced event name.
  @param {Object} [object] Before and After hooks.

  @return {Subscriber}
*/
export function subscribe(pattern, object) {
  var paths = pattern.split("."), path, regex = [];

  for (var i=0, l=paths.length; i<l; i++) {
    path = paths[i];
    if (path === "*") {
      regex.push("[^\\.]*");
    } else {
      regex.push(path);
    }
  }

  regex = regex.join("\\.");
  regex = regex + "(\\..*)?";

  var subscriber = {
    pattern: pattern,
    regex: new RegExp("^" + regex + "$"),
    object: object
  };

  subscribers.push(subscriber);
  cache = {};

  return subscriber;
}

/**
  Unsubscribes from a particular event or instrumented block of code.

  @method unsubscribe
  @namespace Ember.Instrumentation

  @param {Object} [subscriber]
*/
export function unsubscribe(subscriber) {
  var index;

  for (var i=0, l=subscribers.length; i<l; i++) {
    if (subscribers[i] === subscriber) {
      index = i;
    }
  }

  subscribers.splice(index, 1);
  cache = {};
}

/**
  Resets `Ember.Instrumentation` by flushing list of subscribers.

  @method reset
  @namespace Ember.Instrumentation
*/
export function reset() {
  subscribers.length = 0;
  cache = {};
}
<commit_msg>[BUGFIX beta] Support instrument without payload<commit_after>import Ember from "ember-metal/core";
import { tryCatchFinally } from "ember-metal/utils";

/**
  The purpose of the Ember Instrumentation module is
  to provide efficient, general-purpose instrumentation
  for Ember.

  Subscribe to a listener by using `Ember.subscribe`:

  ```javascript
  Ember.subscribe("render", {
    before: function(name, timestamp, payload) {

    },

    after: function(name, timestamp, payload) {

    }
  });
  ```

  If you return a value from the `before` callback, that same
  value will be passed as a fourth parameter to the `after`
  callback.

  Instrument a block of code by using `Ember.instrument`:

  ```javascript
  Ember.instrument("render.handlebars", payload, function() {
    // rendering logic
  }, binding);
  ```

  Event names passed to `Ember.instrument` are namespaced
  by periods, from more general to more specific. Subscribers
  can listen for events by whatever level of granularity they
  are interested in.

  In the above example, the event is `render.handlebars`,
  and the subscriber listened for all events beginning with
  `render`. It would receive callbacks for events named
  `render`, `render.handlebars`, `render.container`, or
  even `render.handlebars.layout`.

  @class Instrumentation
  @namespace Ember
  @static
*/
export var subscribers = [];
var cache = {};

var populateListeners = function(name) {
  var listeners = [];
  var subscriber;

  for (var i=0, l=subscribers.length; i<l; i++) {
    subscriber = subscribers[i];
    if (subscriber.regex.test(name)) {
      listeners.push(subscriber.object);
    }
  }

  cache[name] = listeners;
  return listeners;
};

var time = (function() {
  var perf = 'undefined' !== typeof window ? window.performance || {} : {};
  var fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow;
  // fn.bind will be available in all the browsers that support the advanced window.performance... ;-)
  return fn ? fn.bind(perf) : function() { return +new Date(); };
})();

/**
  Notifies event's subscribers, calls `before` and `after` hooks.

  @method instrument
  @namespace Ember.Instrumentation

  @param {String} [name] Namespaced event name.
  @param {Object} payload
  @param {Function} callback Function that you're instrumenting.
  @param {Object} binding Context that instrument function is called with.
*/
export function instrument(name, _payload, callback, binding) {
  if (arguments.length <= 3 && typeof _payload === 'function') {
    binding = callback;
    callback = _payload;
    _payload = undefined;
  }
  if (subscribers.length === 0) {
    return callback.call(binding);
  }
  var payload = _payload || {};
  var finalizer = _instrumentStart(name, function () {
    return payload;
  });
  if (finalizer) {
    var tryable = function _instrumenTryable() {
      return callback.call(binding);
    };
    var catchable = function _instrumentCatchable(e) {
      payload.exception = e;
    };
    return tryCatchFinally(tryable, catchable, finalizer);
  } else {
    return callback.call(binding);
  }
}

// private for now
export function _instrumentStart(name, _payload) {
  var listeners = cache[name];

  if (!listeners) {
    listeners = populateListeners(name);
  }

  if (listeners.length === 0) {
    return;
  }

  var payload = _payload();

  var STRUCTURED_PROFILE = Ember.STRUCTURED_PROFILE;
  var timeName;
  if (STRUCTURED_PROFILE) {
    timeName = name + ": " + payload.object;
    console.time(timeName);
  }

  var l = listeners.length;
  var beforeValues = new Array(l);
  var i, listener;
  var timestamp = time();
  for (i=0; i<l; i++) {
    listener = listeners[i];
    beforeValues[i] = listener.before(name, timestamp, payload);
  }

  return function _instrumentEnd() {
    var i, l, listener;
    var timestamp = time();
    for (i=0, l=listeners.length; i<l; i++) {
      listener = listeners[i];
      listener.after(name, timestamp, payload, beforeValues[i]);
    }

    if (STRUCTURED_PROFILE) {
      console.timeEnd(timeName);
    }
  };
}

/**
  Subscribes to a particular event or instrumented block of code.

  @method subscribe
  @namespace Ember.Instrumentation

  @param {String} [pattern] Namespaced event name.
  @param {Object} [object] Before and After hooks.

  @return {Subscriber}
*/
export function subscribe(pattern, object) {
  var paths = pattern.split("."), path, regex = [];

  for (var i=0, l=paths.length; i<l; i++) {
    path = paths[i];
    if (path === "*") {
      regex.push("[^\\.]*");
    } else {
      regex.push(path);
    }
  }

  regex = regex.join("\\.");
  regex = regex + "(\\..*)?";

  var subscriber = {
    pattern: pattern,
    regex: new RegExp("^" + regex + "$"),
    object: object
  };

  subscribers.push(subscriber);
  cache = {};

  return subscriber;
}

/**
  Unsubscribes from a particular event or instrumented block of code.

  @method unsubscribe
  @namespace Ember.Instrumentation

  @param {Object} [subscriber]
*/
export function unsubscribe(subscriber) {
  var index;

  for (var i=0, l=subscribers.length; i<l; i++) {
    if (subscribers[i] === subscriber) {
      index = i;
    }
  }

  subscribers.splice(index, 1);
  cache = {};
}

/**
  Resets `Ember.Instrumentation` by flushing list of subscribers.

  @method reset
  @namespace Ember.Instrumentation
*/
export function reset() {
  subscribers.length = 0;
  cache = {};
}
<|endoftext|>


<commit_before>require("ember-views/views/view");

var get = Ember.get, set = Ember.set, isNone = Ember.isNone;

/**
@module ember
@submodule ember-views
*/

/**
  An `Ember.Component` is a view that is completely
  isolated. Property access in its templates go
  to the view object and actions are targeted at
  the view object. There is no access to the
  surrounding context or outer controller; all
  contextual information is passed in.

  The easiest way to create an `Ember.Component` is via
  a template. If you name a template
  `components/my-foo`, you will be able to use
  `{{my-foo}}` in other templates, which will make
  an instance of the isolated component.

  ```html
  {{app-profile person=currentUser}}
  ```

  ```html
  <!-- app-profile template -->
  <h1>{{person.title}}</h1>
  <img {{bindAttr src=person.avatar}}>
  <p class='signature'>{{person.signature}}</p>
  ```

  You can also use `yield` inside a template to
  include the **contents** of the custom tag:

  ```html
  {{#app-profile person=currentUser}}
    <p>Admin mode</p>
  {{/app-profile}}
  ```

  ```html
  <!-- app-profile template -->
  <h1>{{person.title}}</h1>
  {{yield}} <!-- block contents -->
  ```

  If you want to customize the component, in order to
  handle events or actions, you implement a subclass
  of `Ember.Component` named after the name of the
  component. Note that `Component` needs to be appended to the name of
  your subclass like `AppProfileComponent`.

  For example, you could implement the action
  `hello` for the `app-profile` component:

  ```javascript
  App.AppProfileComponent = Ember.Component.extend({
    hello: function(name) {
      console.log("Hello", name);
    }
  });
  ```

  And then use it in the component's template:

  ```html
  <!-- app-profile template -->

  <h1>{{person.title}}</h1>
  {{yield}} <!-- block contents -->

  <button {{action 'hello' person.name}}>
    Say Hello to {{person.name}}
  </button>
  ```

  Components must have a `-` in their name to avoid
  conflicts with built-in controls that wrap HTML
  elements. This is consistent with the same
  requirement in web components.

  @class Component
  @namespace Ember
  @extends Ember.View
*/
Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
  init: function() {
    this._super();
    set(this, 'context', this);
    set(this, 'controller', this);
  },

  // during render, isolate keywords
  cloneKeywords: function() {
    return {
      view: this,
      controller: this
    };
  },

  _yield: function(context, options) {
    var view = options.data.view,
        parentView = this._parentView,
        template = get(this, 'template');

    if (template) {
      Ember.assert("A Component must have a parent view in order to yield.", parentView);

      view.appendChild(Ember.View, {
        isVirtual: true,
        tagName: '',
        template: get(this, 'template'),
        context: get(parentView, 'context'),
        controller: get(parentView, 'controller'),
        templateData: { keywords: parentView.cloneKeywords() }
      });
    }
  },

  /**
    If the component is currently inserted into the DOM of a parent view, this
    property will point to the controller of the parent view.

    @property targetObject
    @type Ember.Controller
    @default null
  */
  targetObject: Ember.computed(function(key) {
    var parentView = get(this, '_parentView');
    return parentView ? get(parentView, 'controller') : null;
  }).property('_parentView'),

  /**
    Sends an action to component's controller. A component inherits its
    controller from the context in which it is used.

    By default, calling `sendAction()` will send an action with the name
    of the component's `action` property.

    For example, if the component had a property `action` with the value
    `"addItem"`, calling `sendAction()` would send the `addItem` action
    to the component's controller.

    If you provide the `action` argument to `sendAction()`, that key will
    be used to look up the action name.

    For example, if the component had a property `playing` with the value
    `didStartPlaying`, calling `sendAction('playing')` would send the
    `didStartPlaying` action to the component's controller.

    Whether or not you are using the default action or a named action, if
    the action name is not defined on the component, calling `sendAction()`
    does not have any effect.

    For example, if you call `sendAction()` on a component that does not have
    an `action` property defined, no action will be sent to the controller,
    nor will an exception be raised.

    You can send a context object with the action by supplying the `context`
    argument. The context will be supplied as the first argument in the
    target's action method. Example:

    ```javascript
    App.MyTreeComponent = Ember.Component.extend({
      click: function() {
        this.sendAction('didClickTreeNode', this.get('node'));
      }
    });

    App.CategoriesController = Ember.Controller.extend({
      didClickCategory: function(category) {
        //Do something with the node/category that was clicked
      }
    });
    ```

    ```handlebars
    {{! categories.hbs}}
    {{my-tree didClickTreeNode='didClickCategory'}}
    ```

    @method sendAction
    @param [action] {String} the action to trigger
    @param [context] {*} a context to send with the action
  */
  sendAction: function(action, context) {
    var actionName;

    // Send the default action
    if (action === undefined) {
      actionName = get(this, 'action');
      Ember.assert("The default action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
    } else {
      actionName = get(this, action);
      Ember.assert("The " + action + " action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
    }

    // If no action name for that action could be found, just abort.
    if (actionName === undefined) { return; }

    this.triggerAction({
      action: actionName,
      actionContext: context
    });
  }
});
<commit_msg>bindAttr -> bind-attr in components docs<commit_after>require("ember-views/views/view");

var get = Ember.get, set = Ember.set, isNone = Ember.isNone;

/**
@module ember
@submodule ember-views
*/

/**
  An `Ember.Component` is a view that is completely
  isolated. Property access in its templates go
  to the view object and actions are targeted at
  the view object. There is no access to the
  surrounding context or outer controller; all
  contextual information is passed in.

  The easiest way to create an `Ember.Component` is via
  a template. If you name a template
  `components/my-foo`, you will be able to use
  `{{my-foo}}` in other templates, which will make
  an instance of the isolated component.

  ```html
  {{app-profile person=currentUser}}
  ```

  ```html
  <!-- app-profile template -->
  <h1>{{person.title}}</h1>
  <img {{bind-attr src=person.avatar}}>
  <p class='signature'>{{person.signature}}</p>
  ```

  You can also use `yield` inside a template to
  include the **contents** of the custom tag:

  ```html
  {{#app-profile person=currentUser}}
    <p>Admin mode</p>
  {{/app-profile}}
  ```

  ```html
  <!-- app-profile template -->
  <h1>{{person.title}}</h1>
  {{yield}} <!-- block contents -->
  ```

  If you want to customize the component, in order to
  handle events or actions, you implement a subclass
  of `Ember.Component` named after the name of the
  component. Note that `Component` needs to be appended to the name of
  your subclass like `AppProfileComponent`.

  For example, you could implement the action
  `hello` for the `app-profile` component:

  ```javascript
  App.AppProfileComponent = Ember.Component.extend({
    hello: function(name) {
      console.log("Hello", name);
    }
  });
  ```

  And then use it in the component's template:

  ```html
  <!-- app-profile template -->

  <h1>{{person.title}}</h1>
  {{yield}} <!-- block contents -->

  <button {{action 'hello' person.name}}>
    Say Hello to {{person.name}}
  </button>
  ```

  Components must have a `-` in their name to avoid
  conflicts with built-in controls that wrap HTML
  elements. This is consistent with the same
  requirement in web components.

  @class Component
  @namespace Ember
  @extends Ember.View
*/
Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
  init: function() {
    this._super();
    set(this, 'context', this);
    set(this, 'controller', this);
  },

  // during render, isolate keywords
  cloneKeywords: function() {
    return {
      view: this,
      controller: this
    };
  },

  _yield: function(context, options) {
    var view = options.data.view,
        parentView = this._parentView,
        template = get(this, 'template');

    if (template) {
      Ember.assert("A Component must have a parent view in order to yield.", parentView);

      view.appendChild(Ember.View, {
        isVirtual: true,
        tagName: '',
        template: get(this, 'template'),
        context: get(parentView, 'context'),
        controller: get(parentView, 'controller'),
        templateData: { keywords: parentView.cloneKeywords() }
      });
    }
  },

  /**
    If the component is currently inserted into the DOM of a parent view, this
    property will point to the controller of the parent view.

    @property targetObject
    @type Ember.Controller
    @default null
  */
  targetObject: Ember.computed(function(key) {
    var parentView = get(this, '_parentView');
    return parentView ? get(parentView, 'controller') : null;
  }).property('_parentView'),

  /**
    Sends an action to component's controller. A component inherits its
    controller from the context in which it is used.

    By default, calling `sendAction()` will send an action with the name
    of the component's `action` property.

    For example, if the component had a property `action` with the value
    `"addItem"`, calling `sendAction()` would send the `addItem` action
    to the component's controller.

    If you provide the `action` argument to `sendAction()`, that key will
    be used to look up the action name.

    For example, if the component had a property `playing` with the value
    `didStartPlaying`, calling `sendAction('playing')` would send the
    `didStartPlaying` action to the component's controller.

    Whether or not you are using the default action or a named action, if
    the action name is not defined on the component, calling `sendAction()`
    does not have any effect.

    For example, if you call `sendAction()` on a component that does not have
    an `action` property defined, no action will be sent to the controller,
    nor will an exception be raised.

    You can send a context object with the action by supplying the `context`
    argument. The context will be supplied as the first argument in the
    target's action method. Example:

    ```javascript
    App.MyTreeComponent = Ember.Component.extend({
      click: function() {
        this.sendAction('didClickTreeNode', this.get('node'));
      }
    });

    App.CategoriesController = Ember.Controller.extend({
      didClickCategory: function(category) {
        //Do something with the node/category that was clicked
      }
    });
    ```

    ```handlebars
    {{! categories.hbs}}
    {{my-tree didClickTreeNode='didClickCategory'}}
    ```

    @method sendAction
    @param [action] {String} the action to trigger
    @param [context] {*} a context to send with the action
  */
  sendAction: function(action, context) {
    var actionName;

    // Send the default action
    if (action === undefined) {
      actionName = get(this, 'action');
      Ember.assert("The default action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
    } else {
      actionName = get(this, action);
      Ember.assert("The " + action + " action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
    }

    // If no action name for that action could be found, just abort.
    if (actionName === undefined) { return; }

    this.triggerAction({
      action: actionName,
      actionContext: context
    });
  }
});
<|endoftext|>


<commit_before>var _ = require('lodash');
var e = require('estree-builder');
var toId = require('to-js-identifier');

var comp_by_type = {
  'String': function(ast, comp){
    return e.str(ast.value);
  },
  'Identifier': function(ast, comp){
    return e.id(toId(ast.value));
  },
  'Application': function(ast, comp){
    return e('call',
      comp(ast.callee),
      comp(ast.args)
    );
  },
  'ExpressionStatement': function(ast, comp){
    return e(';', comp(ast.expression));
  },
  'Ruleset': function(ast, comp){
    var rules_obj = {};
    _.each(ast.rules, function(rule){
      rules_obj[rule.name.value] = comp(rule);
    });
    return [
      e(';', e('=', e('.', e.id('module'), e.id('exports')), e.obj({
        name: e.str(ast.name.value),
        rules: e.obj(rules_obj)
      })))
    ];
  },
  'Rule': function(ast, comp){
    return e.obj({
      select: comp(ast.select),
      action: comp(ast.action_block)
    });
  },
  'RuleSelect': function(ast, comp){
    ast = ast.event;//TODO remove this Hack

    var estCTXEventProp = function(prop){
      return e('.', e('.', e.id('ctx'), e.id('event')), e.id(prop));
    };

    var eventExprToEstree = function(expr){
      var fn_body = [];
      fn_body.push(e(';', e('call', e.id('callback'), [
        e.nil(),
        e('&&',
          e('===', estCTXEventProp('domain'), e.str('echo')),
          e('===', estCTXEventProp('type'), e.str('hello'))
        )
      ])));
      return e.fn(['ctx', 'callback'], fn_body);
    };

    var exprs_array = [];
    exprs_array.push({
      domain: ast.event_domain.value,
      type: ast.event_type.value
    });

    var graph = {};
    var eventexprs = {};
    var state_machine = {start: []};
    _.each(exprs_array, function(expr, i){
      var id = 'expr_' + i;
      _.set(graph, [expr.domain, expr.type, id], true);
      eventexprs[id] = eventExprToEstree(expr);

      state_machine.start.push([id, 'end']);
      state_machine.start.push([['not', id], 'start']);
    });

    return e.obj({
      graph: e.json(graph),
      eventexprs: e.obj(eventexprs),
      state_machine: e.json(state_machine)
    });
  },
  'RuleActionBlock': function(ast, comp){
    return e.fn(['ctx', 'callback'], _.flattenDeep(_.map(ast.actions, function(action){
      return comp(action);
    })));
  },
  'RuleAction': function(ast, comp){
    return e(';', e('call', e.id('callback'), [
      e.nil(),
      e.obj({
        type: e.str('directive'),
        name: e.str(ast.args[0].value),
        options: e.obj(_.fromPairs(_.map(ast['with'], function(dec){
          return [dec.left.value, comp(dec.right)];
        })))
      })
    ]));
  }
};

module.exports = function(ast, options){
  options = options || {};

  var toLoc = options.toLoc || _.noop;

  var compile = function compile(ast){
    if(_.isArray(ast)){
      return _.map(ast, function(a){
        return compile(a);
      });
    }else if(!_.has(ast, 'type')){
      throw new Error('Invalid ast node: ' + JSON.stringify(ast));
    }else if(!_.has(comp_by_type, ast.type)){
      throw new Error('Unsupported ast node type: ' + ast.type);
    }
    return comp_by_type[ast.type](ast, compile, toLoc);
  };

  return compile(ast, 0);
};
<commit_msg>preparing to pass krl-ast locations to estree<commit_after>var _ = require('lodash');
var toId = require('to-js-identifier');
var mkTree = require('estree-builder');

var comp_by_type = {
  'String': function(ast, comp, e){
    return e.str(ast.value);
  },
  'Identifier': function(ast, comp, e){
    return e.id(toId(ast.value));
  },
  'Application': function(ast, comp, e){
    return e('call',
      comp(ast.callee),
      comp(ast.args)
    );
  },
  'ExpressionStatement': function(ast, comp, e){
    return e(';', comp(ast.expression));
  },
  'Ruleset': function(ast, comp, e){
    var rules_obj = {};
    _.each(ast.rules, function(rule){
      rules_obj[rule.name.value] = comp(rule);
    });
    return [
      e(';', e('=', e('.', e.id('module'), e.id('exports')), e.obj({
        name: e.str(ast.name.value),
        rules: e.obj(rules_obj)
      })))
    ];
  },
  'Rule': function(ast, comp, e){
    return e.obj({
      select: comp(ast.select),
      action: comp(ast.action_block)
    });
  },
  'RuleSelect': function(ast, comp, e){
    ast = ast.event;//TODO remove this Hack

    var estCTXEventProp = function(prop){
      return e('.', e('.', e.id('ctx'), e.id('event')), e.id(prop));
    };

    var eventExprToEstree = function(expr){
      var fn_body = [];
      fn_body.push(e(';', e('call', e.id('callback'), [
        e.nil(),
        e('&&',
          e('===', estCTXEventProp('domain'), e.str('echo')),
          e('===', estCTXEventProp('type'), e.str('hello'))
        )
      ])));
      return e.fn(['ctx', 'callback'], fn_body);
    };

    var exprs_array = [];
    exprs_array.push({
      domain: ast.event_domain.value,
      type: ast.event_type.value
    });

    var graph = {};
    var eventexprs = {};
    var state_machine = {start: []};
    _.each(exprs_array, function(expr, i){
      var id = 'expr_' + i;
      _.set(graph, [expr.domain, expr.type, id], true);
      eventexprs[id] = eventExprToEstree(expr);

      state_machine.start.push([id, 'end']);
      state_machine.start.push([['not', id], 'start']);
    });

    return e.obj({
      graph: e.json(graph),
      eventexprs: e.obj(eventexprs),
      state_machine: e.json(state_machine)
    });
  },
  'RuleActionBlock': function(ast, comp, e){
    return e.fn(['ctx', 'callback'], _.flattenDeep(_.map(ast.actions, function(action){
      return comp(action);
    })));
  },
  'RuleAction': function(ast, comp, e){
    return e(';', e('call', e.id('callback'), [
      e.nil(),
      e.obj({
        type: e.str('directive'),
        name: e.str(ast.args[0].value),
        options: e.obj(_.fromPairs(_.map(ast['with'], function(dec){
          return [dec.left.value, comp(dec.right)];
        })))
      })
    ]));
  }
};

module.exports = function(ast, options){
  options = options || {};

  var toLoc = options.toLoc || _.noop;

  var e = function(){
    var args = Array.prototype.slice.call(arguments, 0, arguments.length);
    toLoc(0, 1);
    return mkTree.apply(null, args);
  };
  _.each(mkTree, function(fn, key){
    e[key] = function(){
      var args = Array.prototype.slice.call(arguments, 0, arguments.length);
      return mkTree.apply(null, [key].concat(args));
    };
  });

  var compile = function compile(ast){
    if(_.isArray(ast)){
      return _.map(ast, function(a){
        return compile(a);
      });
    }else if(!_.has(ast, 'type')){
      throw new Error('Invalid ast node: ' + JSON.stringify(ast));
    }else if(!_.has(comp_by_type, ast.type)){
      throw new Error('Unsupported ast node type: ' + ast.type);
    }
    return comp_by_type[ast.type](ast, compile, e);
  };

  return compile(ast, 0);
};
<|endoftext|>


<commit_before>/**
 * @fileoverview Build file for the googlejs ESLint plugin.
 */

/* global echo, exec, exit, find, target, rm */
/* eslint no-use-before-define: "off", no-console: "off" */
'use strict';

require('shelljs/make');

const closurePackage = require('google-closure-compiler');
const ClosureCompiler = closurePackage.compiler;
const nodeCLI = require('shelljs-nodecli');
const path = require('path');

/* eslint-disable googlejs/camelcase */

const NODE_MODULES = './node_modules';

// Intentional extra space at the end of each string so it still works if we
// forget to concat with an extra space.
const MOCHA = `${NODE_MODULES}/mocha/bin/_mocha `;
const ESLINT = 'eslint ';

const MAKEFILE = './Makefile.js';
const JS_FILES = 'lib/**/*.js';
const SOURCE_TEST_FILES = 'tests/*.js tests/**/*.js';
const DIST_TEST_FILES = 'dist/tests/*.js dist/tests/**/*.js';

const MOCHA_TIMEOUT = 10000;

const COMPILER_ERROR_LIST = [
  // Warnings about ambiguous definitions of functions. On Chrome, if (false) {
  // function f() {} } declared 'f' in the global scope anyway. Future versions
  // of javascript forbid this, because the actual semantics differ between
  // browsers.
  'ambiguousFunctionDecl',
  // Warnings when vars are not declared or declared multiple times in the
  // global scope.
  'checkVars',
  // Warnings about misused goog.provide/goog.require calls
  'closureDepMethodUsageChecks',
  // Warnings when a variable is declared twice in the global scope
  'duplicate',
  // Warnings when two i18n messages have the same id
  'duplicateMessage',
  // Warnings about EcmaScript3
  'es3',
  // Warnings about EcmaScript5 strict mode
  'es5Strict',
  // Warnings about syntax error on Internet Explorer.
  'internetExplorerChecks',
  // Warnings when a variable is never defined.
  'undefinedVars',
  // Warnings when there are references in earlier modules to variables defined
  // in later modules
  'violatedModuleDep',
];

const COMPILER_OFF_LIST = [
  // Warnings when @deprecated, @private, @protected, or @package are violated.
  'accessControls',
  // Warnings when the 'debugger' keyword is used.
  'checkDebuggerStatement',
  // Warnings about undisposed eventful objects.
  'checkEventfulObjectDisposal',
  // Warnings about weird regular expression literals
  'checkRegExp',
  // Warnings when a variable or member property marked @const is reassigned.
  'const',
  // Warnings when a member property marked @const is reassigned.
  'constantProperty',
  // Warnings when non-deprecated code accesses code that's marked @deprecated
  'deprecated',
  // Warnings when using annotations that are deprecated
  'deprecatedAnnotations',
  // Warnings about unnecessary goog.require calls
  'extraRequire',
  // Warnings if a @const declaration contains no declared type and the compiler
  // cannot infer one
  'inferredConstCheck',
  // Warnings about strings that should only be used inside calls to
  // goog.getCssName
  'missingGetCssName',
  // Warnings about whether a property will ever be defined on an object. Part
  // of type-checking.
  'missingProperties',
  // Warnings if missing a goog.provide('Foo') when defining a Foo class
  'missingProvide',
  // Warnings if missing a goog.require('Foo') when a new Foo() is encountered
  'missingRequire',
  // Warnings if missing return in a function which a non-void return type
  'missingReturn',
  // Warnings from new type checker
  'newCheckTypes',
  // Warnings for any place in the code where type is inferred to ?. NOT
  // RECOMMENDED!
  'reportUnknownTypes',
  // Warnings about all references potentially violating module dependencies
  'strictModuleDepCheck',
  // Warnings about goog.tweak primitives
  'tweakValidation',
  // Warn about properties that cannot be disambiguated when using type based
  // optimizations
  'typeInvalidation',
  // Warnings when a property of a global name is not defined.
  'undefinedNames',
  // Warnings aout usages of goog.base, which is not compatible with strict mode
  'useOfGoogBase',
  // Warnings when @private and @protected are violated.
  'visibility',
];

const COMPILER_WARNING_LIST = [
  // Type-checking
  'checkTypes',
  // Warnings about conformance violations and possible conformance violations.
  'conformanceViolations',
  // Warnings about malformed externs files
  'externsValidation',
  // Warnings about duplicate @fileoverview tags
  'fileoverviewTags',
  // Warnings about improper use of the global this.
  'globalThis',
  // Warnings about invalid type casts.
  'invalidCasts',
  // Warnings about jsdoc type annotations that are misplaced
  'misplacedTypeAnnotation',
  // Warnings when JSDoc has annotations that the compiler thinks you
  // misspelled.
  'nonStandardJsDocs',
  // Warning about things like missing semicolons and comparisons to NaN
  'suspiciousCode',
  // Warnings when unknown @define values are specified.
  'unknownDefines',
  // Warnings when the compiler sees useless code that it plans to remove.
  'uselessCode',
];

const commonClosureCompilerSettings = {
  js: [
    'index.js',
    "'./lib/**.js'",
  ],
  externs: [
    './externs/externs-chai.js',
    './externs/externs-commonjs.js',
    './externs/externs-doctrine.js',
    './externs/externs-eslint.js',
    './externs/externs-espree.js',
    './externs/externs-escope.js',
    './externs/externs-mocha.js',
  ],
  language_in: 'ECMASCRIPT6_STRICT',
  language_out: 'ECMASCRIPT5_STRICT',
  warning_level: 'VERBOSE',
  jscomp_error: COMPILER_ERROR_LIST,
  jscomp_warning: COMPILER_WARNING_LIST,
  jscomp_off: COMPILER_OFF_LIST,

  // We use null for options that don't have a value.  Otherwise, it errors
  // out.  The existence of 'checks-only' is enough for it to be included as
  // an option.
  new_type_inf: null,
  assume_function_wrapper: null,
  process_closure_primitives: null,
  hide_warnings_for: 'google-closure-library',
  dependency_mode: 'STRICT',
  entry_point: 'googlejs.config',
};

const closureJavaOptions = [];

const CLOSURE_BASE_JS =
    './node_modules/google-closure-library/closure/goog/base.js';

const CLOSURE_LIB_JS =
    './node_modules/google-closure-library/closure/goog/**.js';

/**
 * @typedef {{
 *   compiler: !Object,
 *   outputFile: string,
 * }}
 */
let CompiledInfo;

/**
 * Builds a compiler object suitable for building the output test file that's
 * directly runnable by Node JS.
 * @param {string} testFilePath
 * @param {string} entryPoint
 * @return {!CompiledInfo} An object with the closure compiler and the output
 *     file.
 */
function buildTestCompiler(testFilePath, entryPoint) {
  const outputFile = getTestFilePathOutputLocation(testFilePath);
  const compiler = new ClosureCompiler(
      Object.assign(commonClosureCompilerSettings, {
        js: [
          CLOSURE_BASE_JS,
          // CLOSURE_LIB_JS,
          "'./lib/**.js'",
          testFilePath,
        ],
        js_output_file: outputFile,
        compilation_level: 'WHITESPACE_ONLY',
        formatting: 'PRETTY_PRINT',
        entry_point: entryPoint,
        rewrite_polyfills: false,
      }),
      closureJavaOptions
  );
  return {compiler, outputFile};
}

/**
 * Changes code-like-this to codeLikeThis, i.e. kebab-case to camelCase.
 * @param {string} string
 * @return {string}
 */
function kebabToCamel(string) {
  return string.replace(/(\-\w)/g, (match) => match[1].toUpperCase());
}

/**
 * Converts a path to a test file to the corresponding goog.module name.
 * @param {string} testFilePath
 * @return {string}
 */
function convertTestFilePathToModuleName(testFilePath) {
  const pathComponents = path.parse(path.normalize(testFilePath));
  const relPath = path.relative(__dirname, pathComponents.dir);
  const moduleNamePath = relPath.split(path.sep).join('.');
  const moduleName = kebabToCamel(pathComponents.name);
  const fullModuleName = `googlejs.${moduleNamePath}.${moduleName}`;
  return fullModuleName;
}

/**
 * Gets the corresponding output file path for a test file.
 * @param {string} testFilePath
 * @return {string} The output path for the compiled test file.
 */
function getTestFilePathOutputLocation(testFilePath) {
  const relPath = path.relative(__dirname, path.normalize(testFilePath));
  const outputPath = path.join(__dirname, 'dist', relPath);
  return outputPath;
}

/**
 * Builds a runnable test for mocha and return the output file path.
 * @param {string} testFilePath
 * @param {function(string)} onCompilation Function to run after compilation.
 * @return {string} The output file.
 */
function buildTest(testFilePath, onCompilation) {
  const moduleName = convertTestFilePathToModuleName(testFilePath);
  const compilerInfo = buildTestCompiler(testFilePath, moduleName);
  // Delete existing test so we don't run it again if the compilation fails.
  rm('-f', compilerInfo.outputFile);
  compilerInfo.compiler.run((exitCode, stdout, stderr) => {
    console.log(stdout);
    console.error(stderr);
    onCompilation(compilerInfo.outputFile);
  });
  return compilerInfo.outputFile;
}

/**
 * Runs a compiled test.
 * @param {string} testOutputFile
 */
function runTest(testOutputFile) {
  nodeCLI.exec('./node_modules/mocha/bin/_mocha', '-R progress',
               ` -t  ${MOCHA_TIMEOUT}`, '-c', testOutputFile);
}

/**
 * Builds all tests in project and runs the callback onCompilation for each
 * test.
 * @param {function(string)} onCompilation Function to run after compilation.
 */
function testAllFiles(onCompilation) {
  /** @type {!Array<string>} */
  const allTests = find('./tests').filter((file) => file.match(/\.js$/));
  allTests.forEach((path) => buildTest(path, onCompilation));
}

target.all = () => {
  target.test();
};

target.checkTypes = function() {
  console.log('Checking types.');
  const closureCompilerTypeCheck = new ClosureCompiler(
      Object.assign(commonClosureCompilerSettings, {
        checks_only: null,
      }),
      closureJavaOptions
  );

  closureCompilerTypeCheck.run(function(exitCode, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
  });
};

target.buildSimple = () => {
  console.log('Building the googlejs plugin library with SIMPLE ' +
              'optimizations.');
  const closureCompilerBuild = new ClosureCompiler(
      Object.assign(commonClosureCompilerSettings, {
        js_output_file: './dist/googlejs-eslint-plugin.js',
        compilation_level: 'SIMPLE',
        assume_function_wrapper: null,
        formatting: 'PRETTY_PRINT',
        rewrite_polyfills: false,
      }),
      closureJavaOptions
  );

  closureCompilerBuild.run(function(exitCode, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
  });
};

target.buildAdvanced = () => {
  console.log('Building the googlejs plugin library with ADVANCED ' +
              'optimizations.');
  const closureCompilerBuild = new ClosureCompiler(
      Object.assign(commonClosureCompilerSettings, {
        js_output_file: './dist/googlejs-eslint-plugin.min.js',
        compilation_level: 'ADVANCED',
        use_types_for_optimization: null,
        rewrite_polyfills: false,
      }),
      closureJavaOptions
  );

  closureCompilerBuild.run(function(exitCode, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
  });
};

/**
 * Tests the supplied rules, i.e. no-undef indent.
 * @param {!Array<string>} args
 */
target.testRule = (args) => {
  if (args.length == 0) {
    throw new Error('Need at least one rule to test.');
  }
  for (const rule of args) {
    // Remove any paths just in case.
    const baseRule = path.basename(rule, '.js');
    buildTest(`tests/rules/${baseRule}.js`, runTest);
  }
};

target.lint = () => {
  let errors = 0;
  let makeFileCache = ' ';
  let jsCache = ' ';
  let testCache = ' ';
  let lastReturn;

  // using the cache locally to speed up linting process
  if (!process.env.TRAVIS) {
    makeFileCache = ' --cache --cache-file .cache/makefile_cache ';
    jsCache = ' --cache --cache-file .cache/js_cache ';
    testCache = ' --cache --cache-file .cache/test_cache ';
  }

  echo('Linting Makefile.js');
  lastReturn = exec(ESLINT + makeFileCache + MAKEFILE);
  if (lastReturn.code !== 0) {
    errors++;
  }

  echo('Linting JavaScript files');
  lastReturn = exec(ESLINT + jsCache + JS_FILES);
  if (lastReturn.code !== 0) {
    errors++;
  }

  echo('Linting JavaScript test files');
  lastReturn = exec(ESLINT + testCache + SOURCE_TEST_FILES);
  if (lastReturn.code !== 0) {
    errors++;
  }

  if (errors) {
    exit(1);
  }
};

target.test = () => {
  // target.lint();

  let errors = 0;
  let lastReturn = 0;

  rm('-r', './dist/tests');
  testAllFiles((outputFile) => {
    lastReturn = nodeCLI.exec(
        'istanbul', 'cover', MOCHA, `-- -R progress -t  ${MOCHA_TIMEOUT}`,
        '-c', outputFile);

    if (lastReturn.code !== 0) {
      errors++;
    }
  });

  // lastReturn = nodeCLI.exec(
  //     'istanbul', 'check-coverage',
  //     '--statement 99 --branch 98 --function 99 --lines 99');
  // if (lastReturn.code !== 0) {
  //   errors++;
  // }

  // target.browserify();

  // lastReturn = nodeCLI.exec('karma', "start karma.conf.js");
  // if (lastReturn.code !== 0) {
  //   errors++;
  // }

  if (errors) {
    exit(1);
  }

  // target.checkLicenses();
};
<commit_msg>refactor(build): get errors working by compiling with simple optimizations<commit_after>/**
 * @fileoverview Build file for the googlejs ESLint plugin.
 */

/* global echo, exec, exit, find, target, rm */
/* eslint no-use-before-define: "off", no-console: "off" */
'use strict';

require('shelljs/make');

const closurePackage = require('google-closure-compiler');
const ClosureCompiler = closurePackage.compiler;
const nodeCLI = require('shelljs-nodecli');
const path = require('path');

/* eslint-disable googlejs/camelcase */

const NODE_MODULES = './node_modules';

// Intentional extra space at the end of each string so it still works if we
// forget to concat with an extra space.
const MOCHA = `${NODE_MODULES}/mocha/bin/_mocha `;
const ESLINT = 'eslint ';

const MAKEFILE = './Makefile.js';
const JS_FILES = 'lib/**/*.js';
const SOURCE_TEST_FILES = 'tests/*.js tests/**/*.js';
const DIST_TEST_FILES = 'dist/tests/*.js dist/tests/**/*.js';

const MOCHA_TIMEOUT = 10000;

const COMPILER_ERROR_LIST = [
  // Warnings when @deprecated, @private, @protected, or @package are violated.
  'accessControls',
  // Warnings when the 'debugger' keyword is used.
  'checkDebuggerStatement',
  // Warnings about undisposed eventful objects.
  'checkEventfulObjectDisposal',
  // Warnings about weird regular expression literals
  'checkRegExp',
  // Warnings when a variable or member property marked @const is reassigned.
  'const',
  // Warnings when a member property marked @const is reassigned.
  'constantProperty',
  // Warnings when non-deprecated code accesses code that's marked @deprecated
  'deprecated',
  // Warnings when using annotations that are deprecated
  'deprecatedAnnotations',
  // Warnings about unnecessary goog.require calls
  'extraRequire',
  // Warnings if a @const declaration contains no declared type and the compiler
  // cannot infer one
  'inferredConstCheck',
  // Warnings about strings that should only be used inside calls to
  // goog.getCssName
  'missingGetCssName',
  // Warnings about whether a property will ever be defined on an object. Part
  // of type-checking.
  'missingProperties',
  // Warnings if missing a goog.provide('Foo') when defining a Foo class
  'missingProvide',
  // Warnings if missing a goog.require('Foo') when a new Foo() is encountered
  'missingRequire',
  // Warnings if missing return in a function which a non-void return type
  'missingReturn',
  // Warnings from new type checker
  'newCheckTypes',
  // Warnings about all references potentially violating module dependencies
  'strictModuleDepCheck',
  // Warnings about goog.tweak primitives
  'tweakValidation',
  // Warn about properties that cannot be disambiguated when using type based
  // optimizations
  'typeInvalidation',
  // Warnings when a property of a global name is not defined.
  'undefinedNames',
  // Warnings aout usages of goog.base, which is not compatible with strict mode
  'useOfGoogBase',
  // Warnings when @private and @protected are violated.
  'visibility',
  // Type-checking
  'checkTypes',
  // Warnings about conformance violations and possible conformance violations.
  'conformanceViolations',
  // Warnings about malformed externs files
  'externsValidation',
  // Warnings about duplicate @fileoverview tags
  'fileoverviewTags',
  // Warnings about improper use of the global this.
  'globalThis',
  // Warnings about invalid type casts.
  'invalidCasts',
  // Warnings about jsdoc type annotations that are misplaced
  'misplacedTypeAnnotation',
  // Warnings when JSDoc has annotations that the compiler thinks you
  // misspelled.
  'nonStandardJsDocs',
  // Warning about things like missing semicolons and comparisons to NaN
  'suspiciousCode',
  // Warnings when unknown @define values are specified.
  'unknownDefines',
  // Warnings when the compiler sees useless code that it plans to remove.
  'uselessCode',
];

const COMPILER_WARNING_LIST = [
];

const COMPILER_OFF_LIST = [

  // Warnings for any place in the code where type is inferred to ?. NOT
  // RECOMMENDED!
  'reportUnknownTypes',
];

const COMMON_CLOSURE_COMPILER_SETTINGS = {
  js: [
    'index.js',
    "'./lib/**.js'",
  ],
  externs: [
    './externs/externs-chai.js',
    './externs/externs-commonjs.js',
    './externs/externs-doctrine.js',
    './externs/externs-eslint.js',
    './externs/externs-espree.js',
    './externs/externs-escope.js',
    './externs/externs-mocha.js',
  ],
  language_in: 'ECMASCRIPT6_STRICT',
  language_out: 'ECMASCRIPT5_STRICT',

  warning_level: 'VERBOSE',
  jscomp_error: COMPILER_ERROR_LIST,
  jscomp_warning: COMPILER_WARNING_LIST,
  jscomp_off: COMPILER_OFF_LIST,

  // We use null for options that don't have a value.  Otherwise, it errors
  // out.  The existence of 'checks-only' is enough for it to be included as
  // an option.
  new_type_inf: null,
  assume_function_wrapper: null,
  process_closure_primitives: null,
  hide_warnings_for: 'google-closure-library',
  dependency_mode: 'STRICT',
  entry_point: 'googlejs.config',
};

const CLOSURE_JAVA_OPTIONS = [];

const CLOSURE_BASE_JS =
    './node_modules/google-closure-library/closure/goog/base.js';

const CLOSURE_LIB_JS =
    './node_modules/google-closure-library/closure/goog/**.js';

/**
 * @typedef {{
 *   compiler: !Object,
 *   outputFile: string,
 * }}
 */
let CompiledInfo;

/**
 * Builds a compiler object suitable for building the output test file that's
 * directly runnable by Node JS.
 * @param {string} testFilePath
 * @param {string} entryPoint
 * @return {!CompiledInfo} An object with the closure compiler and the output
 *     file.
 */
function buildTestCompiler(testFilePath, entryPoint) {
  const outputFile = getTestFilePathOutputLocation(testFilePath);
  const compiler = new ClosureCompiler(
      Object.assign(COMMON_CLOSURE_COMPILER_SETTINGS, {
        js: [
          CLOSURE_BASE_JS,
          // CLOSURE_LIB_JS,
          "'./lib/**.js'",
          testFilePath,
        ],
        js_output_file: outputFile,
        // This is slower, but errors aren't reported with WHITESPACE_ONLY.
        compilation_level: 'SIMPLE',
        formatting: 'PRETTY_PRINT',
        entry_point: entryPoint,
        rewrite_polyfills: false,
      }),
      CLOSURE_JAVA_OPTIONS
  );
  return {compiler, outputFile};
}

/**
 * Changes code-like-this to codeLikeThis, i.e. kebab-case to camelCase.
 * @param {string} string
 * @return {string}
 */
function kebabToCamel(string) {
  return string.replace(/(\-\w)/g, (match) => match[1].toUpperCase());
}

/**
 * Converts a path to a test file to the corresponding goog.module name.
 * @param {string} testFilePath
 * @return {string}
 */
function convertTestFilePathToModuleName(testFilePath) {
  const pathComponents = path.parse(path.normalize(testFilePath));
  const relPath = path.relative(__dirname, pathComponents.dir);
  const moduleNamePath = relPath.split(path.sep).join('.');
  const moduleName = kebabToCamel(pathComponents.name);
  const fullModuleName = `googlejs.${moduleNamePath}.${moduleName}`;
  return fullModuleName;
}

/**
 * Gets the corresponding output file path for a test file.
 * @param {string} testFilePath
 * @return {string} The output path for the compiled test file.
 */
function getTestFilePathOutputLocation(testFilePath) {
  const relPath = path.relative(__dirname, path.normalize(testFilePath));
  const outputPath = path.join(__dirname, 'dist', relPath);
  return outputPath;
}

/**
 * Builds a runnable test for mocha and return the output file path.
 * @param {string} testFilePath
 * @param {function(string)} onCompilation Function to run after compilation.
 * @return {string} The output file.
 */
function buildTest(testFilePath, onCompilation) {
  const moduleName = convertTestFilePathToModuleName(testFilePath);
  const compilerInfo = buildTestCompiler(testFilePath, moduleName);
  // Delete existing test so we don't run it again if the compilation fails.
  rm('-f', compilerInfo.outputFile);
  compilerInfo.compiler.run((exitCode, stdout, stderr) => {
    console.log(stdout);
    console.error(stderr);
    onCompilation(compilerInfo.outputFile);
  });
  return compilerInfo.outputFile;
}

/**
 * Runs a compiled test.
 * @param {string} testOutputFile
 */
function runTest(testOutputFile) {
  nodeCLI.exec('./node_modules/mocha/bin/_mocha', '-R progress',
               ` -t  ${MOCHA_TIMEOUT}`, '-c', testOutputFile);
}

/**
 * Builds all tests in project and runs the callback onCompilation for each
 * test.
 * @param {function(string)} onCompilation Function to run after compilation.
 */
function testAllFiles(onCompilation) {
  /** @type {!Array<string>} */
  const allTests = find('./tests').filter((file) => file.match(/\.js$/));
  allTests.forEach((path) => buildTest(path, onCompilation));
}

target.all = () => {
  target.test();
};

target.checkTypes = function() {
  console.log('Checking types.');
  const closureCompilerTypeCheck = new ClosureCompiler(
      Object.assign(COMMON_CLOSURE_COMPILER_SETTINGS, {
        checks_only: null,
      }),
      CLOSURE_JAVA_OPTIONS
  );

  closureCompilerTypeCheck.run(function(exitCode, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
  });
};

target.buildSimple = () => {
  console.log('Building the googlejs plugin library with SIMPLE ' +
              'optimizations.');
  const closureCompilerBuild = new ClosureCompiler(
      Object.assign(COMMON_CLOSURE_COMPILER_SETTINGS, {
        js_output_file: './dist/googlejs-eslint-plugin.js',
        compilation_level: 'SIMPLE',
        assume_function_wrapper: null,
        formatting: 'PRETTY_PRINT',
        rewrite_polyfills: false,
      }),
      CLOSURE_JAVA_OPTIONS
  );

  closureCompilerBuild.run(function(exitCode, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
  });
};

target.buildAdvanced = () => {
  console.log('Building the googlejs plugin library with ADVANCED ' +
              'optimizations.');
  const closureCompilerBuild = new ClosureCompiler(
      Object.assign(COMMON_CLOSURE_COMPILER_SETTINGS, {
        js_output_file: './dist/googlejs-eslint-plugin.min.js',
        compilation_level: 'ADVANCED',
        use_types_for_optimization: null,
        rewrite_polyfills: false,
      }),
      CLOSURE_JAVA_OPTIONS
  );

  closureCompilerBuild.run(function(exitCode, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
  });
};

/**
 * Tests the supplied rules, i.e. no-undef indent.
 * @param {!Array<string>} args
 */
target.testRule = (args) => {
  if (args.length == 0) {
    throw new Error('Need at least one rule to test.');
  }
  for (const rule of args) {
    // Remove any paths just in case.
    const baseRule = path.basename(rule, '.js');
    buildTest(`tests/rules/${baseRule}.js`, runTest);
  }
};

target.lint = () => {
  let errors = 0;
  let makeFileCache = ' ';
  let jsCache = ' ';
  let testCache = ' ';
  let lastReturn;

  // using the cache locally to speed up linting process
  if (!process.env.TRAVIS) {
    makeFileCache = ' --cache --cache-file .cache/makefile_cache ';
    jsCache = ' --cache --cache-file .cache/js_cache ';
    testCache = ' --cache --cache-file .cache/test_cache ';
  }

  echo('Linting Makefile.js');
  lastReturn = exec(ESLINT + makeFileCache + MAKEFILE);
  if (lastReturn.code !== 0) {
    errors++;
  }

  echo('Linting JavaScript files');
  lastReturn = exec(ESLINT + jsCache + JS_FILES);
  if (lastReturn.code !== 0) {
    errors++;
  }

  echo('Linting JavaScript test files');
  lastReturn = exec(ESLINT + testCache + SOURCE_TEST_FILES);
  if (lastReturn.code !== 0) {
    errors++;
  }

  if (errors) {
    exit(1);
  }
};

target.test = () => {
  // target.lint();

  let errors = 0;
  let lastReturn = 0;

  rm('-r', './dist/tests');
  testAllFiles((outputFile) => {
    lastReturn = nodeCLI.exec(
        'istanbul', 'cover', MOCHA, `-- -R progress -t  ${MOCHA_TIMEOUT}`,
        '-c', outputFile);

    if (lastReturn.code !== 0) {
      errors++;
    }
  });

  // lastReturn = nodeCLI.exec(
  //     'istanbul', 'check-coverage',
  //     '--statement 99 --branch 98 --function 99 --lines 99');
  // if (lastReturn.code !== 0) {
  //   errors++;
  // }

  // target.browserify();

  // lastReturn = nodeCLI.exec('karma', "start karma.conf.js");
  // if (lastReturn.code !== 0) {
  //   errors++;
  // }

  if (errors) {
    exit(1);
  }

  // target.checkLicenses();
};
<|endoftext|>


<commit_before>/*!
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var fs = require("fs");
var path = require('path');

var runtime = require("@node-red/runtime");
var redUtil = require("@node-red/util");

var api = require("@node-red/editor-api");

var server = null;
var apiEnabled = false;

function checkVersion(userSettings) {
    var semver = require('semver');
    if (!semver.satisfies(process.version,">=8.9.0")) {
        // TODO: in the future, make this a hard error.
        // var e = new Error("Unsupported version of Node.js");
        // e.code = "unsupported_version";
        // throw e;
        userSettings.UNSUPPORTED_VERSION = process.version;
    }
}
/**
 * This module provides the full Node-RED application, with both the runtime
 * and editor components built in.
 *
 * The API this module exposes allows it to be embedded within another Node.js
 * application.
 *
 * @namespace node-red
 */
module.exports = {
    /**
     * Initialise the Node-RED application.
     * @param {server} httpServer - the HTTP server object to use
     * @param {Object} userSettings - an object containing the runtime settings
     * @memberof node-red
     */
    init: function(httpServer, userSettings) {
        if (!userSettings) {
            userSettings = httpServer;
            httpServer = null;
        }

        if (!userSettings.SKIP_BUILD_CHECK) {
            checkVersion(userSettings);
        }

        if (!userSettings.coreNodesDir) {
            userSettings.coreNodesDir = path.dirname(require.resolve("@node-red/nodes"))
        }
        redUtil.init(userSettings);
        if (userSettings.httpAdminRoot !== false) {
            // Initialise the runtime
            runtime.init(userSettings,httpServer,api);
            // Initialise the editor-api
            api.init(userSettings,httpServer,runtime.storage,runtime);
            // Attach the runtime admin app to the api admin app
            api.httpAdmin.use(runtime.httpAdmin);

            apiEnabled = true;
            server = httpServer;
        } else {
            runtime.init(userSettings, httpServer);
            apiEnabled = false;
            if (httpServer) {
                server = httpServer;
            } else {
                server = null;
            }
        }
        return;
    },
    /**
     * Start the Node-RED application.
     * @return {Promise} - resolves when complete
     * @memberof node-red
     */
    start: function() {
        // The top level red.js has always used 'otherwise' on the promise returned
        // here. This is a non-standard promise function coming from our early use
        // of the when.js library.
        // We want to remove all dependency on when.js as native Promises now exist.
        // But we have the issue that some embedders of Node-RED may have copied our
        // top-level red.js a bit too much.
        //
        let startPromise = runtime.start().then(function() {
            if (apiEnabled) {
                return api.start();
            }
        });
        startPromise._then = startPromise.then;
        startPromise.then = function(resolve,reject) {
            var inner = startPromise._then(resolve,reject);
            inner.otherwise = function(cb) {
                redUtil.log.error("**********************************************");
                redUtil.log.error("* Deprecated call to RED.start().otherwise() *");
                redUtil.log.error("* This will be removed in Node-RED 2.x       *");
                redUtil.log.error("* Use RED.start().catch() instead            *")
                redUtil.log.error("**********************************************");
                return inner.catch(cb);
            }
            return inner;
        }
        return startPromise;
    },
    /**
     * Stop the Node-RED application.
     *
     * Once called, Node-RED should not be restarted until the Node.JS process is
     * restarted.
     *
     * @return {Promise} - resolves when complete
     * @memberof node-red
     */
    stop: function() {
        return runtime.stop().then(function() {
            if (apiEnabled) {
                return api.stop();
            }
        })
    },

    /**
     * Logging utilities
     * @see @node-red/util_log
     * @memberof node-red
     */
    log: redUtil.log,

    /**
     * General utilities
     * @see @node-red/util_util
     * @memberof node-red
     */
    util: redUtil.util,

    /**
     * This provides access to the internal nodes module of the
     * runtime. The details of this API remain undocumented as they should not
     * be used directly.
     *
     * Most administrative actions should be performed use the runtime api
     * under [node-red.runtime]{@link node-red.runtime}.
     *
     * @memberof node-red
     */
    get nodes() { return runtime._.nodes },

    /**
     * Runtime events emitter
     * @see @node-red/util_events
     * @memberof node-red
     */
    events: redUtil.events,

    /**
     * Runtime hooks engine
     * @see @node-red/runtime_hooks
     * @memberof node-red
     */
    hooks: runtime.hooks,

    /**
     * This provides access to the internal settings module of the
     * runtime.
     *
     * @memberof node-red
     */
    get settings() { return runtime._.settings },

    /**
     * Get the version of the runtime
     * @return {String} - the runtime version
     * @function
     * @memberof node-red
     */
    get version() { return runtime._.version },

    /**
     * The express application for the Editor Admin API
     * @type ExpressApplication
     * @memberof node-red
     */
    get httpAdmin() { return api.httpAdmin },

    /**
     * The express application for HTTP Nodes
     * @type ExpressApplication
     * @memberof node-red
     */
    get httpNode() { return runtime.httpNode },

    /**
     * The HTTP Server used by the runtime
     * @type HTTPServer
     * @memberof node-red
     */
    get server() { return server },

    /**
     * The runtime api
     * @see @node-red/runtime
     * @memberof node-red
     */
    runtime: runtime,

    /**
     * The editor authentication api.
     * @see @node-red/editor-api_auth
     * @memberof node-red
     */
    auth: api.auth
};
<commit_msg>Allow coreNodesDir to be set to false<commit_after>/*!
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var fs = require("fs");
var path = require('path');

var runtime = require("@node-red/runtime");
var redUtil = require("@node-red/util");

var api = require("@node-red/editor-api");

var server = null;
var apiEnabled = false;

function checkVersion(userSettings) {
    var semver = require('semver');
    if (!semver.satisfies(process.version,">=8.9.0")) {
        // TODO: in the future, make this a hard error.
        // var e = new Error("Unsupported version of Node.js");
        // e.code = "unsupported_version";
        // throw e;
        userSettings.UNSUPPORTED_VERSION = process.version;
    }
}
/**
 * This module provides the full Node-RED application, with both the runtime
 * and editor components built in.
 *
 * The API this module exposes allows it to be embedded within another Node.js
 * application.
 *
 * @namespace node-red
 */
module.exports = {
    /**
     * Initialise the Node-RED application.
     * @param {server} httpServer - the HTTP server object to use
     * @param {Object} userSettings - an object containing the runtime settings
     * @memberof node-red
     */
    init: function(httpServer, userSettings) {
        if (!userSettings) {
            userSettings = httpServer;
            httpServer = null;
        }

        if (!userSettings.SKIP_BUILD_CHECK) {
            checkVersion(userSettings);
        }

        if (!userSettings.hasOwnProperty("coreNodesDir")) {
            userSettings.coreNodesDir = path.dirname(require.resolve("@node-red/nodes"))
        }
        redUtil.init(userSettings);
        if (userSettings.httpAdminRoot !== false) {
            // Initialise the runtime
            runtime.init(userSettings,httpServer,api);
            // Initialise the editor-api
            api.init(userSettings,httpServer,runtime.storage,runtime);
            // Attach the runtime admin app to the api admin app
            api.httpAdmin.use(runtime.httpAdmin);

            apiEnabled = true;
            server = httpServer;
        } else {
            runtime.init(userSettings, httpServer);
            apiEnabled = false;
            if (httpServer) {
                server = httpServer;
            } else {
                server = null;
            }
        }
        return;
    },
    /**
     * Start the Node-RED application.
     * @return {Promise} - resolves when complete
     * @memberof node-red
     */
    start: function() {
        // The top level red.js has always used 'otherwise' on the promise returned
        // here. This is a non-standard promise function coming from our early use
        // of the when.js library.
        // We want to remove all dependency on when.js as native Promises now exist.
        // But we have the issue that some embedders of Node-RED may have copied our
        // top-level red.js a bit too much.
        //
        let startPromise = runtime.start().then(function() {
            if (apiEnabled) {
                return api.start();
            }
        });
        startPromise._then = startPromise.then;
        startPromise.then = function(resolve,reject) {
            var inner = startPromise._then(resolve,reject);
            inner.otherwise = function(cb) {
                redUtil.log.error("**********************************************");
                redUtil.log.error("* Deprecated call to RED.start().otherwise() *");
                redUtil.log.error("* This will be removed in Node-RED 2.x       *");
                redUtil.log.error("* Use RED.start().catch() instead            *")
                redUtil.log.error("**********************************************");
                return inner.catch(cb);
            }
            return inner;
        }
        return startPromise;
    },
    /**
     * Stop the Node-RED application.
     *
     * Once called, Node-RED should not be restarted until the Node.JS process is
     * restarted.
     *
     * @return {Promise} - resolves when complete
     * @memberof node-red
     */
    stop: function() {
        return runtime.stop().then(function() {
            if (apiEnabled) {
                return api.stop();
            }
        })
    },

    /**
     * Logging utilities
     * @see @node-red/util_log
     * @memberof node-red
     */
    log: redUtil.log,

    /**
     * General utilities
     * @see @node-red/util_util
     * @memberof node-red
     */
    util: redUtil.util,

    /**
     * This provides access to the internal nodes module of the
     * runtime. The details of this API remain undocumented as they should not
     * be used directly.
     *
     * Most administrative actions should be performed use the runtime api
     * under [node-red.runtime]{@link node-red.runtime}.
     *
     * @memberof node-red
     */
    get nodes() { return runtime._.nodes },

    /**
     * Runtime events emitter
     * @see @node-red/util_events
     * @memberof node-red
     */
    events: redUtil.events,

    /**
     * Runtime hooks engine
     * @see @node-red/runtime_hooks
     * @memberof node-red
     */
    hooks: runtime.hooks,

    /**
     * This provides access to the internal settings module of the
     * runtime.
     *
     * @memberof node-red
     */
    get settings() { return runtime._.settings },

    /**
     * Get the version of the runtime
     * @return {String} - the runtime version
     * @function
     * @memberof node-red
     */
    get version() { return runtime._.version },

    /**
     * The express application for the Editor Admin API
     * @type ExpressApplication
     * @memberof node-red
     */
    get httpAdmin() { return api.httpAdmin },

    /**
     * The express application for HTTP Nodes
     * @type ExpressApplication
     * @memberof node-red
     */
    get httpNode() { return runtime.httpNode },

    /**
     * The HTTP Server used by the runtime
     * @type HTTPServer
     * @memberof node-red
     */
    get server() { return server },

    /**
     * The runtime api
     * @see @node-red/runtime
     * @memberof node-red
     */
    runtime: runtime,

    /**
     * The editor authentication api.
     * @see @node-red/editor-api_auth
     * @memberof node-red
     */
    auth: api.auth
};
<|endoftext|>


<commit_before>// @require ModernizrADownload

Ext.define('Densa.grid.PanelController', {
    extend: 'Densa.mvc.ViewController',
    uses: [ 'Ext.window.MessageBox' ],
    autoSync: true,
    autoLoad: false,
    deleteConfirmTitle: 'Delete',
    deleteConfirmText: 'Do you really wish to remove this entry?',
    exportProgressTitle: 'Export',
    exportProgressMsg: 'Exporting rows...',
    postBackUrl: null,

    grid: null,

    _store: null,

    optionalControl: {

        exportCsv: {
            selector: 'button#exportCsvButton',
            listeners: {
                click: 'onCsvExport'
            }
        },

        deleteButton: {
            selector: 'button#deleteButton',
            listeners: {
                click: 'onDeleteClick'
            }
        }

    },

    init: function()
    {
        if (!this.view) Ext.Error.raise('view config is required');
        if (!(this.view instanceof Ext.grid.Panel)) Ext.Error.raise('view config needs to be a Ext.grid.Panel');
        var grid = this.view;

        if (this.getDeleteButton) this.getDeleteButton().disable();
        grid.on('selectionchange', function(model, rows) {
            if (rows[0]) {
                var row = rows[0];
                if (this.getDeleteButton) this.getDeleteButton().enable();
            } else {
                if (this.getDeleteButton) this.getDeleteButton().disable();
            }
        }, this);
        Ext.each(grid.query('> toolbar[dock=top] field'), function(field) {
            field.on('change', function() {
                var filterId = 'filter-'+field.getName();
                var v = field.getValue();
                var filter = this.view.getStore().filters.get(filterId);
                if (!filter || filter.value != v) {
                    this.view.getStore().addFilter({
                        id: filterId,
                        property: field.getName(),
                        value: v
                    });
                }
            }, this, { buffer: 300 });
        }, this);

        if (grid.getStore()) this.onBindStore();
        Ext.Function.interceptAfter(grid, "bindStore", this.onBindStore, this);

        if (this.autoLoad) {
            this.view.getStore().load();
        }

    },

    onDeleteClick: function(options)
    {
        if (this.autoSync) {
            Ext.Msg.show({
                title: this.deleteConfirmTitle,
                msg: this.deleteConfirmText,
                icon: Ext.MessageBox.QUESTION,
                buttons: Ext.Msg.YESNO,
                scope: this,
                fn: function(button) {
                    if (button == 'yes') {
                        this.deleteSelected();
                        if (options.callback) options.callback.call(options.scope || this);
                    }
                }
            });
        } else {
            this.deleteSelected();
            if (options.callback) options.callback.call(options.scope || this);
        }
    },

    deleteSelected: function()
    {
        this.view.getStore().remove(this.view.getSelectionModel().getSelection());
        if (this.autoSync) {
            this.view.getStore().sync({
                success: function() {
                    this.fireViewEvent('savesuccess');
                },
                scope: this
            });
        }
        this.fireViewEvent('save');
    },

    onBindStore: function()
    {
        var s = this.view.getStore();
        this._store = s;
        Ext.each(this.view.query('pagingtoolbar'), function(i) {
            i.bindStore(s);
        }, this);
        Ext.each(this.view.query('> toolbar[dock=top] field'), function(field) {
            var filterId = 'filter-'+field.getName();
            var v = field.getValue();
            if (typeof v == 'undefined') v = null;
            this.view.getStore().addFilter({
                id: filterId,
                property: field.getName(),
                value: v
            }, false);
        }, this);

        this.fireViewEvent('bindstore', s);
    },

    onCsvExport: function()
    {
        var csv = '';

        //header
        var sep = '';
        Ext.each(this.view.columns, function(col) {
            if (!col.dataIndex) return;
            csv += sep+col.text;
            sep = ';';
        }, this);
        csv += '\n';

        var pageSize = 25;
        var totalCount = this._store.getTotalCount();
        var pageCount = Math.ceil(totalCount / pageSize);
        var page = 1;

        //create own store, so grid doesn't display loaded rows
        var store = this._store.self.create({
            model: this._store.model,
            filters: this._store.filters.items,
            sorters: this._store.sorters.items,
            pageSize: pageSize
        });

        Ext.Msg.show({
            title: this.exportProgressTitle,
            msg: this.exportProgressMsg,
            progress: true,
            buttons: Ext.Msg.CANCEL
        });

        loadPage.call(this);

        function loadPage()
        {
            if (!Ext.Msg.isVisible()) return; //export cancelled
            Ext.Msg.updateProgress((page-1)/pageCount);
            store.loadPage(page, {
                callback: function() {
                    exportRows.call(this);
                    if (page < pageCount) {
                        page++;
                        loadPage.call(this);
                    } else {
                        Ext.Msg.updateProgress(1);
                        createDownload.call(this);
                    }
                },
                scope: this
            });
        }

        function exportRows()
        {
            store.each(function(row) {
                var sep = '';
                Ext.each(this.view.columns, function(col) {
                    if (!col.dataIndex) return;
                    var val = row.get(col.dataIndex);
                    if (col.renderer) {
                        val = Ext.util.Format.stripTags(col.renderer(val, col, row));
                    }
                    if (!val) val = '';
                    csv += sep;
                    csv += String(val).replace('\\', '\\\\').replace(';', '\;').replace('\n', '\\n');
                    sep = ';';
                }, this);
                csv += '\n';
            }, this);
        }

        function createDownload()
        {
            var downloadFilename = 'export.csv';
            var URL = window.URL || window.webkiURL;
            if (false && window.Modernizr && Modernizr.adownload && URL && window.Blob) {
                //modern browser
                var blob = new Blob([csv]);
                var blobURL = URL.createObjectURL(blob);
                var a = this.view.el.createChild({
                    tag: 'a',
                    href: blobURL,
                    style: 'display:none;',
                    download: downloadFilename
                });
                a.dom.click();
                a.remove();
                Ext.Msg.hide();
            } else {
                //IE, Safari
                Ext.Ajax.request({
                    url: this.postBackUrl,
                    params: csv,
                    headers: {
                        'Content-Type': 'text/csv',
                        'X-Download-Filename': downloadFilename
                    },
                    success: function(response, options) {
                        if (!Ext.Msg.isVisible()) return; //export cancelled
                        var r = Ext.decode(response.responseText);
                        var a = this.view.el.createChild({
                            tag: 'a',
                            href: r.downloadUrl,
                            style: 'display:none;'
                        });
                        a.dom.click();
                        a.remove();
                        Ext.Msg.hide();
                    },
                    failure: function() {
                        Ext.Msg.hide();
                    },
                    scope: this
                });
            }
        }
    }
});
<commit_msg>Remove false, was only used for testing (oops)<commit_after>// @require ModernizrADownload

Ext.define('Densa.grid.PanelController', {
    extend: 'Densa.mvc.ViewController',
    uses: [ 'Ext.window.MessageBox' ],
    autoSync: true,
    autoLoad: false,
    deleteConfirmTitle: 'Delete',
    deleteConfirmText: 'Do you really wish to remove this entry?',
    exportProgressTitle: 'Export',
    exportProgressMsg: 'Exporting rows...',
    postBackUrl: null,

    grid: null,

    _store: null,

    optionalControl: {

        exportCsv: {
            selector: 'button#exportCsvButton',
            listeners: {
                click: 'onCsvExport'
            }
        },

        deleteButton: {
            selector: 'button#deleteButton',
            listeners: {
                click: 'onDeleteClick'
            }
        }

    },

    init: function()
    {
        if (!this.view) Ext.Error.raise('view config is required');
        if (!(this.view instanceof Ext.grid.Panel)) Ext.Error.raise('view config needs to be a Ext.grid.Panel');
        var grid = this.view;

        if (this.getDeleteButton) this.getDeleteButton().disable();
        grid.on('selectionchange', function(model, rows) {
            if (rows[0]) {
                var row = rows[0];
                if (this.getDeleteButton) this.getDeleteButton().enable();
            } else {
                if (this.getDeleteButton) this.getDeleteButton().disable();
            }
        }, this);
        Ext.each(grid.query('> toolbar[dock=top] field'), function(field) {
            field.on('change', function() {
                var filterId = 'filter-'+field.getName();
                var v = field.getValue();
                var filter = this.view.getStore().filters.get(filterId);
                if (!filter || filter.value != v) {
                    this.view.getStore().addFilter({
                        id: filterId,
                        property: field.getName(),
                        value: v
                    });
                }
            }, this, { buffer: 300 });
        }, this);

        if (grid.getStore()) this.onBindStore();
        Ext.Function.interceptAfter(grid, "bindStore", this.onBindStore, this);

        if (this.autoLoad) {
            this.view.getStore().load();
        }

    },

    onDeleteClick: function(options)
    {
        if (this.autoSync) {
            Ext.Msg.show({
                title: this.deleteConfirmTitle,
                msg: this.deleteConfirmText,
                icon: Ext.MessageBox.QUESTION,
                buttons: Ext.Msg.YESNO,
                scope: this,
                fn: function(button) {
                    if (button == 'yes') {
                        this.deleteSelected();
                        if (options.callback) options.callback.call(options.scope || this);
                    }
                }
            });
        } else {
            this.deleteSelected();
            if (options.callback) options.callback.call(options.scope || this);
        }
    },

    deleteSelected: function()
    {
        this.view.getStore().remove(this.view.getSelectionModel().getSelection());
        if (this.autoSync) {
            this.view.getStore().sync({
                success: function() {
                    this.fireViewEvent('savesuccess');
                },
                scope: this
            });
        }
        this.fireViewEvent('save');
    },

    onBindStore: function()
    {
        var s = this.view.getStore();
        this._store = s;
        Ext.each(this.view.query('pagingtoolbar'), function(i) {
            i.bindStore(s);
        }, this);
        Ext.each(this.view.query('> toolbar[dock=top] field'), function(field) {
            var filterId = 'filter-'+field.getName();
            var v = field.getValue();
            if (typeof v == 'undefined') v = null;
            this.view.getStore().addFilter({
                id: filterId,
                property: field.getName(),
                value: v
            }, false);
        }, this);

        this.fireViewEvent('bindstore', s);
    },

    onCsvExport: function()
    {
        var csv = '';

        //header
        var sep = '';
        Ext.each(this.view.columns, function(col) {
            if (!col.dataIndex) return;
            csv += sep+col.text;
            sep = ';';
        }, this);
        csv += '\n';

        var pageSize = 25;
        var totalCount = this._store.getTotalCount();
        var pageCount = Math.ceil(totalCount / pageSize);
        var page = 1;

        //create own store, so grid doesn't display loaded rows
        var store = this._store.self.create({
            model: this._store.model,
            filters: this._store.filters.items,
            sorters: this._store.sorters.items,
            pageSize: pageSize
        });

        Ext.Msg.show({
            title: this.exportProgressTitle,
            msg: this.exportProgressMsg,
            progress: true,
            buttons: Ext.Msg.CANCEL
        });

        loadPage.call(this);

        function loadPage()
        {
            if (!Ext.Msg.isVisible()) return; //export cancelled
            Ext.Msg.updateProgress((page-1)/pageCount);
            store.loadPage(page, {
                callback: function() {
                    exportRows.call(this);
                    if (page < pageCount) {
                        page++;
                        loadPage.call(this);
                    } else {
                        Ext.Msg.updateProgress(1);
                        createDownload.call(this);
                    }
                },
                scope: this
            });
        }

        function exportRows()
        {
            store.each(function(row) {
                var sep = '';
                Ext.each(this.view.columns, function(col) {
                    if (!col.dataIndex) return;
                    var val = row.get(col.dataIndex);
                    if (col.renderer) {
                        val = Ext.util.Format.stripTags(col.renderer(val, col, row));
                    }
                    if (!val) val = '';
                    csv += sep;
                    csv += String(val).replace('\\', '\\\\').replace(';', '\;').replace('\n', '\\n');
                    sep = ';';
                }, this);
                csv += '\n';
            }, this);
        }

        function createDownload()
        {
            var downloadFilename = 'export.csv';
            var URL = window.URL || window.webkiURL;
            if (window.Modernizr && Modernizr.adownload && URL && window.Blob) {
                //modern browser
                var blob = new Blob([csv]);
                var blobURL = URL.createObjectURL(blob);
                var a = this.view.el.createChild({
                    tag: 'a',
                    href: blobURL,
                    style: 'display:none;',
                    download: downloadFilename
                });
                a.dom.click();
                a.remove();
                Ext.Msg.hide();
            } else {
                //IE, Safari
                Ext.Ajax.request({
                    url: this.postBackUrl,
                    params: csv,
                    headers: {
                        'Content-Type': 'text/csv',
                        'X-Download-Filename': downloadFilename
                    },
                    success: function(response, options) {
                        if (!Ext.Msg.isVisible()) return; //export cancelled
                        var r = Ext.decode(response.responseText);
                        var a = this.view.el.createChild({
                            tag: 'a',
                            href: r.downloadUrl,
                            style: 'display:none;'
                        });
                        a.dom.click();
                        a.remove();
                        Ext.Msg.hide();
                    },
                    failure: function() {
                        Ext.Msg.hide();
                    },
                    scope: this
                });
            }
        }
    }
});
<|endoftext|>