<commit_before>import Ember from 'ember';
import layout from '../templates/components/froala-editor';
import isHTMLSafe from 'ember-string-ishtmlsafe-polyfill';
import assign from 'ember-assign-polyfill';

const FroalaEditorComponent = Ember.Component.extend({
  layout,

  // Enable the ability to .extend() this Component
  // to define "default" options that will get merged
  // with any options passed in as an attribute
  mergedProperties: [
    'defaultOptions'
  ],

  // Empty options hash, .extend() this Component
  // to define defaults. Ex: `defaultOptions:{ heightMin:200 }`
  defaultOptions: {},

  // Froala Editor prefixes all event names,
  // which is technically NOT a namespace,
  // and should be used when attaching event handlers
  // Note: Include the trailing dot
  eventPrefix: 'froalaEditor.',

  // Froala Editor event name that will trigger the
  // `update` action, mainly used to update the `content` value
  // and takes a cue from ember-one-way-controls
  updateEvent: 'contentChanged',

  // Option to return a SafeString when using on-*-getHtml event actions
  // By default, look at the current type of `content`
  returnSafeString: Ember.computed('content', function(){
    return isHTMLSafe( this.get('content') );
  }),

  // Few depreciations to help with the 2.3.5 to 2.4.0 transition
  contentBindingEvent: Ember.computed.deprecatingAlias('updateEvent', {
    id    : 'ember-froala-editor.contentBindingEvent',
    until : '2.3.7',
    url   : 'https://github.com/froala/ember-froala-editor/releases/tag/v2.4.0-rc.1'
  }),
  isSafeString: Ember.computed.deprecatingAlias('returnSafeString', {
    id    : 'ember-froala-editor.isSafeString',
    until : '2.3.7',
    url   : 'https://github.com/froala/ember-froala-editor/releases/tag/v2.4.0-rc.1'
  }),
  _optionsChanged: Ember.computed('options', {
    get() {
      // Skip the first "get" from the `init()` hook
      if ( !this.get('_optionsChangedFirst') ) {
        this.set('_optionsChangedFirst', true);
      } else {
        Ember.deprecate(
          "froala-editor 'options' changed post-initialization no longer updates the editor, instead use the related froala-editor methods",
          this.get('_optionsChangedWarned'),
          {
            id    : 'ember-froala-editor.optionsChanged',
            until : '2.3.7',
            url   : 'https://github.com/froala/ember-froala-editor/releases/tag/v2.4.0-rc.1'
          }
        );
        this.set('_optionsChangedWarned', true);
      }
    }
  }),

  // Private, internal Computed Property to handle SafeString support
  // and it will always return a string, even if `content` is null or undefined
  // Note: Both Strings and SafeStrings have a .toString() function
  _content: Ember.computed('content', {
    get() {
      let content = this.get('content');
      return (
        content && typeof content.toString === 'function' ?
        content.toString() :
        ''
      );
    }
  }),

  // Private, internal Computed Property to merge all the possible "options"
  _options: Ember.computed('defaultOptions', 'options', {
    get() {
      return assign(
        {},
        this.getWithDefault('defaultOptions', {}),
        this.getWithDefault('options', {})
      );
    }
  }),

  // Public facing API's for editor instance and state information
  // Note: The referenced properties are .set() on the init() hook
  editor            : Ember.computed.readOnly( '_editor'             ),
  editorInitializing: Ember.computed.readOnly( '_editorInitializing' ),
  editorInitialized : Ember.computed.readOnly( '_editorInitialized'  ),
  editorDestroying  : Ember.computed.readOnly( '_editorDestroying'   ),
  editorDestroyed   : Ember.computed.not(      'editorInitialized'   ),

  /**********
   * Ember Lifecycle Hooks
   *
   * These will basically get everything going, from setup to tear down.
   * They are listed in the order they are typically called.
   **********/

  // Initialize the per-instance property defaults,
  // mainly for internal state flags and details.
  // https://guides.emberjs.com/v2.9.0/object-model/classes-and-instances/#toc_initializing-instances
  init() {
    this._super( ...arguments );
    this.set( '_editor'            , null  );
    this.set( '_editorInitializing', false );
    this.set( '_editorInitialized' , false );
    this.set( '_editorDestroying'  , false );
    this.get( '_optionsChanged' ); // To monitor changes for depreciation notices
  }, // init()

  // Start the setup...
  didInsertElement() {
    this._super( ...arguments );
    Ember.run.schedule( 'afterRender', this, this.initEditor );
  }, // didInsertElement()

  // Look for changes in `content`
  // and update directly on the editor
  didUpdateAttrs() {
    this._super( ...arguments );

    let editor  = this.get('_editor');
    let content = this.get('_content');

    if ( editor && editor.html.get() !== content ) {
      editor.html.set( content );
    }

  }, // didUpdateAttrs()

  // Start the tear down...
  willDestroyElement() {
    this._super( ...arguments );
    this.destroyEditor();
  }, // willDestroyElement()

  /**********
   * Froala Editor Lifecycle Methods
   *
   * The following are methods used to setup and tear down the Froala Editor.
   * The Ember Lifecycle hooks will typically call these methods,
   * although they _could_ be called in a custom event handler.
   **********/

  // Responsible for the actual initialization of the Froala Editor
  // and sets up required event handlers and property observers
  initEditor() {

    // Do NOT re-initialize, user should call reinitEditor()
    if ( this.get('_editorInitialized') || this.get('_editorInitializing') ) {
      return;
    }

    // Update the state flag so we know initialization has started
    this.set( '_editorInitializing', true );

    // Init jQuery once...
    let $element = this.$();

    // Attach a one time 'froalaEditor.initialized' event handler
    // to know when initialization has finished, updating state flags
    // Note: Cannot be done via editor.events.on()
    //       since access to `editor` is not available yet
    $element.one(
      this.get('eventPrefix') + 'initialized',
      Ember.run.bind(this, 'didInitEditor')
    );

    // Actual initialization of the Froala Editor
    $element.froalaEditor( this.get('_options') );

  }, // initEditor()

  // Just a quick method to destroy then initialize the editor,
  // the called methods will properly handle the current state
  reinitEditor() {
    this.destroyEditor();
    this.initEditor();
  }, // reinitEditor()

  // Responsible for destroying the Froala Editor and tearing down
  // everything that was setup in the initEditor() method
  destroyEditor() {

    // Do NOT try to destroy if there is nothing to destroy...
    // Note: !this.get('_editorInitialized') == this.get('editorDestroyed')
    if ( !this.get('_editorInitialized') || this.get('_editorDestroying') ) {
      return;
    }

    // Update the state flag so we know destroying has started
    this.set( '_editorDestroying', true );

    // Note: Destroy event handler added in didInitEditor()

    // Actual destruction of the Froala Editor
    this.$().froalaEditor( 'destroy' );

  }, // destroyEditor()

  /**********
   * Froala Editor Event Handlers
   *
   * Callbacks for event listeners setup by the
   * Froala Editor Lifecycle Methods above and
   * any event callbacks added by didInitEditor()
   **********/

  // Triggered by the 'froalaEditor.initialized' event, updates
  // component state flags, sets the original html/content, and
  // attaches event handlers directly to the editor
  didInitEditor( event, editor, ...params ) {
    this.set( '_editorInitializing', false  );
    this.set( '_editorInitialized' , true   );
    this.set( '_editor'            , editor );

    // Set the initial HTML content
    editor.html.set( this.get('_content') );

    // Regex's used for replacing things in the property name
    const regexOnOrHtml = /(^on\-|\-getHtml$)/g;
    const regexHyphens  = /\-/g;
    const regexDots     = /\./g;

    // Go through all the property names looking for event handlers, on-*
    // Note: Use a for-in loop here over Object.keys() to get _all_ properties
    //       up the proto-chain, specifically needed when .extend()ing
    for ( let propertyName in this ) {

      // Just need on-* properties...
      // if ( !propertyName.startsWith('on-') ) // ES2015 (requires polyfill)
      if ( propertyName.indexOf('on-') !== 0 ) {
        continue;
      }

      // Initialization actions will be triggered later,
      // that way the component is setup before calling the event action
      // if ( propertyName.startsWith('on-initialized') ) // ES2015 (requires polyfill)
      if ( propertyName.indexOf('on-initialized') === 0 ) {
        continue;
      }

      // Convert the property name to what the event name would be
      let eventName = propertyName;
      eventName = eventName.replace( regexOnOrHtml, '' );
      eventName = eventName.replace( regexHyphens , '.');

      // Special use case for the 'popups.hide.[id]' event
      // Ember usage would be 'on-popups-hide-id=(action)'
      // https://www.froala.com/wysiwyg-editor/docs/events#popups.hide.[id]
      // if ( eventName.startsWith('popups.hide.') ) // ES2016 (requires polyfill)
      if ( eventName.indexOf('popups.hide.') === 0 ) {
        let id = eventName.replace( 'popups.hide.', '' );
        id = id.replace( regexDots, '-' ); // Convert back to hyphens
        eventName = `popups.hide.[${id}]`;
      }

      // Attach the appropriate event handler
      // if ( propertyName.endsWith('-getHtml') ) // ES2016 (requires polyfill)
      let getHtmlPos = propertyName.indexOf('-getHtml', -8);
      if ( getHtmlPos !== -1 && getHtmlPos === propertyName.length - 8 ) {
        editor.events.on(
          eventName,
          Ember.run.bind( this, this.didEditorEventReturnHtml, propertyName, editor ),
          true
        );
      } else {
        editor.events.on(
          eventName,
          Ember.run.bind( this, this.didEditorEvent, propertyName ),
          true
        );
      }

    } // for ()

    // Get the update action and event name
    let update      = this.get('update');
    let updateEvent = this.get('updateEvent');

    // If there is an update action and event,
    // then bind an event handler to get the html
    if ( update && updateEvent ) {
      editor.events.on(
        updateEvent,
        Ember.run.bind( this, this.didEditorEventReturnHtml, 'update', editor ),
        true
      );
    }

    // Add the destroy event handler
    // Run _after_ any other destroy handler
    editor.events.on(
      'destroy',
      Ember.run.bind( this, this.didDestroyEditor, editor ),
      false
    );

    // Fire the "initialized" event actions (if defined)
    if ( this.get('on-initialized') ) {
      this.didEditorEvent( 'on-initialized', ...params );
    }
    if ( this.get('on-initialized-getHtml') ) {
      this.didEditorEventReturnHtml( 'on-initialized-getHtml', editor, ...params );
    }

  }, // didInitEditor()

  // Triggered by the 'froalaEditor.destroy' event,
  // updates component state flags
  didDestroyEditor() {
    this.set( '_editor'           , null  );
    this.set( '_editorDestroying' , false );
    this.set( '_editorInitialized', false );
    // No need for '_editorDestroyed', the Computed
    // Property 'editorDestroyed' fills that need
  }, // didDestroyEditor()

  // Event handler for the 'on-*' property actions, which should be strings or functions
  // When the action is a function, return the handlers returned value back to the editor,
  // this allows support of "canceling" a Froala Editor event.
  // Note: This function is only triggered for Froala Editor events
  //       that have a related 'on-*' property defined for Ember actions
  didEditorEvent( propertyName, ...params ) {
    if ( typeof this[ propertyName ] === 'function' ) {
      return this[ propertyName ](
        this,
        ...params
      );
    } else {
      this.sendAction(
        propertyName,
        this,
        ...params
      );
    }
  }, // didEditorEvent()

  // Event handler for the 'on-*-getHtml' property actions,
  // returns the editors current html as the first argument.
  // This is mainly useful when combined with the (mut) helper
  // Note: This function is only triggered for Froala Editor events
  //       that have a related 'on-*-getHtml' property defined for Ember actions
  didEditorEventReturnHtml( propertyName, editor, ...params ) {

    // Get the HTML to return as the first argument
    let html = editor.html.get();

    // SafeString in = SafeString out..
    if ( this.get('returnSafeString') ) {
      html = Ember.String.htmlSafe( html );
    }

    // When the action is a function, return the handlers returned value back to the editor,
    // this allows support of "canceling" a Froala Editor event.
    if ( typeof this[ propertyName ] === 'function' ) {
      return this[ propertyName ](
        html,
        this,
        ...params
      );
    } else {
      this.sendAction(
        propertyName,
        html,
        this,
        ...params
      );
    }

  }, // didEditorEventReturnHtml()

  /**********
   * Froala Editor Method APIs
   *
   * Expose the Froala Editor methods in "Ember land",
   * basically proxies everything to the editor instance...
   **********/

  // Generic method() function that will proxy the call
  // to the Froala Editor public API for methods
  // Notes: Also handles editor state properly by returning a Promise if need be
  method( methodName ) {

    // special, added, ember-froala-editor specific method
    if ( methodName === 'reinit' ) {
      this.reinitEditor();

    // Editor should be initialized before calling the method
    } else if ( this.get('_editorInitialized') ) {
      return this.$().froalaEditor( ...arguments );

    } else {

      // Label for the following Promise, so it appears nicely in the Ember Inspector
      const promiseLabel = 'froala-editor: ' +
        `Delayed method() call to '${methodName}' until editor is initialized`
      ;

      // Instead of throwing an error, lets return a Promise
      // that will call the method once the editor _is_ initialized
      return new Ember.RSVP.Promise( (resolve, reject) => {

        // Create a one time event listener for the initialized event
        this.$().one(
          this.get('eventPrefix') + 'initialized',
          () => {

            // Try calling the Froala Editor method, returning the outcome
            try {
              resolve(
                this.$().froalaEditor( ...arguments )
              );
            } catch (e) {
              reject(e);
            }

          } // () => {}
        ); // this.$().one()

      }, promiseLabel ); // Ember.RSVP.Promise()

    } // else ( !editorInitialized )

  }, // method()

  // Also define the method() as a Component action, that way it could be invoked
  // as component.send('method', 'method.name', 'arg') from an event handler
  actions: {
    method() {
      this.method( ...arguments );
    } // method()
  } // :actions

}); // Ember.Component.extend()

FroalaEditorComponent.reopenClass({
  positionalParams: ['content', 'update', 'options']
});

export default FroalaEditorComponent;
<commit_msg>Update 2.4 depreciations within {{froala-editor}}<commit_after>import Ember from 'ember';
import layout from '../templates/components/froala-editor';
import isHTMLSafe from 'ember-string-ishtmlsafe-polyfill';
import assign from 'ember-assign-polyfill';

const FroalaEditorComponent = Ember.Component.extend({
  layout,

  // Enable the ability to .extend() this Component
  // to define "default" options that will get merged
  // with any options passed in as an attribute
  mergedProperties: [
    'defaultOptions'
  ],

  // Empty options hash, .extend() this Component
  // to define defaults. Ex: `defaultOptions:{ heightMin:200 }`
  defaultOptions: {},

  // Froala Editor prefixes all event names,
  // which is technically NOT a namespace,
  // and should be used when attaching event handlers
  // Note: Include the trailing dot
  eventPrefix: 'froalaEditor.',

  // Froala Editor event name that will trigger the
  // `update` action, mainly used to update the `content` value
  // and takes a cue from ember-one-way-controls
  updateEvent: 'contentChanged',

  // Option to return a SafeString when using on-*-getHtml event actions
  // By default, look at the current type of `content`
  returnSafeString: Ember.computed('content', function(){
    return isHTMLSafe( this.get('content') );
  }),

  // Few depreciations to help with the 2.3.5 to 2.4.0 transition
  // These can be removed for 2.5.0
  update(){
    Ember.deprecate(
      "froala-editor 'content' will no longer be updated by the component (two way bound), instead use the 'update' event action to '(mut)' the original property",
      this.get('_updateActionWarned'),
      {
        id    : 'ember-froala-editor.updateAction',
        until : '2.5.0',
        url   : 'https://github.com/froala/ember-froala-editor/releases/tag/2.4.0'
      }
    );
    this.set('_updateActionWarned', true);
  },
  contentBindingEvent: Ember.computed.deprecatingAlias('updateEvent', {
    id    : 'ember-froala-editor.contentBindingEvent',
    until : '2.5.0',
    url   : 'https://github.com/froala/ember-froala-editor/releases/tag/2.4.0'
  }),
  isSafeString: Ember.computed.deprecatingAlias('returnSafeString', {
    id    : 'ember-froala-editor.isSafeString',
    until : '2.5.5',
    url   : 'https://github.com/froala/ember-froala-editor/releases/tag/2.4.0'
  }),
  _optionsChanged: Ember.computed('options', {
    get() {
      // Skip the first "get" from the `init()` hook
      if ( !this.get('_optionsChangedFirst') ) {
        this.set('_optionsChangedFirst', true);
      } else {
        Ember.deprecate(
          "froala-editor 'options' changed post-initialization no longer updates the editor, instead use the related froala-editor methods",
          this.get('_optionsChangedWarned'),
          {
            id    : 'ember-froala-editor.optionsChanged',
            until : '2.5.0',
            url   : 'https://github.com/froala/ember-froala-editor/releases/tag/2.4.0'
          }
        );
        this.set('_optionsChangedWarned', true);
      }
    }
  }),

  // Private, internal Computed Property to handle SafeString support
  // and it will always return a string, even if `content` is null or undefined
  // Note: Both Strings and SafeStrings have a .toString() function
  _content: Ember.computed('content', {
    get() {
      let content = this.get('content');
      return (
        content && typeof content.toString === 'function' ?
        content.toString() :
        ''
      );
    }
  }),

  // Private, internal Computed Property to merge all the possible "options"
  _options: Ember.computed('defaultOptions', 'options', {
    get() {
      return assign(
        {},
        this.getWithDefault('defaultOptions', {}),
        this.getWithDefault('options', {})
      );
    }
  }),

  // Public facing API's for editor instance and state information
  // Note: The referenced properties are .set() on the init() hook
  editor            : Ember.computed.readOnly( '_editor'             ),
  editorInitializing: Ember.computed.readOnly( '_editorInitializing' ),
  editorInitialized : Ember.computed.readOnly( '_editorInitialized'  ),
  editorDestroying  : Ember.computed.readOnly( '_editorDestroying'   ),
  editorDestroyed   : Ember.computed.not(      'editorInitialized'   ),

  /**********
   * Ember Lifecycle Hooks
   *
   * These will basically get everything going, from setup to tear down.
   * They are listed in the order they are typically called.
   **********/

  // Initialize the per-instance property defaults,
  // mainly for internal state flags and details.
  // https://guides.emberjs.com/v2.11.0/object-model/classes-and-instances/#toc_initializing-instances
  init() {
    this._super( ...arguments );
    this.set( '_editor'            , null  );
    this.set( '_editorInitializing', false );
    this.set( '_editorInitialized' , false );
    this.set( '_editorDestroying'  , false );
    this.get( '_optionsChanged' ); // To monitor changes for depreciation notices
  }, // init()

  // Start the setup...
  didInsertElement() {
    this._super( ...arguments );
    Ember.run.schedule( 'afterRender', this, this.initEditor );
  }, // didInsertElement()

  // Look for changes in `content`
  // and update directly on the editor
  didUpdateAttrs() {
    this._super( ...arguments );

    let editor  = this.get('_editor');
    let content = this.get('_content');

    if ( editor && editor.html.get() !== content ) {
      editor.html.set( content );
    }

  }, // didUpdateAttrs()

  // Start the tear down...
  willDestroyElement() {
    this._super( ...arguments );
    this.destroyEditor();
  }, // willDestroyElement()

  /**********
   * Froala Editor Lifecycle Methods
   *
   * The following are methods used to setup and tear down the Froala Editor.
   * The Ember Lifecycle hooks will typically call these methods,
   * although they _could_ be called in a custom event handler.
   **********/

  // Responsible for the actual initialization of the Froala Editor
  // and sets up required event handlers and property observers
  initEditor() {

    // Do NOT re-initialize, user should call reinitEditor()
    if ( this.get('_editorInitialized') || this.get('_editorInitializing') ) {
      return;
    }

    // Update the state flag so we know initialization has started
    this.set( '_editorInitializing', true );

    // Init jQuery once...
    let $element = this.$();

    // Attach a one time 'froalaEditor.initialized' event handler
    // to know when initialization has finished, updating state flags
    // Note: Cannot be done via editor.events.on()
    //       since access to `editor` is not available yet
    $element.one(
      this.get('eventPrefix') + 'initialized',
      Ember.run.bind(this, 'didInitEditor')
    );

    // Actual initialization of the Froala Editor
    $element.froalaEditor( this.get('_options') );

  }, // initEditor()

  // Just a quick method to destroy then initialize the editor,
  // the called methods will properly handle the current state
  reinitEditor() {
    this.destroyEditor();
    this.initEditor();
  }, // reinitEditor()

  // Responsible for destroying the Froala Editor and tearing down
  // everything that was setup in the initEditor() method
  destroyEditor() {

    // Do NOT try to destroy if there is nothing to destroy...
    // Note: !this.get('_editorInitialized') == this.get('editorDestroyed')
    if ( !this.get('_editorInitialized') || this.get('_editorDestroying') ) {
      return;
    }

    // Update the state flag so we know destroying has started
    this.set( '_editorDestroying', true );

    // Note: Destroy event handler added in didInitEditor()

    // Actual destruction of the Froala Editor
    this.$().froalaEditor( 'destroy' );

  }, // destroyEditor()

  /**********
   * Froala Editor Event Handlers
   *
   * Callbacks for event listeners setup by the
   * Froala Editor Lifecycle Methods above and
   * any event callbacks added by didInitEditor()
   **********/

  // Triggered by the 'froalaEditor.initialized' event, updates
  // component state flags, sets the original html/content, and
  // attaches event handlers directly to the editor
  didInitEditor( event, editor, ...params ) {
    this.set( '_editorInitializing', false  );
    this.set( '_editorInitialized' , true   );
    this.set( '_editor'            , editor );

    // Set the initial HTML content
    editor.html.set( this.get('_content') );

    // Regex's used for replacing things in the property name
    const regexOnOrHtml = /(^on\-|\-getHtml$)/g;
    const regexHyphens  = /\-/g;
    const regexDots     = /\./g;

    // Go through all the property names looking for event handlers, on-*
    // Note: Use a for-in loop here over Object.keys() to get _all_ properties
    //       up the proto-chain, specifically needed when .extend()ing
    for ( let propertyName in this ) {

      // Just need on-* properties...
      // if ( !propertyName.startsWith('on-') ) // ES2015 (requires polyfill)
      if ( propertyName.indexOf('on-') !== 0 ) {
        continue;
      }

      // Initialization actions will be triggered later,
      // that way the component is setup before calling the event action
      // if ( propertyName.startsWith('on-initialized') ) // ES2015 (requires polyfill)
      if ( propertyName.indexOf('on-initialized') === 0 ) {
        continue;
      }

      // Convert the property name to what the event name would be
      let eventName = propertyName;
      eventName = eventName.replace( regexOnOrHtml, '' );
      eventName = eventName.replace( regexHyphens , '.');

      // Special use case for the 'popups.hide.[id]' event
      // Ember usage would be 'on-popups-hide-id=(action)'
      // https://www.froala.com/wysiwyg-editor/docs/events#popups.hide.[id]
      // if ( eventName.startsWith('popups.hide.') ) // ES2016 (requires polyfill)
      if ( eventName.indexOf('popups.hide.') === 0 ) {
        let id = eventName.replace( 'popups.hide.', '' );
        id = id.replace( regexDots, '-' ); // Convert back to hyphens
        eventName = `popups.hide.[${id}]`;
      }

      // Attach the appropriate event handler
      // if ( propertyName.endsWith('-getHtml') ) // ES2016 (requires polyfill)
      let getHtmlPos = propertyName.indexOf('-getHtml', -8);
      if ( getHtmlPos !== -1 && getHtmlPos === propertyName.length - 8 ) {
        editor.events.on(
          eventName,
          Ember.run.bind( this, this.didEditorEventReturnHtml, propertyName, editor ),
          true
        );
      } else {
        editor.events.on(
          eventName,
          Ember.run.bind( this, this.didEditorEvent, propertyName ),
          true
        );
      }

    } // for ()

    // Get the update action and event name
    let update      = this.get('update');
    let updateEvent = this.get('updateEvent');

    // If there is an update action and event,
    // then bind an event handler to get the html
    if ( update && updateEvent ) {
      editor.events.on(
        updateEvent,
        Ember.run.bind( this, this.didEditorEventReturnHtml, 'update', editor ),
        true
      );
    }

    // Add the destroy event handler
    // Run _after_ any other destroy handler
    editor.events.on(
      'destroy',
      Ember.run.bind( this, this.didDestroyEditor, editor ),
      false
    );

    // Fire the "initialized" event actions (if defined)
    if ( this.get('on-initialized') ) {
      this.didEditorEvent( 'on-initialized', ...params );
    }
    if ( this.get('on-initialized-getHtml') ) {
      this.didEditorEventReturnHtml( 'on-initialized-getHtml', editor, ...params );
    }

  }, // didInitEditor()

  // Triggered by the 'froalaEditor.destroy' event,
  // updates component state flags
  didDestroyEditor() {
    this.set( '_editor'           , null  );
    this.set( '_editorDestroying' , false );
    this.set( '_editorInitialized', false );
    // No need for '_editorDestroyed', the Computed
    // Property 'editorDestroyed' fills that need
  }, // didDestroyEditor()

  // Event handler for the 'on-*' property actions, which should be strings or functions
  // When the action is a function, return the handlers returned value back to the editor,
  // this allows support of "canceling" a Froala Editor event.
  // Note: This function is only triggered for Froala Editor events
  //       that have a related 'on-*' property defined for Ember actions
  didEditorEvent( propertyName, ...params ) {
    if ( typeof this[ propertyName ] === 'function' ) {
      return this[ propertyName ](
        this,
        ...params
      );
    } else {
      this.sendAction(
        propertyName,
        this,
        ...params
      );
    }
  }, // didEditorEvent()

  // Event handler for the 'on-*-getHtml' property actions,
  // returns the editors current html as the first argument.
  // This is mainly useful when combined with the (mut) helper
  // Note: This function is only triggered for Froala Editor events
  //       that have a related 'on-*-getHtml' property defined for Ember actions
  didEditorEventReturnHtml( propertyName, editor, ...params ) {

    // Get the HTML to return as the first argument
    let html = editor.html.get();

    // SafeString in = SafeString out..
    if ( this.get('returnSafeString') ) {
      html = Ember.String.htmlSafe( html );
    }

    // When the action is a function, return the handlers returned value back to the editor,
    // this allows support of "canceling" a Froala Editor event.
    if ( typeof this[ propertyName ] === 'function' ) {
      return this[ propertyName ](
        html,
        this,
        ...params
      );
    } else {
      this.sendAction(
        propertyName,
        html,
        this,
        ...params
      );
    }

  }, // didEditorEventReturnHtml()

  /**********
   * Froala Editor Method APIs
   *
   * Expose the Froala Editor methods in "Ember land",
   * basically proxies everything to the editor instance...
   **********/

  // Generic method() function that will proxy the call
  // to the Froala Editor public API for methods
  // Notes: Also handles editor state properly by returning a Promise if need be
  method( methodName ) {

    // special, added, ember-froala-editor specific method
    if ( methodName === 'reinit' ) {
      this.reinitEditor();

    // Editor should be initialized before calling the method
    } else if ( this.get('_editorInitialized') ) {
      return this.$().froalaEditor( ...arguments );

    } else {

      // Label for the following Promise, so it appears nicely in the Ember Inspector
      const promiseLabel = 'froala-editor: ' +
        `Delayed method() call to '${methodName}' until editor is initialized`
      ;

      // Instead of throwing an error, lets return a Promise
      // that will call the method once the editor _is_ initialized
      return new Ember.RSVP.Promise( (resolve, reject) => {

        // Create a one time event listener for the initialized event
        this.$().one(
          this.get('eventPrefix') + 'initialized',
          () => {

            // Try calling the Froala Editor method, returning the outcome
            try {
              resolve(
                this.$().froalaEditor( ...arguments )
              );
            } catch (e) {
              reject(e);
            }

          } // () => {}
        ); // this.$().one()

      }, promiseLabel ); // Ember.RSVP.Promise()

    } // else ( !editorInitialized )

  }, // method()

  // Also define the method() as a Component action, that way it could be invoked
  // as component.send('method', 'method.name', 'arg') from an event handler
  actions: {
    method() {
      this.method( ...arguments );
    } // method()
  } // :actions

}); // Ember.Component.extend()

FroalaEditorComponent.reopenClass({
  positionalParams: ['content', 'update', 'options']
});

export default FroalaEditorComponent;
<|endoftext|>


<commit_before>/**
 * @license MIT License
 *
 * Copyright (c) 2015 Tetsuharu OHZEKI <saneyuki.snyk@gmail.com>
 * Copyright (c) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import MessageActionCreator from '../../action/MessageActionCreator';
import Mousetrap from 'mousetrap';
import Rx from 'rx';
import UIActionCreator from '../../action/UIActionCreator';

export default class InputBoxViewController {

    /**
     *  @constructor
     *  @param  {Element}   element
     */
    constructor(element) {
        if (!element) {
            throw new Error();
        }

        /** @type   {Element}   */
        this._element = element;

        /** @type   {Element}   */
        this._textInput = element.querySelector('#input');

        /*eslint-disable valid-jsdoc */
        /** @type   {Rx.IDisposable}    */
        this._disposeFocus = UIActionCreator.getDispatcher().focusInputBox.subscribe(() => {
            this.focusInput();
        });
        /*eslint-enable*/

        /** @type   {Rx.Subject<Rx.AsyncSubject<string>>}   */
        this.queryCurrentChannel = new Rx.Subject();

        this._init();
    }

    _init() {
        this._element.addEventListener('submit', this);

        const shortcut = [
            'command+k',
            'ctrl+l',
            'ctrl+shift+l'
        ];
        Mousetrap.bind(shortcut, (aEvent) => {
            if (aEvent.target === this._textInput) {
                MessageActionCreator.clear();
            }
        });
    }

    /**
     *  @return {Element}
     */
    get textInput() {
        return this._textInput;
    }

    /**
     *  @param  {Event} aEvent
     *  @return {void}
     */
    handleEvent(aEvent) {
        switch (aEvent.type) {
            case 'submit':
                this.onSubmit(aEvent);
                break;
        }
    }

    /**
     *  @param  {Event} aEvent
     *  @return {void}
     */
    onSubmit(aEvent) {
        aEvent.preventDefault();

        const input = this._textInput;
        const text = input.value;
        // lock input field until dispatching to input command.
        input.readOnly = true;

        // FIXME: this is pretty ad hoc way.
        // should create a reasonable domain model approach.
        let query = new Rx.AsyncSubject();
        query.subscribe((id) => {
            MessageActionCreator.inputCommand(id, text);
            input.value = '';
            input.readOnly = false;
        });

        this.queryCurrentChannel.onNext(query);
    }

    /**
     *  @return {void}
     */
    focusInput() {
        this._textInput.focus();
    }
}
<commit_msg>Use Mousetrap binding to the specific element<commit_after>/**
 * @license MIT License
 *
 * Copyright (c) 2015 Tetsuharu OHZEKI <saneyuki.snyk@gmail.com>
 * Copyright (c) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import MessageActionCreator from '../../action/MessageActionCreator';
import Mousetrap from 'mousetrap';
import Rx from 'rx';
import UIActionCreator from '../../action/UIActionCreator';

export default class InputBoxViewController {

    /**
     *  @constructor
     *  @param  {Element}   element
     */
    constructor(element) {
        if (!element) {
            throw new Error();
        }

        /** @type   {Element}   */
        this._element = element;

        /** @type   {Element}   */
        this._textInput = element.querySelector('#input');

        /*eslint-disable valid-jsdoc */
        /** @type   {Rx.IDisposable}    */
        this._disposeFocus = UIActionCreator.getDispatcher().focusInputBox.subscribe(() => {
            this.focusInput();
        });
        /*eslint-enable*/

        /** @type   {Rx.Subject<Rx.AsyncSubject<string>>}   */
        this.queryCurrentChannel = new Rx.Subject();

        this._init();
    }

    _init() {
        this._element.addEventListener('submit', this);

        const shortcut = [
            'command+k',
            'ctrl+l',
            'ctrl+shift+l'
        ];
        Mousetrap(this._textInput).bind(shortcut, (aEvent) => {
            MessageActionCreator.clear();
        });
    }

    /**
     *  @return {Element}
     */
    get textInput() {
        return this._textInput;
    }

    /**
     *  @param  {Event} aEvent
     *  @return {void}
     */
    handleEvent(aEvent) {
        switch (aEvent.type) {
            case 'submit':
                this.onSubmit(aEvent);
                break;
        }
    }

    /**
     *  @param  {Event} aEvent
     *  @return {void}
     */
    onSubmit(aEvent) {
        aEvent.preventDefault();

        const input = this._textInput;
        const text = input.value;
        // lock input field until dispatching to input command.
        input.readOnly = true;

        // FIXME: this is pretty ad hoc way.
        // should create a reasonable domain model approach.
        let query = new Rx.AsyncSubject();
        query.subscribe((id) => {
            MessageActionCreator.inputCommand(id, text);
            input.value = '';
            input.readOnly = false;
        });

        this.queryCurrentChannel.onNext(query);
    }

    /**
     *  @return {void}
     */
    focusInput() {
        this._textInput.focus();
    }
}
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var alexa = require('alexa-nodekit');
var moment = require('moment');
var bassdrive = require('../components/bassdrive');
var router = express.Router();

router.post('/archive', function (req, res) {
  if(!(req.body.request && req.body.request.type)) {
    return res.jsonp({message: 'Not an echo request.'});
  }
  var sessionId;
  var userId;
  if(req.body.request.type === 'LaunchRequest') {
    alexa.launchRequest(req.body);
    sessionId = alexa.sessionId;
    userId = alexa.userId;
    alexa.response('Welcome to the bass drive app, you can say check for update to see if a new archive download is available.', {
      title: 'Bassdrive',
      subtitle: 'Welcome to the Bassdrive app.',
      content: 'Say "check for update" to find out if a new archive is available.'
    }, false, function (error, response) {
      if(error) {
        return res.status(500).jsonp({message: error});
      }
      return res.jsonp(response);
    });
  } else if (req.body.request.type === 'IntentRequest') {
    alexa.intentRequest(req.body);
    sessionId = alexa.sessionId;
    userId = alexa.userId;
    var intent = alexa.intentName;
    var slots = alexa.slots;
    if(intent === 'UpdateCheck') {
      bassdrive.getListings(6, bassdrive.OVERFIEND, function (error, listings) {
        if(error) {
          console.log(error);
          return res.status(500).jsonp({message: 'Could not process intention.'});
        }
        var latestListing = listings[0];
        var latestListingHuman = latestListing.time_ago;
        var latestListingDate = moment(latestListing.published_at).format('dddd, MMMM Do YYYY');
        var latestListingTime = moment(latestListing.published_at).format('hA zz');
        alexa.response('The latest archive is dated as ' + latestListingHuman + ' on ' + latestListingDate + ' at ' + latestListingTime, {
          title: 'Bassdrive',
          subtitle: 'Latest Archive',
          content: latestListingHuman + ' - ' + latestListingDate
        }, true, function (error, response) {
          if(error) {
            return res.status(500).jsonp(error);
          }
          return res.jsonp(response);
        });
      });
    } else {
      alexa.response('Unknown intention, please try a different command.', {
        title: 'Bassdrive',
        subtitle: 'Unknown intention.',
        content: 'Unknown intention, please try a different command.'
      }, true, function (error, response) {
        if(error) {
          return res.status(500).jsonp(error);
        }
        return res.jsonp(response);
      });
    }
  } else {
    alexa.sessionEndedRequest(req.body);
    sessionId = alexa.sessionId;
    userId = alexa.userId;
    var sessionEndReason = alexa.reason;
    alexa.response(function (error, response) {
      if(error) {
        return res.status(500).jsonp(error);
      }
      return res.jsonp(response);
    });
  }
});

module.exports = router;<commit_msg>Updated dates/times with UTC.<commit_after>'use strict';

var express = require('express');
var alexa = require('alexa-nodekit');
var moment = require('moment');
var bassdrive = require('../components/bassdrive');
var router = express.Router();

router.post('/archive', function (req, res) {
  if(!(req.body.request && req.body.request.type)) {
    return res.jsonp({message: 'Not an echo request.'});
  }
  var sessionId;
  var userId;
  if(req.body.request.type === 'LaunchRequest') {
    alexa.launchRequest(req.body);
    sessionId = alexa.sessionId;
    userId = alexa.userId;
    alexa.response('Welcome to the bass drive app, you can say check for update to see if a new archive download is available.', {
      title: 'Bassdrive',
      subtitle: 'Welcome to the Bassdrive app.',
      content: 'Say "check for update" to find out if a new archive is available.'
    }, false, function (error, response) {
      if(error) {
        return res.status(500).jsonp({message: error});
      }
      return res.jsonp(response);
    });
  } else if (req.body.request.type === 'IntentRequest') {
    alexa.intentRequest(req.body);
    sessionId = alexa.sessionId;
    userId = alexa.userId;
    var intent = alexa.intentName;
    var slots = alexa.slots;
    if(intent === 'UpdateCheck') {
      bassdrive.getListings(6, bassdrive.OVERFIEND, function (error, listings) {
        if(error) {
          console.log(error);
          return res.status(500).jsonp({message: 'Could not process intention.'});
        }
        var latestListing = listings[0];
        var latestListingHuman = latestListing.time_ago;
        var latestListingDate = moment(latestListing.published_at).utc().format('dddd, MMMM Do YYYY');
        var latestListingTime = moment(latestListing.published_at).utc().format('hA zz');
        alexa.response('The latest archive is dated as ' + latestListingHuman + ' on ' + latestListingDate + ' at ' + latestListingTime + ' UTC.', {
          title: 'Bassdrive',
          subtitle: 'Latest Archive',
          content: latestListingHuman + ' - ' + latestListingDate
        }, true, function (error, response) {
          if(error) {
            return res.status(500).jsonp(error);
          }
          return res.jsonp(response);
        });
      });
    } else {
      alexa.response('Unknown intention, please try a different command.', {
        title: 'Bassdrive',
        subtitle: 'Unknown intention.',
        content: 'Unknown intention, please try a different command.'
      }, true, function (error, response) {
        if(error) {
          return res.status(500).jsonp(error);
        }
        return res.jsonp(response);
      });
    }
  } else {
    alexa.sessionEndedRequest(req.body);
    sessionId = alexa.sessionId;
    userId = alexa.userId;
    var sessionEndReason = alexa.reason;
    alexa.response(function (error, response) {
      if(error) {
        return res.status(500).jsonp(error);
      }
      return res.jsonp(response);
    });
  }
});

module.exports = router;<|endoftext|>


<commit_before>const request = require('request');
var _ = require( 'underscore' );
var results = []
var survey_result = {}

if (!process.env.page_token) {
    console.log('Error: Specify page_token in environment');
    process.exit(1);
}

if (!process.env.verify_token) {
    console.log('Error: Specify verify_token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.facebookbot({
    debug: false,
    access_token: process.env.page_token,
    verify_token: process.env.verify_token,
});

var bot = controller.spawn({
});

controller.setupWebserver(process.env.port || 3000, function(err, webserver) {
    controller.createWebhookEndpoints(webserver, bot, function() {
        console.log('ONLINE!');
    });
});

controller.hears(['hi','Hi'], 'message_received', function(bot, message) {
    getProfile(message.user, function(err, profile) {
        var found_result = _.findWhere(results, {id: message.user});
        if (found_result == undefined){
            bot.reply(message, `Hello ${profile.first_name}`);
            askSurvey(bot, message);
        } else {
            bot.reply(message, `Hello ${profile.first_name}, you have already done the survey`);
            redoSurvey(bot, message)
        }
    });

});

// POSTBACK HANLDER
controller.on('facebook_postback', function(bot, message) {
    if (message.payload == 'yes(start)') {
        bot.reply(message, `Excellent! Lets get started.`);
        survey_result = {}
        getProfile(message.user, function(err, profile) {
            survey_result.id = message.user
            survey_result.user = `${profile.first_name} ${profile.last_name}`
            survey_result.gender = `${profile.gender}`
            survey_result.locale = `${profile.locale}`
            survey_result.timezone = `${profile.timezone}`
        });
        askRelationship(bot, message)
    } else if (message.payload == 'I love it' || message.payload == 'I hate it' || message.payload == 'Guilty pleasure') {

            if (survey_result.relationship == null) {
                survey_result.relationship = message.payload
                askDetail(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == 'I make it myself' || message.payload == 'KFC is my go to' || message.payload == 'Any way is good' || message.payload == 'Fried food is gross' || message.payload == `I don't eat animals` || message.payload == `It's a secret` || message.payload == `reward` ||message.payload == `cures hangover`) {

            if (survey_result.detail == null) {
                survey_result.detail = message.payload
                askMood(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == '(•‿•)' || message.payload == '(•︵•)' || message.payload == '(•_•)') {

            if (survey_result.mood == null) {
                survey_result.mood = message.payload
                askPreference(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == 'Chicken Parmesan' || message.payload == 'Double Down' || message.payload == 'Fried Drumsticks' || message.payload == 'Chicken Nuggets' || message.payload == 'Veggies') {

            if (survey_result.preference == null) {
                survey_result.preference = message.payload
                askHungry(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == 'yes' || message.payload == 'no' ) {

            if (survey_result.hungry == null) {
                survey_result.hungry = message.payload
                redoSurvey(bot, message)
            } else if (survey_result == {}){
            bot.reply(message, `You've already answered that question.`);
            }
    } else if (message.payload == 'no(survey)') {
        sayThanks(bot, message)
    } else if (message.payload == 'View results') {
        viewResults(bot, message)
    } else if (message.payload == 'Re-do survey') {
        bot.reply(message, `Excellent! Lets get started.`);
        survey_result = {}
        getProfile(message.user, function(err, profile) {
            survey_result.id = message.user
            survey_result.user = `${profile.first_name} ${profile.last_name}`
            survey_result.gender = `${profile.gender}`
            survey_result.locale = `${profile.locale}`
            survey_result.timezone = `${profile.timezone}`

        });
        askRelationship(bot, message)
    } else if (message.payload == `I'm done`) {
        saveResults(bot, message)
        sayThanks(bot, message)
    }
});
// View Results
viewResults = function(bot, message) {
    var found_result = _.findWhere(results, {id: message.user});
    var text = JSON.stringify(found_result)
    bot.reply(message, `${text}`);
}
// QUESTIONS
askSurvey = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'Do you have a moment to answer some questions about fried chicken ?',
            'buttons':[
                {
                'type':'postback',
                'title':`Yes`,
                'payload':`yes(start)`
                },
                {
                'type':'postback',
                'title':`No`,
                'payload':`no(survey)`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });
}

askRelationship = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text':  'What would you say your relationship is with fried chicken ?',
            'buttons':[
                {
                'type':'postback',
                'title':'I love it',
                'payload':'I love it'
                },
                {
                'type':'postback',
                'title':'I hate it',
                'payload':'I hate it'
                },
                {
                'type':'postback',
                'title':'Guilty pleasure',
                'payload':'Guilty pleasure'
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });
}

askDetail = function(bot, message) {
    if (message.payload == 'I love it') {
        var attachment = {
            'type':'template',
            'payload':{
                'template_type':'button',
                'text': 'What is your favourite way to eat fried chicken ?',
                'buttons':[
                    {
                    'type':'postback',
                    'title':'I make it myself',
                    'payload':'I make it myself'
                    },
                    {
                    'type':'postback',
                    'title':'KFC is my go to',
                    'payload':'KFC is my go to'
                    },
                    {
                    'type':'postback',
                    'title':'Any way is good',
                    'payload':'Any way is good'
                    }
                ]
            }
        };

        bot.reply(message, {
            attachment: attachment,
        });

    } else if (message.payload == 'I hate it') {
        var attachment = {
            'type':'template',
            'payload':{
                'template_type':'button',
                'text': 'Not a fan ? tell me more.',
                'buttons':[
                    {
                    'type':'postback',
                    'title':'Fried food is gross',
                    'payload':'Fried food is gross'
                    },
                    {
                    'type':'postback',
                    'title':`I don't eat animals`,
                    'payload':`I don't eat animals`
                    },
                    {
                    'type':'postback',
                    'title':`It's a secret`,
                    'payload':`It's a secret`
                    }
                ]
            }
        };

        bot.reply(message, {
            attachment: attachment,
        });

    } else if (message.payload == 'Guilty pleasure') {
        var attachment = {
            'type':'template',
            'payload':{
                'template_type':'button',
                'text':  'Guilty pleasure you say, tell me more.',
                'buttons':[
                    {
                    'type':'postback',
                    'title':'When Hungover',
                    'payload':'cures hangover'
                    },
                    {
                    'type':'postback',
                    'title':'Reward for myself',
                    'payload':'reward'
                    },
                    {
                    'type':'postback',
                    'title':`It's a secret`,
                    'payload':`It's a secret`
                    }
                ]
            }
        };

        bot.reply(message, {
            attachment: attachment,
        });

    } else  {
        bot.reply(message, 'oops')
    }

}

askMood = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'What is your current mood ?',
            'buttons':[
                {
                'type':'postback',
                'title':`(•‿•)`,
                'payload':`(•‿•)`
                },
                {
                'type':'postback',
                'title':`(•︵•)`,
                'payload':`(•︵•)`
                },
                {
                'type':'postback',
                'title':`(•_•)`,
                'payload':`(•_•)`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });

}

askPreference = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload': {
                'template_type': 'generic',
                'elements': [
                    {
                        'title': 'Chicken Parmesan',
                        'image_url': 'http://fiber-international.com/wp-content/uploads/2015/04/800x600-chicken.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Chicken Parmesan'
                            }
                        ]
                    },
                    {
                        'title': 'Double Down',
                        'image_url': 'http://assets.bwbx.io/images/ieMg5BCeWkWU/v1/-1x-1.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Double Down'
                            }
                        ]
                    },
                    {
                        'title': 'Fried Drumsticks',
                        'image_url': 'https://i.ytimg.com/vi/G8hbFO-r2nQ/maxresdefault.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Fried Drumsticks'
                            }
                        ]
                    },
                    {
                        'title': 'Chicken Nuggets',
                        'image_url': 'http://www.urbanmommies.com/wp-content/uploads/McDonalds-Chicken-Nuggets.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Chicken Nuggets'
                            }
                        ]
                    },
                    {
                        'title': 'Veggies',
                        'image_url': 'http://www.stevensonfitness.com/wp-content/uploads/2014/10/veggies.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Veggies'
                            }
                        ]
                    }
                ]
            }
    };

    bot.reply(message, {
        attachment: attachment,
    });

    bot.reply(message, 'Which of these meals your you like to be eating right now ?');
}

askHungry = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'Have I made you hungry ?',
            'buttons':[
                {
                'type':'postback',
                'title':`yes`,
                'payload':`yes`
                },
                {
                'type':'postback',
                'title':`no`,
                'payload':`no`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });

}
// OTHER RESPONSES
sayThanks = function(bot, message) {
  bot.reply(message, 'OK! thanks for your time');
}
//saving
saveResults = function(bot, message) {
    results.push(survey_result)
    console.log(results)
}

redoSurvey = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'OK! thanks for your time',
            'buttons':[
                {
                'type':'postback',
                'title':`view results`,
                'payload':`View results`
                },
                {
                'type':'postback',
                'title':`re-do survey`,
                'payload':`Re-do survey`
                },
                {
                'type':'postback',
                'title':`I'm done`,
                'payload':`I'm done`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });

}
// GET USER INFO !!!
getProfile = function (id, cb) {
    if (!cb) cb = Function.prototype

    request({
      method: 'GET',
      uri: `https://graph.facebook.com/v2.6/${id}`,
      qs: {
        fields: 'first_name,last_name,profile_pic,gender,locale,timezone',
        access_token: process.env.page_token
      },
      json: true
    }, function(err, res, body) {
      if (err) return cb(err)
      if (body.error) return cb(body.error)

      cb(null, body)
    })
}
<commit_msg>one result per unique user<commit_after>const request = require('request');
var _ = require( 'underscore' );
var results = []
var survey_result = {}

if (!process.env.page_token) {
    console.log('Error: Specify page_token in environment');
    process.exit(1);
}

if (!process.env.verify_token) {
    console.log('Error: Specify verify_token in environment');
    process.exit(1);
}

var Botkit = require('./lib/Botkit.js');
var os = require('os');

var controller = Botkit.facebookbot({
    debug: false,
    access_token: process.env.page_token,
    verify_token: process.env.verify_token,
});

var bot = controller.spawn({
});

controller.setupWebserver(process.env.port || 3000, function(err, webserver) {
    controller.createWebhookEndpoints(webserver, bot, function() {
        console.log('ONLINE!');
    });
});

controller.hears(['hi','Hi'], 'message_received', function(bot, message) {
    getProfile(message.user, function(err, profile) {
        var found_result = _.findWhere(results, {id: message.user});
        if (found_result == undefined){
            bot.reply(message, `Hello ${profile.first_name}`);
            askSurvey(bot, message);
        } else {
            bot.reply(message, `Hello ${profile.first_name}, you have already done the survey`);
            redoSurvey(bot, message)
        }
    });

});

// POSTBACK HANLDER
controller.on('facebook_postback', function(bot, message) {
    if (message.payload == 'yes(start)') {
        bot.reply(message, `Excellent! Lets get started.`);
        survey_result = {}
        getProfile(message.user, function(err, profile) {
            survey_result.id = message.user
            survey_result.user = `${profile.first_name} ${profile.last_name}`
            survey_result.gender = `${profile.gender}`
            survey_result.locale = `${profile.locale}`
            survey_result.timezone = `${profile.timezone}`
        });
        askRelationship(bot, message)
    } else if (message.payload == 'I love it' || message.payload == 'I hate it' || message.payload == 'Guilty pleasure') {

            if (survey_result.relationship == null) {
                survey_result.relationship = message.payload
                askDetail(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == 'I make it myself' || message.payload == 'KFC is my go to' || message.payload == 'Any way is good' || message.payload == 'Fried food is gross' || message.payload == `I don't eat animals` || message.payload == `It's a secret` || message.payload == `reward` ||message.payload == `cures hangover`) {

            if (survey_result.detail == null) {
                survey_result.detail = message.payload
                askMood(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == '(•‿•)' || message.payload == '(•︵•)' || message.payload == '(•_•)') {

            if (survey_result.mood == null) {
                survey_result.mood = message.payload
                askPreference(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == 'Chicken Parmesan' || message.payload == 'Double Down' || message.payload == 'Fried Drumsticks' || message.payload == 'Chicken Nuggets' || message.payload == 'Veggies') {

            if (survey_result.preference == null) {
                survey_result.preference = message.payload
                askHungry(bot, message)
            } else {
                bot.reply(message, `You've already answered that question.`);
            }

    } else if (message.payload == 'yes' || message.payload == 'no' ) {

            if (survey_result.hungry == null) {
                survey_result.hungry = message.payload
                redoSurvey(bot, message)
            } else if (survey_result == {}){
            bot.reply(message, `You've already answered that question.`);
            }
    } else if (message.payload == 'no(survey)') {
        sayThanks(bot, message)
    } else if (message.payload == 'View results') {
        saveResults(bot, message)
        viewResults(bot, message)
    } else if (message.payload == 'Re-do survey') {
        bot.reply(message, `Excellent! Lets get started.`);
        deleteEntry(bot, message)
        survey_result = {}
        getProfile(message.user, function(err, profile) {
            survey_result.id = message.user
            survey_result.user = `${profile.first_name} ${profile.last_name}`
            survey_result.gender = `${profile.gender}`
            survey_result.locale = `${profile.locale}`
            survey_result.timezone = `${profile.timezone}`

        });
        askRelationship(bot, message)
    } else if (message.payload == `I'm done`) {
        saveResults(bot, message)
        sayThanks(bot, message)
    }
});
// check for user
deleteEntry = function(bot, message){
    var found_result = _.findWhere(results, {id: message.user});
    var result = results.filter(function (value) {return (value !== found_result);});
    results = result
}
// View Results
viewResults = function(bot, message) {
    var found_result = _.findWhere(results, {id: message.user});
    var text = JSON.stringify(found_result)
    bot.reply(message, `${text}`);
}
// QUESTIONS
askSurvey = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'Do you have a moment to answer some questions about fried chicken ?',
            'buttons':[
                {
                'type':'postback',
                'title':`Yes`,
                'payload':`yes(start)`
                },
                {
                'type':'postback',
                'title':`No`,
                'payload':`no(survey)`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });
}

askRelationship = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text':  'What would you say your relationship is with fried chicken ?',
            'buttons':[
                {
                'type':'postback',
                'title':'I love it',
                'payload':'I love it'
                },
                {
                'type':'postback',
                'title':'I hate it',
                'payload':'I hate it'
                },
                {
                'type':'postback',
                'title':'Guilty pleasure',
                'payload':'Guilty pleasure'
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });
}

askDetail = function(bot, message) {
    if (message.payload == 'I love it') {
        var attachment = {
            'type':'template',
            'payload':{
                'template_type':'button',
                'text': 'What is your favourite way to eat fried chicken ?',
                'buttons':[
                    {
                    'type':'postback',
                    'title':'I make it myself',
                    'payload':'I make it myself'
                    },
                    {
                    'type':'postback',
                    'title':'KFC is my go to',
                    'payload':'KFC is my go to'
                    },
                    {
                    'type':'postback',
                    'title':'Any way is good',
                    'payload':'Any way is good'
                    }
                ]
            }
        };

        bot.reply(message, {
            attachment: attachment,
        });

    } else if (message.payload == 'I hate it') {
        var attachment = {
            'type':'template',
            'payload':{
                'template_type':'button',
                'text': 'Not a fan ? tell me more.',
                'buttons':[
                    {
                    'type':'postback',
                    'title':'Fried food is gross',
                    'payload':'Fried food is gross'
                    },
                    {
                    'type':'postback',
                    'title':`I don't eat animals`,
                    'payload':`I don't eat animals`
                    },
                    {
                    'type':'postback',
                    'title':`It's a secret`,
                    'payload':`It's a secret`
                    }
                ]
            }
        };

        bot.reply(message, {
            attachment: attachment,
        });

    } else if (message.payload == 'Guilty pleasure') {
        var attachment = {
            'type':'template',
            'payload':{
                'template_type':'button',
                'text':  'Guilty pleasure you say, tell me more.',
                'buttons':[
                    {
                    'type':'postback',
                    'title':'When Hungover',
                    'payload':'cures hangover'
                    },
                    {
                    'type':'postback',
                    'title':'Reward for myself',
                    'payload':'reward'
                    },
                    {
                    'type':'postback',
                    'title':`It's a secret`,
                    'payload':`It's a secret`
                    }
                ]
            }
        };

        bot.reply(message, {
            attachment: attachment,
        });

    } else  {
        bot.reply(message, 'oops')
    }

}

askMood = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'What is your current mood ?',
            'buttons':[
                {
                'type':'postback',
                'title':`(•‿•)`,
                'payload':`(•‿•)`
                },
                {
                'type':'postback',
                'title':`(•︵•)`,
                'payload':`(•︵•)`
                },
                {
                'type':'postback',
                'title':`(•_•)`,
                'payload':`(•_•)`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });

}

askPreference = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload': {
                'template_type': 'generic',
                'elements': [
                    {
                        'title': 'Chicken Parmesan',
                        'image_url': 'http://fiber-international.com/wp-content/uploads/2015/04/800x600-chicken.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Chicken Parmesan'
                            }
                        ]
                    },
                    {
                        'title': 'Double Down',
                        'image_url': 'http://assets.bwbx.io/images/ieMg5BCeWkWU/v1/-1x-1.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Double Down'
                            }
                        ]
                    },
                    {
                        'title': 'Fried Drumsticks',
                        'image_url': 'https://i.ytimg.com/vi/G8hbFO-r2nQ/maxresdefault.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Fried Drumsticks'
                            }
                        ]
                    },
                    {
                        'title': 'Chicken Nuggets',
                        'image_url': 'http://www.urbanmommies.com/wp-content/uploads/McDonalds-Chicken-Nuggets.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Chicken Nuggets'
                            }
                        ]
                    },
                    {
                        'title': 'Veggies',
                        'image_url': 'http://www.stevensonfitness.com/wp-content/uploads/2014/10/veggies.jpg',
                        'buttons': [
                            {
                                'type': 'postback',
                                'title': 'Choose',
                                'payload': 'Veggies'
                            }
                        ]
                    }
                ]
            }
    };

    bot.reply(message, {
        attachment: attachment,
    });

    bot.reply(message, 'Which of these meals your you like to be eating right now ?');
}

askHungry = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'Have I made you hungry ?',
            'buttons':[
                {
                'type':'postback',
                'title':`yes`,
                'payload':`yes`
                },
                {
                'type':'postback',
                'title':`no`,
                'payload':`no`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });

}
// OTHER RESPONSES
sayThanks = function(bot, message) {
  bot.reply(message, 'OK! thanks for your time');
}
//saving
saveResults = function(bot, message) {
    results.push(survey_result)
    console.log(results)
}

redoSurvey = function(bot, message) {
    var attachment = {
        'type':'template',
        'payload':{
            'template_type':'button',
            'text': 'OK! thanks for your time',
            'buttons':[
                {
                'type':'postback',
                'title':`view results`,
                'payload':`View results`
                },
                {
                'type':'postback',
                'title':`re-do survey`,
                'payload':`Re-do survey`
                },
                {
                'type':'postback',
                'title':`I'm done`,
                'payload':`I'm done`
                }
            ]
        }
    };

    bot.reply(message, {
        attachment: attachment,
    });

}
// GET USER INFO !!!
getProfile = function (id, cb) {
    if (!cb) cb = Function.prototype

    request({
      method: 'GET',
      uri: `https://graph.facebook.com/v2.6/${id}`,
      qs: {
        fields: 'first_name,last_name,profile_pic,gender,locale,timezone',
        access_token: process.env.page_token
      },
      json: true
    }, function(err, res, body) {
      if (err) return cb(err)
      if (body.error) return cb(body.error)

      cb(null, body)
    })
}
<|endoftext|>


<commit_before>var assert = require('assert');
var types = require('./types');
var R = require('../../..');

var IO = require('../IO');

describe('IO', function() {
    var f1 = function() { console.log('IO 1'); return '1 '; };
    var f2 = function(x) { console.log('IO 2'); return x + '2 '; };
    var f3 = function(x) { console.log('IO 3'); return x + '3 '; };
    var i1 = IO(f1);
    var i2 = IO(f2);

    it('is a Functor', function() {
        var fTest = types.functor;
        assert.equal(true, fTest.iface(i1));
        assert.equal(true, fTest.id(i1));
        assert.equal(true, fTest.compose(i1, f2, f3));
    });

    it('is an Apply', function() {
        var aTest = types.apply;
        var a = IO(function() { return R.add(1); });
        var b = IO(function() { return R.always(2); });
        var c = IO(R.always(4));

        assert.equal(true, aTest.iface(i1));
        assert.equal(true, aTest.compose(a, b, c));
    });

    it('is an Applicative', function() {
        var aTest = types.applicative;

        assert.equal(true, aTest.iface(i1));
        assert.equal(true, aTest.id(IO, i2));
        assert.equal(true, aTest.homomorphic(i1, R.add(3), 46));
        assert.equal(true, aTest.interchange(
            IO(function() { return R.multiply(20); }),
            IO(function() { return R.multiply(0.5); }),
            73
        ));
    });

    it('is a Chain', function() {
        var cTest = types.chain;
        var c = IO(function() {
            return IO(function() {
                return IO(function() {
                    return 3;
                });
            });
        });
        assert.equal(true, cTest.iface(i1));
        assert.equal(true, cTest.associative(c, R.I, R.I));
    });

    it('is a Monad', function() {
        var mTest = types.monad;
        assert.equal(true, mTest.iface(i1));
    });

});
<commit_msg>Replace console.log calls with assert-able calls.<commit_after>var assert = require('assert');
var types = require('./types');
var R = require('../../..');

var IO = require('../IO');

describe('IO', function() {
    var logger = (function() {
        var results = [];
        return {
            log: function(str) {results.push(str);},
            clear: function() {results = [];},
            report: function() {return results.join(' ~ ');}
        };
    }());
    var f1 = function() { logger.log('IO 1'); return '1 '; };
    var f2 = function(x) { logger.log('IO 2'); return x + '2 '; };
    var f3 = function(x) { logger.log('IO 3'); return x + '3 '; };
    var i1 = IO(f1);
    var i2 = IO(f2);

    beforeEach(function() {
        logger.clear();
    });

    it('is a Functor', function() {
        var fTest = types.functor;
        assert.equal(true, fTest.iface(i1));
        assert.equal(true, fTest.id(i1));
        assert.equal(logger.report(), 'IO 1 ~ IO 1');
        logger.clear();
        assert.equal(true, fTest.compose(i1, f2, f3));
        assert.equal(logger.report(), 'IO 1 ~ IO 3 ~ IO 2 ~ IO 1 ~ IO 3 ~ IO 2');
    });

    it('is an Apply', function() {
        var aTest = types.apply;
        var a = IO(function() { return R.add(1); });
        var b = IO(function() { return R.always(2); });
        var c = IO(R.always(4));

        assert.equal(true, aTest.iface(i1));
        assert.equal(true, aTest.compose(a, b, c));
    });

    it('is an Applicative', function() {
        var aTest = types.applicative;

        assert.equal(true, aTest.iface(i1));
        assert.equal(true, aTest.id(IO, i2));
        assert.equal(logger.report(), 'IO 2 ~ IO 2');
        assert.equal(true, aTest.homomorphic(i1, R.add(3), 46));
        assert.equal(true, aTest.interchange(
            IO(function() { return R.multiply(20); }),
            IO(function() { return R.multiply(0.5); }),
            73
        ));
    });

    it('is a Chain', function() {
        var cTest = types.chain;
        var c = IO(function() {
            return IO(function() {
                return IO(function() {
                    return 3;
                });
            });
        });
        assert.equal(true, cTest.iface(i1));
        assert.equal(true, cTest.associative(c, R.I, R.I));
    });

    it('is a Monad', function() {
        var mTest = types.monad;
        assert.equal(true, mTest.iface(i1));
    });

});
<|endoftext|>


<commit_before>//  "use strict";
var VERBOSE = "VERBOSE" in process.env;
var TESTS = "TESTS" in process.env ? process.env.TESTS.split(/,/) : null;

var ShExParser = require("../lib/ShExParser").Parser;
var ShExValidator = require("../lib/ShExValidator");

var N3 = require("n3");
var turtleParser = N3.Parser();
var fs = require("fs");
var path = require("path");
var expect = require("chai").expect;
var assert = require("chai").assert;
var findPath = require("./findPath.js");

var schemasPath = findPath("schemas");
var validationsPath = findPath("validations");
var manifestFile = validationsPath+"manifest.jsonld";
var negSyntaxTestsPath = findPath("negativeSyntax");

describe("A ShEx validator", function () {
  // var b = function () {  };
  // it("is a toy", function () {
  //   expect({a:1, b: b}).to.deep.equal({a:1, b: b});
  // });

  var shexParser = new ShExParser();
  // Ensure the same blank node identifiers are used in every test
  beforeEach(function () {
    shexParser._resetBlanks();
  });

  var tests = parseJSONFile(manifestFile)["@graph"][0]["mf:entries"];

  if (TESTS)
    tests = tests.filter(function (t) {
      return TESTS.indexOf(t["@id"]) !== -1 ||
        TESTS.indexOf(t["@id"].substr(1)) !== -1 ||
        TESTS.indexOf(t.schema) !== -1 ||
        TESTS.indexOf(t.data) !== -1 ||
        TESTS.indexOf(t.result) !== -1;
    });

  tests.forEach(function (test) {
// try {
    var schemaFile = path.join(schemasPath, test.schema);
    var dataFile = path.join(validationsPath, test.data);
    var resultsFile = test.result ? path.join(validationsPath, test.result) : null;
    var schema = shexParser.parse(fs.readFileSync(schemaFile, "utf8"));
    var referenceResult = resultsFile ? parseJSONFile(resultsFile) : null;

    assert(referenceResult !== null || test["@type"] === "shext:ValidationFailure");
    // var start = schema.start;
    // if (start === undefined && Object.keys(schema.shapes).length === 1)
    //   start = Object.keys(schema.shapes)[0];

    it("should validate data '" + (VERBOSE ? dataFile : test.data) + // test title
       "' against schema '" + (VERBOSE ? schemaFile : test.schema) +
       "' and get '" + (VERBOSE ? resultsFile : test.result) + "'." ,
       function (report) {                                             // test action
         var store = N3.Store();
         var validator = ShExValidator(schema);  // @@ Why does a validator fail when constructed outside the call to it()?!
         turtleParser.parse(
           fs.readFileSync(dataFile, "utf8"),
           function (error, triple, prefixes) {
             if (error) {
               report("error parsing " + dataFile + ": " + error);
             } else if (triple) {
               store.addTriple(triple)
             } else {
               try {
                 var validationResult = validator.validate(store, test.focus, test.shape);
                 if (VERBOSE) console.log("result   :" + JSON.stringify(validationResult));
                 if (VERBOSE) console.log("expected :" + JSON.stringify(referenceResult));
                 expect(validationResult).to.deep.equal(referenceResult);
                 report();
               } catch (e) {
                 report(e);
               }
             }
           });
       });
// } catch (e) {
//   throw new Error("in "+test["@id"]+" "+e);
// }
  });
});

// Parses a JSON object, restoring `undefined` values
function parseJSONFile(filename) {
  try {
    var string = fs.readFileSync(filename, "utf8");
    var object = JSON.parse(string);
    return /"\{undefined\}"/.test(string) ? restoreUndefined(object) : object;
  } catch (e) {
    throw new Error("error reading " + filename + ": " + e);
  }
}

// Recursively replace values of "{undefined}" by `undefined`
function restoreUndefined(object) {
  for (var key in object) {
    var item = object[key];
    if (typeof item === "object")
      object[key] = restoreUndefined(item);
    else if (item === "{undefined}")
      object[key] = undefined;
  }
  return object;
}
<commit_msg>~ changed prefix to sht<commit_after>//  "use strict";
var VERBOSE = "VERBOSE" in process.env;
var TESTS = "TESTS" in process.env ? process.env.TESTS.split(/,/) : null;

var ShExParser = require("../lib/ShExParser").Parser;
var ShExValidator = require("../lib/ShExValidator");

var N3 = require("n3");
var turtleParser = N3.Parser();
var fs = require("fs");
var path = require("path");
var expect = require("chai").expect;
var assert = require("chai").assert;
var findPath = require("./findPath.js");

var schemasPath = findPath("schemas");
var validationsPath = findPath("validations");
var manifestFile = validationsPath+"manifest.jsonld";
var negSyntaxTestsPath = findPath("negativeSyntax");

describe("A ShEx validator", function () {
  // var b = function () {  };
  // it("is a toy", function () {
  //   expect({a:1, b: b}).to.deep.equal({a:1, b: b});
  // });

  var shexParser = new ShExParser();
  // Ensure the same blank node identifiers are used in every test
  beforeEach(function () {
    shexParser._resetBlanks();
  });

  var tests = parseJSONFile(manifestFile)["@graph"][0]["mf:entries"];

  if (TESTS)
    tests = tests.filter(function (t) {
      return TESTS.indexOf(t["@id"]) !== -1 ||
        TESTS.indexOf(t["@id"].substr(1)) !== -1 ||
        TESTS.indexOf(t.schema) !== -1 ||
        TESTS.indexOf(t.data) !== -1 ||
        TESTS.indexOf(t.result) !== -1;
    });

  tests.forEach(function (test) {
// try {
    var schemaFile = path.join(schemasPath, test.schema);
    var dataFile = path.join(validationsPath, test.data);
    var resultsFile = test.result ? path.join(validationsPath, test.result) : null;
    var schema = shexParser.parse(fs.readFileSync(schemaFile, "utf8"));
    var referenceResult = resultsFile ? parseJSONFile(resultsFile) : null;

    assert(referenceResult !== null || test["@type"] === "sht:ValidationFailure");
    // var start = schema.start;
    // if (start === undefined && Object.keys(schema.shapes).length === 1)
    //   start = Object.keys(schema.shapes)[0];

    it("should validate data '" + (VERBOSE ? dataFile : test.data) + // test title
       "' against schema '" + (VERBOSE ? schemaFile : test.schema) +
       "' and get '" + (VERBOSE ? resultsFile : test.result) + "'." ,
       function (report) {                                             // test action
         var store = N3.Store();
         var validator = ShExValidator(schema);  // @@ Why does a validator fail when constructed outside the call to it()?!
         turtleParser.parse(
           fs.readFileSync(dataFile, "utf8"),
           function (error, triple, prefixes) {
             if (error) {
               report("error parsing " + dataFile + ": " + error);
             } else if (triple) {
               store.addTriple(triple)
             } else {
               try {
                 var validationResult = validator.validate(store, test.focus, test.shape);
                 if (VERBOSE) console.log("result   :" + JSON.stringify(validationResult));
                 if (VERBOSE) console.log("expected :" + JSON.stringify(referenceResult));
                 expect(validationResult).to.deep.equal(referenceResult);
                 report();
               } catch (e) {
                 report(e);
               }
             }
           });
       });
// } catch (e) {
//   throw new Error("in "+test["@id"]+" "+e);
// }
  });
});

// Parses a JSON object, restoring `undefined` values
function parseJSONFile(filename) {
  try {
    var string = fs.readFileSync(filename, "utf8");
    var object = JSON.parse(string);
    return /"\{undefined\}"/.test(string) ? restoreUndefined(object) : object;
  } catch (e) {
    throw new Error("error reading " + filename + ": " + e);
  }
}

// Recursively replace values of "{undefined}" by `undefined`
function restoreUndefined(object) {
  for (var key in object) {
    var item = object[key];
    if (typeof item === "object")
      object[key] = restoreUndefined(item);
    else if (item === "{undefined}")
      object[key] = undefined;
  }
  return object;
}
<|endoftext|>


<commit_before>import Ember from 'ember';

const {
  Component,
  get
} = Ember;

export default Component.extend({
  tagName: 'input',
  type: 'text',
  attributeBindings: [
    'accept',
    'autocomplete',
    'autosave',
    'checked',
    'dir',
    'disabled',
    'formaction',
    'formenctype',
    'formmethod',
    'formnovalidate',
    'formtarget',
    'height',
    'inputmode',
    'lang',
    'list',
    'max',
    'min',
    'multiple',
    'name',
    'pattern',
    'placeholder',
    'size',
    'step',
    'type',
    'value',
    'width'
  ],
  KEY_EVENTS: {
    '13': 'onenter',
    '27': 'onescape'
  },
  _sanitizedValue: undefined,

  input() { this._handleChangeEvent(); },
  change() { this._handleChangeEvent(); },
  keyUp(event) { this._interpretKeyEvents(event); },

  _interpretKeyEvents(event) {
    let methodName = this.KEY_EVENTS[event.keyCode];

    if (methodName) {
      this._sanitizedValue = null;
      this._processNewValue.call(this, methodName, this._readAppropriateAttr());
    }
  },

  _handleChangeEvent() {
    this._processNewValue.call(this, 'update', this._readAppropriateAttr());
  },

  _readAppropriateAttr() {
    let attr;
    if (get(this, 'type') === 'checkbox') {
      attr = 'checked';
    } else {
      attr = 'value';
    }

    return this.readDOMAttr(attr);
  },

  _processNewValue(methodName, rawValue) {
    let value = this.sanitizeInput(rawValue);
    let action = this.attrs[methodName];

    if (this._sanitizedValue !== value) {
      this._sanitizedValue = value;
      if (action) {
        action(value);
      }
    }
  },

  sanitizeInput(input) {
    return input;
  },

  init() {
    this._super(...arguments);
    this._sanitizedValue = get(this, 'value') || get(this, 'checked');
  },

  didReceiveAttrs() {
    this._super(...arguments);
    this._processNewValue.call(this, 'update', get(this, 'value') || get(this, 'checked'));
  }
});
<commit_msg>Auto merge of #14 - cogniteev:add-maxlength-binding, r=poteto<commit_after>import Ember from 'ember';

const {
  Component,
  get
} = Ember;

export default Component.extend({
  tagName: 'input',
  type: 'text',
  attributeBindings: [
    'accept',
    'autocomplete',
    'autosave',
    'checked',
    'dir',
    'disabled',
    'formaction',
    'formenctype',
    'formmethod',
    'formnovalidate',
    'formtarget',
    'height',
    'inputmode',
    'lang',
    'list',
    'max',
    'maxlength',
    'min',
    'multiple',
    'name',
    'pattern',
    'placeholder',
    'size',
    'step',
    'type',
    'value',
    'width'
  ],
  KEY_EVENTS: {
    '13': 'onenter',
    '27': 'onescape'
  },
  _sanitizedValue: undefined,

  input() { this._handleChangeEvent(); },
  change() { this._handleChangeEvent(); },
  keyUp(event) { this._interpretKeyEvents(event); },

  _interpretKeyEvents(event) {
    let methodName = this.KEY_EVENTS[event.keyCode];

    if (methodName) {
      this._sanitizedValue = null;
      this._processNewValue.call(this, methodName, this._readAppropriateAttr());
    }
  },

  _handleChangeEvent() {
    this._processNewValue.call(this, 'update', this._readAppropriateAttr());
  },

  _readAppropriateAttr() {
    let attr;
    if (get(this, 'type') === 'checkbox') {
      attr = 'checked';
    } else {
      attr = 'value';
    }

    return this.readDOMAttr(attr);
  },

  _processNewValue(methodName, rawValue) {
    let value = this.sanitizeInput(rawValue);
    let action = this.attrs[methodName];

    if (this._sanitizedValue !== value) {
      this._sanitizedValue = value;
      if (action) {
        action(value);
      }
    }
  },

  sanitizeInput(input) {
    return input;
  },

  init() {
    this._super(...arguments);
    this._sanitizedValue = get(this, 'value') || get(this, 'checked');
  },

  didReceiveAttrs() {
    this._super(...arguments);
    this._processNewValue.call(this, 'update', get(this, 'value') || get(this, 'checked'));
  }
});
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

var Weather = require('../models/weather');

router.get('/', function(req, res, next){
  Weather.find(function(err, weatherData){
    //res.json(weatherData);
    res.send(weatherData);
  });
});

router.post('/', function(req, res, next){
  var weather = new Weather();

  //Set Values
  weather.temp = req.body.temp;
  weather.humidity = req.body.humidity;
  //weather.timeCreated = req.body.timecreated;
  //weather.protonID = req.body.guid;

  weather.save(function(err){
    if(!err){
      res.send({
        success: true,
        data: weather
      });
    }
    else {
      res.send({
        success: false,
        error: err.toString()
      });
    }
  });
});

module.exports = router;
<commit_msg>Edited POST route<commit_after>var express = require('express');
var router = express.Router();

var Weather = require('../models/weather');

router.get('/', function(req, res, next){
  Weather.find(function(err, weatherData){
    //res.json(weatherData);
    res.send(weatherData);
  });
});

router.post('/', function(req, res, next){
  var weather = new Weather();

  //Set Values
  weather.temp = req.body.temp;
  weather.humidity = req.body.humidity;
  weather.timeCreated = req.body.timecreated;
  weather.protonID = req.body.guid;

  weather.save(function(err){
    if(!err){
      res.send({
        success: true,
        data: weather
      });
    }
    else {
      res.send({
        success: false,
        error: err.toString()
      });
    }
  });
});

module.exports = router;
<|endoftext|>