<commit_before>/*global  require:true, module:true */
/*jshint boss:true, browser:true */

(function() {
  var VERSION = "1.1.2";
  var Backbone = this.Backbone;
  var _ = this._;

  // Load Backbone and Underscore using require if we don't have the global for
  // some reason
  if (typeof require === "function") {
    if (!Backbone) Backbone = require("backbone");
    if (!_) _ = require("underscore");
  }

  function ensureArray(ob){
    return _.isArray(ob) ? ob : [ob];
  }

  function isConstructor(o) {
    return o.prototype && o === o.prototype.constructor;
  }

  /**
   * Few tested opinions on how to handle deeply nested views in Backbone.js
   * focusing on encapsulation and reusability.
   *
   * <https://github.com/epeli/backbone.viewmaster>
   *
   * @class Backbone.ViewMaster
   * @extends Backbone.View
   * */
  Backbone.ViewMaster = Backbone.View.extend({

    constructor: function() {
      Backbone.View.prototype.constructor.apply(this, arguments);

      /**
       * Boolean indicating whether this view has been rendered at least once.
       * Set by the `render` method and used by the parent view.
       *
       * @property rendered
       * @type Boolean
       **/
      this.rendered = false;

      /**
      * Reference to the parent view.
      *
      * @property parent
      * @type Object
      **/
      this.parent = null;

      /**
      * Object containing all child views. Key are CSS selectors and values
      * are arrays of views
      *
      * @private
      * @property _views
      * @type Object
      **/
      this._views = {};

      /**
      * Array of views to be removed on next `refreshViews` call.
      *
      * @private
      * @property _remove
      * @type Array
      **/
      this._remove = [];

      /**
      * Record of view container with changes
      *
      * @private
      * @type Object
      **/
      this._dirty = {};
    },

    /**
     * Template function. User must override this!
     *
     * @method template
     * @param {Object} context
     * @return {String / DOM object} rendered template
     **/
    template: function(){
      throw new Error("Template function not defined!");
    },

    /**
     * Returns the context object for the `template` method.
     *
     * Default: `this.model.toJSON()` or an empty object if view has no model.
     *
     * @method context
     * @return {Object} context
     **/
    context: function() {
      if (this.model) return this.model.toJSON();
      return {};
    },

    /**
     * Render this view with `this.template(this.context())` and refresh child
     * views with `this.refreshViews(options)`.
     *
     * @method render
     * @param {Object} options
     *   @param {Boolean} [options.force]
     *   force rerendering of child views
     * @return {Object} this
     **/
    render: function(opts) {
      opts = _.extend({}, opts);

      // Remove subviews with detach. This way they don't lose event handlers
      // and parent view can rerender itself
      this.eachView(function(sel, view) {
        view.$el.detach();
      });

      opts.detached = true;

      this.$el.html(this.template(this.context()));

      this.afterTemplate();

      // Mark this view as rendered. Parent view wont try to render this
      // anymore unless force:true is passed
      this.rendered = true;

      this.refreshViews(opts);

      return this;
    },

    /**
     * Overrideable by the view implementor.
     *
     * Called after the template has been rendered but before adding child
     * views.  This is perfect place to hook up any jQuery plugings using
     * `this.$` or `this.$el` or set access points to view DOM elements
     *
     *     this.$el("button").tooltipPlugin();
     *     this.$saveButton = this.$("button.save");
     *
     * because they cannot have any side affects to child views.
     *
     * @method aftertemplate
     **/
    afterTemplate: function() {},

    /**
     * Refresh any child view changes made with `setView`, `appendView`,
     * `prependView` or `insertView`. Tries to avoid doing any work unless it
     * is absolutely necessary or `{ force: true }` is passed.
     *
     * It calls `render` on child views only when a child has never been
     * rendered before.
     *
     * @method refreshViews
     * @param {Object} options
     *   @param {Boolean} [options.force]
     *   force rerendering of child views
     * @return {Object} this
     **/
    refreshViews: function(opts) {
      opts = _.extend({}, opts);
      var self = this;
      var dirty = this._dirty;

      var oldView;
      while (oldView = this._remove.shift()) oldView.remove();

      this.eachView(function(sel, view) {
        // Detach view from container if it is dirty to update possible view
        // order changes.
        var refresh = dirty[sel] || opts.force;

        // Render child view only if it never has been rendered before.
        var render = !view.rendered || opts.force;

        if (refresh && !opts.detached) view.$el.detach();
        if (!view.rendered || opts.force) view.render(opts);
        if (refresh || opts.detached) self.$(sel).append(view.el);
      });

      this._dirty = {};
      return this;
    },

    /**
     * Detach view from its parent but keep it in DOM.
     *
     * @private
     * @method _removeParent
     * @return {Object} this
     **/
    _removeParent: function() {

      if (!this.parent) return;

      var key;
      for (key in this.parent._views) {
        this.parent._views[key] = _.without(
          this.parent._views[key], this
        );
      }

      this.parent = null;

      return this;
    },

    /**
     * Prepare views to be nested in this view. Set up event bubbling and
     * remove possible previous parent.
     *
     * @private
     * @method _prepareViews
     * @param {Object/Constructor/Array} view(s) One or array of view objects
     * or Constructors
     **/
    _prepareViews: function(views) {
      var self = this;
      views = _.map(ensureArray(views), _.bind(this._createInstance, this));

      _.each(views, function(view) {
        // when parent is changing remove it from previous
        if (view.parent && view.parent !== self) view._removeParent();
        view.parent = self;
      });

      return views;
    },

    _createInstance: function(view) {
      return isConstructor(view) ? new view({
        model: this.model,
        collection: this.collection
      }) : view;
    },

    /**
    * Like `trigger` but the events are bubbled all the way up to all
    * parent views too.
    *
    * @method bubble
    * @param {String} event
    * @param {any} [args*] Zero or more objects to be passed along with the
    * event
    * @return {Object} this
    **/
    bubble: function() {
      var args = _.toArray(arguments);
      this.trigger.apply(this, args);
      if (this.parent) this.parent.bubble.apply(this.parent, args);
      return this;
    },

    /**
    * Opposite of `bubble`. The events are broadcasted all the way down to all
    * child views too.
    *
    * @method broadcast
    * @param {String} event
    * @param {any} [args*] Zero or more objects to be passed along with the
    * event
    * @return {Object} this
    **/
    broadcast: function() {
      var args = _.toArray(arguments);
      this.trigger.apply(this, args);
      this.eachView(function(sel, view) {
        view.broadcast.apply(view, args);
      });
      return this;
    },

    /**
     * Iterate each child view.
     *
     * @method eachView
     * @param {Function} iterator
     *   @param {String} iterator.selector CSS selector for the view container
     *   @param {Object} iterator.view The view object
     * @return {Object} this
     **/
    eachView: function(fn) {
      _.each(this._views, function(views, sel) {
        _.each(views, function(view) {
          fn(sel, view);
        });
      });
    },

    /**
     * Set a view or an array of views to a given view container.
     *
     * If previous set of views is replaced by a new set — the views not
     * present in the new set will be discarded with `remove` on the next
     * `refreshViews` call unless detached with `detach` method.
     *
     * If `views` parameter is a constructor function it will be instantiated
     * with `model` and `collection` of the parent. This applies to
     * `appendView`, `prependview` and `insertView` too.
     *
     * @protected
     * @method setView
     * @param {String} selector CSS selector for the view container
     * @param {Object/Constructor/Array} view(s) One or array of view objects
     * or Constructors
     **/
    setView: function(sel, currentViews) {
      var previousViews;
      currentViews = this._prepareViews(currentViews);

      if (previousViews = this._views[sel]) {
        this._remove = this._remove.concat(
          _.difference(previousViews, currentViews)
        );
      }

      this._views[sel] = currentViews;
      this._dirty[sel] = true;
      return this;
    },

    /**
     * Insert views to specific index in the view array.
     *
     * @protected
     * @method insertView
     * @param {String} selector CSS selector for the view container
     * @param {Number} index Index to insert view(s)
     * @param {Object/Constructor/Array} view(s) Views to insert
     **/
    insertView: function(sel, index, views) {
      var current;
      views = this._prepareViews(views);
      if (current = this._views[sel]) {
        current.slice().splice.apply(current, [index, 0].concat(views));
      }
      else {
        this._views[sel] = views;
      }

      this._dirty[sel] = true;
      return this;
    },

    /**
     * Append a views to a given view container.
     *
     * @protected
     * @method appendView
     * @param {String} selector CSS selector for the view container
     * @param {Object/Constructor/Array} view(s) Views to append
     **/
    appendView: function(sel, views) {
      this._views[sel] = (this._views[sel] || []).concat(
        this._prepareViews(views)
      );

      this._dirty[sel] = true;
      return this;
    },

    /**
     * Prepend a views to a given view container.
     *
     * @protected
     * @method prependView
     * @param {String} selector CSS selector for the view container
     * @param {Object/Constructor/Array} view(s) Views to prepend
     **/
    prependView: function(sel, views) {
      this._views[sel] = this._prepareViews(views).concat(
        this._views[sel] || []
      );

      this._dirty[sel] = true;
      return this;
    },

    /**
     * Return array of views for given container or null if the container have
     * not been used.
     *
     * @protected
     * @method getViews
     * @param {String} selector container CSS selector
     * @return {Array/null} views
     **/
    getViews: function(sel) {
      var views;
      if (views = this._views[sel]) return views.slice();
      return null;
    },

    /**
     * Detaches view from its parent and DOM.
     *
     * @method detach
     * @return {Object} this
     **/
    detach: function() {
      this._removeParent();
      this.$el.detach();
      return this;
    },

    /**
     * Discard this view and all its children for good. Clears all events bound
     * for the view. Use `detach` instead if you need to use the view again
     * some time later.
     *
     * @method remove
     * @return {Object} this
     **/
    remove: function() {
      Backbone.View.prototype.remove.apply(this, arguments);
      this._removeParent();
      this.eachView(function(sel, view) {
        view.remove();
      });
      return this;
    }

  });

  Backbone.ViewMaster.VERSION = VERSION;
  if (typeof module !== 'undefined') module.exports = Backbone.ViewMaster;

})();
<commit_msg>Remove unused variable<commit_after>/*global  require:true, module:true */
/*jshint boss:true, browser:true */

(function() {
  var VERSION = "1.1.2";
  var Backbone = this.Backbone;
  var _ = this._;

  // Load Backbone and Underscore using require if we don't have the global for
  // some reason
  if (typeof require === "function") {
    if (!Backbone) Backbone = require("backbone");
    if (!_) _ = require("underscore");
  }

  function ensureArray(ob){
    return _.isArray(ob) ? ob : [ob];
  }

  function isConstructor(o) {
    return o.prototype && o === o.prototype.constructor;
  }

  /**
   * Few tested opinions on how to handle deeply nested views in Backbone.js
   * focusing on encapsulation and reusability.
   *
   * <https://github.com/epeli/backbone.viewmaster>
   *
   * @class Backbone.ViewMaster
   * @extends Backbone.View
   * */
  Backbone.ViewMaster = Backbone.View.extend({

    constructor: function() {
      Backbone.View.prototype.constructor.apply(this, arguments);

      /**
       * Boolean indicating whether this view has been rendered at least once.
       * Set by the `render` method and used by the parent view.
       *
       * @property rendered
       * @type Boolean
       **/
      this.rendered = false;

      /**
      * Reference to the parent view.
      *
      * @property parent
      * @type Object
      **/
      this.parent = null;

      /**
      * Object containing all child views. Key are CSS selectors and values
      * are arrays of views
      *
      * @private
      * @property _views
      * @type Object
      **/
      this._views = {};

      /**
      * Array of views to be removed on next `refreshViews` call.
      *
      * @private
      * @property _remove
      * @type Array
      **/
      this._remove = [];

      /**
      * Record of view container with changes
      *
      * @private
      * @type Object
      **/
      this._dirty = {};
    },

    /**
     * Template function. User must override this!
     *
     * @method template
     * @param {Object} context
     * @return {String / DOM object} rendered template
     **/
    template: function(){
      throw new Error("Template function not defined!");
    },

    /**
     * Returns the context object for the `template` method.
     *
     * Default: `this.model.toJSON()` or an empty object if view has no model.
     *
     * @method context
     * @return {Object} context
     **/
    context: function() {
      if (this.model) return this.model.toJSON();
      return {};
    },

    /**
     * Render this view with `this.template(this.context())` and refresh child
     * views with `this.refreshViews(options)`.
     *
     * @method render
     * @param {Object} options
     *   @param {Boolean} [options.force]
     *   force rerendering of child views
     * @return {Object} this
     **/
    render: function(opts) {
      opts = _.extend({}, opts);

      // Remove subviews with detach. This way they don't lose event handlers
      // and parent view can rerender itself
      this.eachView(function(sel, view) {
        view.$el.detach();
      });

      opts.detached = true;

      this.$el.html(this.template(this.context()));

      this.afterTemplate();

      // Mark this view as rendered. Parent view wont try to render this
      // anymore unless force:true is passed
      this.rendered = true;

      this.refreshViews(opts);

      return this;
    },

    /**
     * Overrideable by the view implementor.
     *
     * Called after the template has been rendered but before adding child
     * views.  This is perfect place to hook up any jQuery plugings using
     * `this.$` or `this.$el` or set access points to view DOM elements
     *
     *     this.$el("button").tooltipPlugin();
     *     this.$saveButton = this.$("button.save");
     *
     * because they cannot have any side affects to child views.
     *
     * @method aftertemplate
     **/
    afterTemplate: function() {},

    /**
     * Refresh any child view changes made with `setView`, `appendView`,
     * `prependView` or `insertView`. Tries to avoid doing any work unless it
     * is absolutely necessary or `{ force: true }` is passed.
     *
     * It calls `render` on child views only when a child has never been
     * rendered before.
     *
     * @method refreshViews
     * @param {Object} options
     *   @param {Boolean} [options.force]
     *   force rerendering of child views
     * @return {Object} this
     **/
    refreshViews: function(opts) {
      opts = _.extend({}, opts);
      var self = this;
      var dirty = this._dirty;

      var oldView;
      while (oldView = this._remove.shift()) oldView.remove();

      this.eachView(function(sel, view) {
        // Detach view from container if it is dirty to update possible view
        // order changes.
        var refresh = dirty[sel] || opts.force;

        if (refresh && !opts.detached) view.$el.detach();
        if (!view.rendered || opts.force) view.render(opts);
        if (refresh || opts.detached) self.$(sel).append(view.el);
      });

      this._dirty = {};
      return this;
    },

    /**
     * Detach view from its parent but keep it in DOM.
     *
     * @private
     * @method _removeParent
     * @return {Object} this
     **/
    _removeParent: function() {

      if (!this.parent) return;

      var key;
      for (key in this.parent._views) {
        this.parent._views[key] = _.without(
          this.parent._views[key], this
        );
      }

      this.parent = null;

      return this;
    },

    /**
     * Prepare views to be nested in this view. Set up event bubbling and
     * remove possible previous parent.
     *
     * @private
     * @method _prepareViews
     * @param {Object/Constructor/Array} view(s) One or array of view objects
     * or Constructors
     **/
    _prepareViews: function(views) {
      var self = this;
      views = _.map(ensureArray(views), _.bind(this._createInstance, this));

      _.each(views, function(view) {
        // when parent is changing remove it from previous
        if (view.parent && view.parent !== self) view._removeParent();
        view.parent = self;
      });

      return views;
    },

    _createInstance: function(view) {
      return isConstructor(view) ? new view({
        model: this.model,
        collection: this.collection
      }) : view;
    },

    /**
    * Like `trigger` but the events are bubbled all the way up to all
    * parent views too.
    *
    * @method bubble
    * @param {String} event
    * @param {any} [args*] Zero or more objects to be passed along with the
    * event
    * @return {Object} this
    **/
    bubble: function() {
      var args = _.toArray(arguments);
      this.trigger.apply(this, args);
      if (this.parent) this.parent.bubble.apply(this.parent, args);
      return this;
    },

    /**
    * Opposite of `bubble`. The events are broadcasted all the way down to all
    * child views too.
    *
    * @method broadcast
    * @param {String} event
    * @param {any} [args*] Zero or more objects to be passed along with the
    * event
    * @return {Object} this
    **/
    broadcast: function() {
      var args = _.toArray(arguments);
      this.trigger.apply(this, args);
      this.eachView(function(sel, view) {
        view.broadcast.apply(view, args);
      });
      return this;
    },

    /**
     * Iterate each child view.
     *
     * @method eachView
     * @param {Function} iterator
     *   @param {String} iterator.selector CSS selector for the view container
     *   @param {Object} iterator.view The view object
     * @return {Object} this
     **/
    eachView: function(fn) {
      _.each(this._views, function(views, sel) {
        _.each(views, function(view) {
          fn(sel, view);
        });
      });
    },

    /**
     * Set a view or an array of views to a given view container.
     *
     * If previous set of views is replaced by a new set — the views not
     * present in the new set will be discarded with `remove` on the next
     * `refreshViews` call unless detached with `detach` method.
     *
     * If `views` parameter is a constructor function it will be instantiated
     * with `model` and `collection` of the parent. This applies to
     * `appendView`, `prependview` and `insertView` too.
     *
     * @protected
     * @method setView
     * @param {String} selector CSS selector for the view container
     * @param {Object/Constructor/Array} view(s) One or array of view objects
     * or Constructors
     **/
    setView: function(sel, currentViews) {
      var previousViews;
      currentViews = this._prepareViews(currentViews);

      if (previousViews = this._views[sel]) {
        this._remove = this._remove.concat(
          _.difference(previousViews, currentViews)
        );
      }

      this._views[sel] = currentViews;
      this._dirty[sel] = true;
      return this;
    },

    /**
     * Insert views to specific index in the view array.
     *
     * @protected
     * @method insertView
     * @param {String} selector CSS selector for the view container
     * @param {Number} index Index to insert view(s)
     * @param {Object/Constructor/Array} view(s) Views to insert
     **/
    insertView: function(sel, index, views) {
      var current;
      views = this._prepareViews(views);
      if (current = this._views[sel]) {
        current.slice().splice.apply(current, [index, 0].concat(views));
      }
      else {
        this._views[sel] = views;
      }

      this._dirty[sel] = true;
      return this;
    },

    /**
     * Append a views to a given view container.
     *
     * @protected
     * @method appendView
     * @param {String} selector CSS selector for the view container
     * @param {Object/Constructor/Array} view(s) Views to append
     **/
    appendView: function(sel, views) {
      this._views[sel] = (this._views[sel] || []).concat(
        this._prepareViews(views)
      );

      this._dirty[sel] = true;
      return this;
    },

    /**
     * Prepend a views to a given view container.
     *
     * @protected
     * @method prependView
     * @param {String} selector CSS selector for the view container
     * @param {Object/Constructor/Array} view(s) Views to prepend
     **/
    prependView: function(sel, views) {
      this._views[sel] = this._prepareViews(views).concat(
        this._views[sel] || []
      );

      this._dirty[sel] = true;
      return this;
    },

    /**
     * Return array of views for given container or null if the container have
     * not been used.
     *
     * @protected
     * @method getViews
     * @param {String} selector container CSS selector
     * @return {Array/null} views
     **/
    getViews: function(sel) {
      var views;
      if (views = this._views[sel]) return views.slice();
      return null;
    },

    /**
     * Detaches view from its parent and DOM.
     *
     * @method detach
     * @return {Object} this
     **/
    detach: function() {
      this._removeParent();
      this.$el.detach();
      return this;
    },

    /**
     * Discard this view and all its children for good. Clears all events bound
     * for the view. Use `detach` instead if you need to use the view again
     * some time later.
     *
     * @method remove
     * @return {Object} this
     **/
    remove: function() {
      Backbone.View.prototype.remove.apply(this, arguments);
      this._removeParent();
      this.eachView(function(sel, view) {
        view.remove();
      });
      return this;
    }

  });

  Backbone.ViewMaster.VERSION = VERSION;
  if (typeof module !== 'undefined') module.exports = Backbone.ViewMaster;

})();
<|endoftext|>


<commit_before>import ResponseValidator from '../src/ResponseValidator';
import Log from '../src/Log';

import StubMetadataService from './StubMetadataService';

import chai from 'chai';
chai.should();
let assert = chai.assert;
let expect = chai.expect;

class StubUserInfoService {
    getClaims() {
        this.getClaimsWasCalled = true;
        return this.getClaimsResult;
    }
}
class MockResponseValidator extends ResponseValidator {
    constructor(settings, MetadataServiceCtor, UserInfoServiceCtor) {
        super(settings, MetadataServiceCtor, UserInfoServiceCtor);
    }

    _mock(name, ...args) {
        Log.info("mock called", name);
        this[name + "WasCalled"] = true;

        if (this[name + "Result"]) {
            Log.info("mock returning result", this[name + "Result"]);
            return this[name + "Result"];
        }

        Log.info("mock calling super");
        return super[name](...args);
    }

    processSigninParams(...args) {
        return this._mock("processSigninParams", ...args);
    }
    validateTokens(...args) {
        return this._mock("validateTokens", ...args);
    }
    processClaims(...args) {
        return this._mock("processClaims", ...args);
    }
    mergeClaims(...args){
        return this._mock("mergeClaims", ...args);
    }

    validateIdTokenAndAccessToken(...args) {
        return this._mock("validateIdTokenAndAccessToken", ...args);
    }
    validateIdToken(...args) {
        return this._mock("validateIdToken", ...args);
    }
    validateAccessToken(...args) {
        return this._mock("validateAccessToken", ...args);
    }

    filterProtocolClaims(...args) {
        return this._mock("filterProtocolClaims", ...args);
    }
}

describe("ResponseValidator", function() {
    let settings;
    let subject;
    let stubMetadataService;
    let stubUserInfoService;

    let stubState;
    let stubResponse;

    beforeEach(function() {
        Log.setLogger(console);
        Log.level = Log.NONE;

        stubState = {
            id: "the_id",
            data: { some: 'data' }
        };
        stubResponse = {
            state: 'the_id'
        };

        settings = {};
        stubMetadataService = new StubMetadataService();
        stubUserInfoService = new StubUserInfoService();
        subject = new MockResponseValidator(settings, () => stubMetadataService, () => stubUserInfoService);
    });

    describe("constructor", function() {

        it("should require a settings param", function() {
            try {
                new ResponseValidator(undefined, () => stubMetadataService, () => stubUserInfoService);
            }
            catch (e) {
                e.message.should.contain('settings');
                return;
            }
            assert.fail();
        });

    });

    describe("validateSignoutResponse", function(done) {

        it("should validate that the client state matches response state", function() {

            stubResponse.state = "not_the_id";
            subject.validateSignoutResponse(stubState, stubResponse).then(null, err => {
                err.message.should.contain('match');
                done();
            });

        });

        it("should fail on error response", function(done) {

            stubResponse.error = "some_error";
            subject.validateSignoutResponse(stubState, stubResponse).then(null, err => {
                err.error.should.equal("some_error");
                done();
            });

        });

        it("should return data for error responses", function(done) {

            stubResponse.error = "some_error";
            subject.validateSignoutResponse(stubState, stubResponse).then(null, err => {
                err.state.should.deep.equal({ some: 'data' });
                done();
            });

        });

        it("should return data for successful responses", function(done) {

            subject.validateSignoutResponse(stubState, stubResponse).then(response => {
                response.state.should.deep.equal({ some: 'data' });
                done();
            });

        });

    });

    describe("validateSigninResponse", function(done) {

        it("should process signin params", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(response => {
                subject.processSigninParamsWasCalled.should.be.true;
                done();
            });

        });

        it("should validate tokens", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(response => {
                subject.validateTokensWasCalled.should.be.true;
                done();
            });

        });

        it("should not validate tokens if state fails", function(done) {

            subject.processSigninParamsResult = Promise.reject("error");
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(null, err => {
                expect(subject.validateTokensWasCalled).to.be.undefined;
                done();
            });

        });

        it("should process claims", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(response => {
                subject.processClaimsWasCalled.should.be.true;
                done();
            });

        });

        it("should not process claims if state fails", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.reject("error");

            subject.validateSigninResponse(stubState, stubResponse).then(null, err => {
                expect(subject.processClaimsWasCalled).to.be.undefined;
                done();
            });

        });

    });

    describe("processSigninParams", function() {

        it("should validate that the client state matches response state", function() {

            stubResponse.state = "not_the_id";
            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.message.should.contain('match');
                done();
            });

        });

        it("should fail on error response", function(done) {

            stubResponse.error = "some_error";
            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.error.should.equal("some_error");
                done();
            });

        });

        it("should return data for error responses", function(done) {

            stubResponse.error = "some_error";
            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.state.should.deep.equal({ some: 'data' });
                done();
            });

        });

        it("should fail if request was OIDC but no id_token in response", function(done) {

            stubState.nonce = "some_nonce";
            delete stubResponse.id_token;

            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.message.should.contain("id_token");
                done();
            });

        });

        it("should fail if request was not OIDC but id_token in response", function(done) {

            stubResponse.id_token = "id_token";

            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.message.should.contain("id_token");
                done();
            });

        });

        it("should return data for successful responses", function(done) {

            subject.processSigninParams(stubState, stubResponse).then(response => {
                response.state.should.deep.equal({ some: 'data' });
                done();
            });

        });
    });

    describe("processClaims", function() {

        it("should filter protocol claims", function(done) {

            subject.processClaims(stubState, stubResponse).then(response => {
                subject.filterProtocolClaimsWasCalled.should.be.true;
                done();
            });

        });

        it("should load and merge user info claims when loadUserInfo configured", function(done) {

            settings.loadUserInfo = true;
            stubResponse.profile = {a:'apple', b:'banana'};
            stubUserInfoService.getClaimsResult = Promise.resolve({c:'carrot'});

            subject.processClaims(stubResponse).then(response => {
                stubUserInfoService.getClaimsWasCalled.should.be.true;
                subject.mergeClaimsWasCalled.should.be.true;
                done();
            });

        });

        it("should not load and merge user info claims when loadUserInfo not configured", function(done) {

            settings.loadUserInfo = false;
            stubResponse.profile = {a:'apple', b:'banana'};
            stubUserInfoService.getClaimsResult = Promise.resolve({c:'carrot'});

            subject.processClaims(stubResponse).then(response => {
                expect(stubUserInfoService.getClaimsWasCalled).to.be.undefined;
                expect(subject.mergeClaimsWasCalled).to.be.undefined;
                done();
            });

        });

    });

    describe("mergeClaims", function(){

         it("should merge claims", function() {

            var c1 = {a:'apple', b:'banana'};
            var c2 = {c:'carrot'};

            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({a:'apple', c:'carrot', b:'banana'});

        });

        it("should merge same claim types into array", function() {

            var c1 = {a:'apple', b:'banana'};
            var c2 = {a:'carrot'};

            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({a:['apple', 'carrot'], b:'banana'});

        });

        it("should merge arrays of same claim types into array", function() {

            var c1 = {a:'apple', b:'banana'};
            var c2 = {a:['carrot', 'durian']};
            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({a:['apple', 'carrot', 'durian'], b:'banana'});

            var c1 = {a:['apple', 'carrot'], b:'banana'};
            var c2 = {a:['durian']};
            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({a:['apple', 'carrot', 'durian'], b:'banana'});

            var c1 = {a:['apple', 'carrot'], b:'banana'};
            var c2 = {a:'durian'};
            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({a:['apple', 'carrot', 'durian'], b:'banana'});
        });
    });

    describe("filterProtocolClaims", function() {

        it("should filter protocol claims if enabled on settings", function() {

            settings.filterProtocolClaims = true;
            let claims = {
                foo: 1, bar: 'test',
                aud: 'some_aud', iss: 'issuer',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev'],
                nonce: 'nonce', at_hash: "athash",
                iat: 5, nbf: 10, exp: 20
            };

            var result = subject.filterProtocolClaims(claims);
            result.should.deep.equal({
                foo: 1, bar: 'test',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev']
            });

        });

        it("should not filter protocol claims if not enabled on settings", function() {

            settings.filterProtocolClaims = false;
            let claims = {
                foo: 1, bar: 'test',
                aud: 'some_aud', iss: 'issuer',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev'],
                nonce: 'nonce', at_hash: "athash",
                iat: 5, nbf: 10, exp: 20
            };

            var result = subject.filterProtocolClaims(claims);
            result.should.deep.equal({
                foo: 1, bar: 'test',
                aud: 'some_aud', iss: 'issuer',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev'],
                nonce: 'nonce', at_hash: "athash",
                iat: 5, nbf: 10, exp: 20
            });

        });
    });

    describe("validateTokens", function() {

        it("should validate id_token and access_token", function(done) {

            stubResponse.id_token = "id_token";
            stubResponse.access_token = "access_token";
            subject.validateIdTokenAndAccessTokenResult = Promise.resolve(stubResponse);

            subject.validateTokens(stubState, stubResponse).then(response => {
                subject.validateIdTokenAndAccessTokenWasCalled.should.be.true;
                expect(subject.validateIdTokenWasCalled).to.be.undefined;
                done();
            });

        });

        it("should validate just id_token", function(done) {

            stubResponse.id_token = "id_token";
            subject.validateIdTokenResult = Promise.resolve(stubResponse);

            subject.validateTokens(stubState, stubResponse).then(response => {
                subject.validateIdTokenWasCalled.should.be.true;
                expect(subject.validateIdTokenAndAccessTokenWasCalled).to.be.undefined;
                done();
            });

        });

        it("should not validate if only access_token", function(done) {

            stubResponse.access_token = "access_token";

            subject.validateTokens(stubState, stubResponse).then(response => {
                expect(subject.validateIdTokenWasCalled).to.be.undefined;
                expect(subject.validateIdTokenAndAccessTokenWasCalled).to.be.undefined;
                done();
            });

        });
    });

});
<commit_msg>stub out token validation tests<commit_after>import ResponseValidator from '../src/ResponseValidator';
import Log from '../src/Log';

import StubMetadataService from './StubMetadataService';

import chai from 'chai';
chai.should();
let assert = chai.assert;
let expect = chai.expect;

class StubUserInfoService {
    getClaims() {
        this.getClaimsWasCalled = true;
        return this.getClaimsResult;
    }
}
class MockResponseValidator extends ResponseValidator {
    constructor(settings, MetadataServiceCtor, UserInfoServiceCtor) {
        super(settings, MetadataServiceCtor, UserInfoServiceCtor);
    }

    _mock(name, ...args) {
        Log.info("mock called", name);
        this[name + "WasCalled"] = true;

        if (this[name + "Result"]) {
            Log.info("mock returning result", this[name + "Result"]);
            return this[name + "Result"];
        }

        Log.info("mock calling super");
        return super[name](...args);
    }

    processSigninParams(...args) {
        return this._mock("processSigninParams", ...args);
    }
    validateTokens(...args) {
        return this._mock("validateTokens", ...args);
    }
    processClaims(...args) {
        return this._mock("processClaims", ...args);
    }
    mergeClaims(...args) {
        return this._mock("mergeClaims", ...args);
    }

    validateIdTokenAndAccessToken(...args) {
        return this._mock("validateIdTokenAndAccessToken", ...args);
    }
    validateIdToken(...args) {
        return this._mock("validateIdToken", ...args);
    }
    validateAccessToken(...args) {
        return this._mock("validateAccessToken", ...args);
    }

    filterProtocolClaims(...args) {
        return this._mock("filterProtocolClaims", ...args);
    }
}

describe("ResponseValidator", function() {
    let settings;
    let subject;
    let stubMetadataService;
    let stubUserInfoService;

    let stubState;
    let stubResponse;

    beforeEach(function() {
        Log.setLogger(console);
        Log.level = Log.NONE;

        stubState = {
            id: "the_id",
            data: { some: 'data' }
        };
        stubResponse = {
            state: 'the_id'
        };

        settings = {};
        stubMetadataService = new StubMetadataService();
        stubUserInfoService = new StubUserInfoService();
        subject = new MockResponseValidator(settings, () => stubMetadataService, () => stubUserInfoService);
    });

    describe("constructor", function() {

        it("should require a settings param", function() {
            try {
                new ResponseValidator(undefined, () => stubMetadataService, () => stubUserInfoService);
            }
            catch (e) {
                e.message.should.contain('settings');
                return;
            }
            assert.fail();
        });

    });

    describe("validateSignoutResponse", function(done) {

        it("should validate that the client state matches response state", function() {

            stubResponse.state = "not_the_id";
            subject.validateSignoutResponse(stubState, stubResponse).then(null, err => {
                err.message.should.contain('match');
                done();
            });

        });

        it("should fail on error response", function(done) {

            stubResponse.error = "some_error";
            subject.validateSignoutResponse(stubState, stubResponse).then(null, err => {
                err.error.should.equal("some_error");
                done();
            });

        });

        it("should return data for error responses", function(done) {

            stubResponse.error = "some_error";
            subject.validateSignoutResponse(stubState, stubResponse).then(null, err => {
                err.state.should.deep.equal({ some: 'data' });
                done();
            });

        });

        it("should return data for successful responses", function(done) {

            subject.validateSignoutResponse(stubState, stubResponse).then(response => {
                response.state.should.deep.equal({ some: 'data' });
                done();
            });

        });

    });

    describe("validateSigninResponse", function(done) {

        it("should process signin params", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(response => {
                subject.processSigninParamsWasCalled.should.be.true;
                done();
            });

        });

        it("should validate tokens", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(response => {
                subject.validateTokensWasCalled.should.be.true;
                done();
            });

        });

        it("should not validate tokens if state fails", function(done) {

            subject.processSigninParamsResult = Promise.reject("error");
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(null, err => {
                expect(subject.validateTokensWasCalled).to.be.undefined;
                done();
            });

        });

        it("should process claims", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.resolve(stubResponse);

            subject.validateSigninResponse(stubState, stubResponse).then(response => {
                subject.processClaimsWasCalled.should.be.true;
                done();
            });

        });

        it("should not process claims if state fails", function(done) {

            subject.processSigninParamsResult = Promise.resolve(stubResponse);
            subject.validateTokensResult = Promise.reject("error");

            subject.validateSigninResponse(stubState, stubResponse).then(null, err => {
                expect(subject.processClaimsWasCalled).to.be.undefined;
                done();
            });

        });

    });

    describe("processSigninParams", function() {

        it("should validate that the client state matches response state", function() {

            stubResponse.state = "not_the_id";
            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.message.should.contain('match');
                done();
            });

        });

        it("should fail on error response", function(done) {

            stubResponse.error = "some_error";
            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.error.should.equal("some_error");
                done();
            });

        });

        it("should return data for error responses", function(done) {

            stubResponse.error = "some_error";
            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.state.should.deep.equal({ some: 'data' });
                done();
            });

        });

        it("should fail if request was OIDC but no id_token in response", function(done) {

            stubState.nonce = "some_nonce";
            delete stubResponse.id_token;

            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.message.should.contain("id_token");
                done();
            });

        });

        it("should fail if request was not OIDC but id_token in response", function(done) {

            stubResponse.id_token = "id_token";

            subject.processSigninParams(stubState, stubResponse).then(null, err => {
                err.message.should.contain("id_token");
                done();
            });

        });

        it("should return data for successful responses", function(done) {

            subject.processSigninParams(stubState, stubResponse).then(response => {
                response.state.should.deep.equal({ some: 'data' });
                done();
            });

        });
    });

    describe("processClaims", function() {

        it("should filter protocol claims", function(done) {

            subject.processClaims(stubState, stubResponse).then(response => {
                subject.filterProtocolClaimsWasCalled.should.be.true;
                done();
            });

        });

        it("should load and merge user info claims when loadUserInfo configured", function(done) {

            settings.loadUserInfo = true;
            stubResponse.profile = { a: 'apple', b: 'banana' };
            stubUserInfoService.getClaimsResult = Promise.resolve({ c: 'carrot' });

            subject.processClaims(stubResponse).then(response => {
                stubUserInfoService.getClaimsWasCalled.should.be.true;
                subject.mergeClaimsWasCalled.should.be.true;
                done();
            });

        });

        it("should not load and merge user info claims when loadUserInfo not configured", function(done) {

            settings.loadUserInfo = false;
            stubResponse.profile = { a: 'apple', b: 'banana' };
            stubUserInfoService.getClaimsResult = Promise.resolve({ c: 'carrot' });

            subject.processClaims(stubResponse).then(response => {
                expect(stubUserInfoService.getClaimsWasCalled).to.be.undefined;
                expect(subject.mergeClaimsWasCalled).to.be.undefined;
                done();
            });

        });

    });

    describe("mergeClaims", function() {

        it("should merge claims", function() {

            var c1 = { a: 'apple', b: 'banana' };
            var c2 = { c: 'carrot' };

            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({ a: 'apple', c: 'carrot', b: 'banana' });

        });

        it("should merge same claim types into array", function() {

            var c1 = { a: 'apple', b: 'banana' };
            var c2 = { a: 'carrot' };

            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({ a: ['apple', 'carrot'], b: 'banana' });

        });

        it("should merge arrays of same claim types into array", function() {

            var c1 = { a: 'apple', b: 'banana' };
            var c2 = { a: ['carrot', 'durian'] };
            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({ a: ['apple', 'carrot', 'durian'], b: 'banana' });

            var c1 = { a: ['apple', 'carrot'], b: 'banana' };
            var c2 = { a: ['durian'] };
            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({ a: ['apple', 'carrot', 'durian'], b: 'banana' });

            var c1 = { a: ['apple', 'carrot'], b: 'banana' };
            var c2 = { a: 'durian' };
            var result = subject.mergeClaims(c1, c2);
            result.should.deep.equal({ a: ['apple', 'carrot', 'durian'], b: 'banana' });
        });
    });

    describe("filterProtocolClaims", function() {

        it("should filter protocol claims if enabled on settings", function() {

            settings.filterProtocolClaims = true;
            let claims = {
                foo: 1, bar: 'test',
                aud: 'some_aud', iss: 'issuer',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev'],
                nonce: 'nonce', at_hash: "athash",
                iat: 5, nbf: 10, exp: 20
            };

            var result = subject.filterProtocolClaims(claims);
            result.should.deep.equal({
                foo: 1, bar: 'test',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev']
            });

        });

        it("should not filter protocol claims if not enabled on settings", function() {

            settings.filterProtocolClaims = false;
            let claims = {
                foo: 1, bar: 'test',
                aud: 'some_aud', iss: 'issuer',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev'],
                nonce: 'nonce', at_hash: "athash",
                iat: 5, nbf: 10, exp: 20
            };

            var result = subject.filterProtocolClaims(claims);
            result.should.deep.equal({
                foo: 1, bar: 'test',
                aud: 'some_aud', iss: 'issuer',
                sub: '123', email: 'foo@gmail.com',
                role: ['admin', 'dev'],
                nonce: 'nonce', at_hash: "athash",
                iat: 5, nbf: 10, exp: 20
            });

        });
    });

    describe("validateTokens", function() {

        it("should validate id_token and access_token", function(done) {

            stubResponse.id_token = "id_token";
            stubResponse.access_token = "access_token";
            subject.validateIdTokenAndAccessTokenResult = Promise.resolve(stubResponse);

            subject.validateTokens(stubState, stubResponse).then(response => {
                subject.validateIdTokenAndAccessTokenWasCalled.should.be.true;
                expect(subject.validateIdTokenWasCalled).to.be.undefined;
                done();
            });

        });

        it("should validate just id_token", function(done) {

            stubResponse.id_token = "id_token";
            subject.validateIdTokenResult = Promise.resolve(stubResponse);

            subject.validateTokens(stubState, stubResponse).then(response => {
                subject.validateIdTokenWasCalled.should.be.true;
                expect(subject.validateIdTokenAndAccessTokenWasCalled).to.be.undefined;
                done();
            });

        });

        it("should not validate if only access_token", function(done) {

            stubResponse.access_token = "access_token";

            subject.validateTokens(stubState, stubResponse).then(response => {
                expect(subject.validateIdTokenWasCalled).to.be.undefined;
                expect(subject.validateIdTokenAndAccessTokenWasCalled).to.be.undefined;
                done();
            });

        });
    });

    describe("validateIdTokenAndAccessToken", function() {

        it("should validate id_token and access_token", function(done) {

            stubResponse.id_token = "id_token";
            stubResponse.access_token = "access_token";
            subject.validateIdTokenResult = Promise.resolve(stubResponse);

            subject.validateIdTokenAndAccessToken(stubState, stubResponse).then(response => {
                subject.validateIdTokenWasCalled.should.be.true;
                subject.validateAccessTokenWasCalled.should.be.true;
                done();
            });

        });

        it("should not access_token if id_token validation fails", function(done) {

            stubResponse.id_token = "id_token";
            stubResponse.access_token = "access_token";
            subject.validateIdTokenResult = Promise.reject(new Error("error"));

            subject.validateIdTokenAndAccessToken(stubState, stubResponse).then(null, err => {
                subject.validateIdTokenWasCalled.should.be.true;
                expect(subject.validateAccessTokenWasCalled).to.be.undefined;
                done();
            });

        });

    });

    describe("validateIdToken", function() {

    });

    describe("validateAccessToken", function() {

    });
});
<|endoftext|>


<commit_before>/*
    Copyright 2010 Emilis Dambauskas

    This file is part of PolicyFeed module.

    PolicyFeed is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    PolicyFeed is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with PolicyFeed.  If not, see <http://www.gnu.org/licenses/>.
*/

var update_url = "http://localhost:8081/solr/update";
var search_url = 'http://localhost:8081/solr/select/?start=0&rows=100&sort=published+desc&fl=id,published,type,org,organization,title&wt=json&q=';

var httpclient = require("ringo/httpclient");
var jstorage = require("ctl/JsonStorage");

/**
 *
 */
var prepareDate = function(str) {
    if (!str.match(/^\d\d\d\d-\d\d-\d\d.\d\d:\d\d:\d\d/))
        throw Error("Unsupported date format: " + str);

    var d = new Date();
    d.setFullYear(str.substr(0,4));
    d.setMonth(parseInt(str.substr(5,2), 10) - 1);
    d.setDate(str.substr(8,2));
    d.setHours(str.substr(11,2));
    d.setMinutes(str.substr(14,2));
    d.setSeconds(str.substr(17,2));

    return d.toISOString();
}

/**
 *
 */
var stripTags = function(str) {
    return str.replace(/<[^>]*>/g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&\w+;/g, " ").trim();
}

/**
 *
 */
exports.itemToXml = function(item) {
    return '<doc>'
        + '<field name="id">' + item._id + '</field>'
        + '<field name="published">' + prepareDate(item.published) + '</field>'
        + '<field name="type">' + item.type + '</field>'
        + '<field name="org">' + item.org + '</field>'
        + '<field name="organization">' + item.organization + '</field>'
        + '<field name="title">' + stripTags(item.title) + '</field>'
        + '<field name="html">' + stripTags(item.html) + '</field>'
        + '</doc>';
}

/**
 *
 */
exports.indexItem = function(item) {
    print("SolrClient.indexItem", item._id);
    try {
        var req = {
            method: "POST",
            url: update_url,
            contentType: "text/xml; charset=utf-8",
            data: '<add>' + this.itemToXml(item) + "</add>",
            error: function(e) { print(e, e.stack); throw e; }
            };
    } catch (e) {
        throw Error("Failed to create request for item " + item._id + ". Error: " + e.message);
    }

    return httpclient.request(req);
}

/**
 *
 */
exports.removeItem = function(id) {
    var req = {
        method: "POST",
        url: update_url,
        contentType: "text/xml; charset=utf-8",
        data: '<delete><id>' + id + '</id></delete>'
    };

    return httpclient.request(req);
}

/**
 *
 */
exports.reindex = function(path) {
    if (path === undefined)
        path = "/docs";

    var gen = jstorage.iterate(path);

    for each (var doc in gen) {
        try {
            var e = this.indexItem(doc);
            if (e.content != '<result status="0"></result>')
                print(doc._id, e.content);
        } catch (e) {
            print(e.message);
        }
    }
}

/**
 *
 */
exports.onItemChange = function(action, _id, item) {
    print("SolrClient.onItemChange", action, _id);
    if (_id.indexOf("/docs/") > -1) {
        try {
            switch (action) {
                case "after-write":
                    return exports.indexItem(item);
                break;
                case "after-remove":
                    return exports.removeItem(_id);
                break;
            }
        } catch(e) {
            print(e, e.stack);
        }
    }
}

/**
 *
 */
exports.search = function(query) {
    var res = httpclient.get(search_url + query);
    if (res.status === 200)
        return JSON.parse(res.content);
}

/**
 *
 */
exports.searchByDay = function(day) {
    var query = "published:[" + day + "T00:00:00Z+TO+" + day + "T23:59:59.999Z]";
    return this.search(query);
}

/**
 *
 */
exports.searchByMonth = function(month) {
    var query = "published:[" + month + "-01T00:00:00Z+TO+" + month + "-01T00:00:00Z%2B1MONTH]";
    return this.search(query);
}

/**
 *
 */
exports.searchByYear = function(year) {
    var query = "published:[" + year + "-01-01T00:00:00Z+TO+" + year + "-12-31T23:59:59.999Z]";
    return this.search(query);
}

/**
 *
 */
exports.getLatestDocs = function () {
    return this.search("*:*").response.docs;
}
<commit_msg>Added options parameter to PolicyFeed/SolrClient.search().<commit_after>/*
    Copyright 2010 Emilis Dambauskas

    This file is part of PolicyFeed module.

    PolicyFeed is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    PolicyFeed is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with PolicyFeed.  If not, see <http://www.gnu.org/licenses/>.
*/

var update_url = "http://localhost:8081/solr/update";
var search_url = 'http://localhost:8081/solr/select/?start=0&rows=100&sort=published+desc&fl=id,published,type,org,organization,title&wt=json&q=';

var httpclient = require("ringo/httpclient");
var jstorage = require("ctl/JsonStorage");

/**
 *
 */
var prepareDate = function(str) {
    if (!str.match(/^\d\d\d\d-\d\d-\d\d.\d\d:\d\d:\d\d/))
        throw Error("Unsupported date format: " + str);

    var d = new Date();
    d.setFullYear(str.substr(0,4));
    d.setMonth(parseInt(str.substr(5,2), 10) - 1);
    d.setDate(str.substr(8,2));
    d.setHours(str.substr(11,2));
    d.setMinutes(str.substr(14,2));
    d.setSeconds(str.substr(17,2));

    return d.toISOString();
}

/**
 *
 */
var stripTags = function(str) {
    return str.replace(/<[^>]*>/g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&\w+;/g, " ").trim();
}

/**
 *
 */
exports.itemToXml = function(item) {
    return '<doc>'
        + '<field name="id">' + item._id + '</field>'
        + '<field name="published">' + prepareDate(item.published) + '</field>'
        + '<field name="type">' + item.type + '</field>'
        + '<field name="org">' + item.org + '</field>'
        + '<field name="organization">' + item.organization + '</field>'
        + '<field name="title">' + stripTags(item.title) + '</field>'
        + '<field name="html">' + stripTags(item.html) + '</field>'
        + '</doc>';
}

/**
 *
 */
exports.indexItem = function(item) {
    print("SolrClient.indexItem", item._id);
    try {
        var req = {
            method: "POST",
            url: update_url,
            contentType: "text/xml; charset=utf-8",
            data: '<add>' + this.itemToXml(item) + "</add>",
            error: function(e) { print(e, e.stack); throw e; }
            };
    } catch (e) {
        throw Error("Failed to create request for item " + item._id + ". Error: " + e.message);
    }

    return httpclient.request(req);
}

/**
 *
 */
exports.removeItem = function(id) {
    var req = {
        method: "POST",
        url: update_url,
        contentType: "text/xml; charset=utf-8",
        data: '<delete><id>' + id + '</id></delete>'
    };

    return httpclient.request(req);
}

/**
 *
 */
exports.reindex = function(path) {
    if (path === undefined)
        path = "/docs";

    var gen = jstorage.iterate(path);

    for each (var doc in gen) {
        try {
            var e = this.indexItem(doc);
            if (e.content != '<result status="0"></result>')
                print(doc._id, e.content);
        } catch (e) {
            print(e.message);
        }
    }
}

/**
 *
 */
exports.onItemChange = function(action, _id, item) {
    print("SolrClient.onItemChange", action, _id);
    if (_id.indexOf("/docs/") > -1) {
        try {
            switch (action) {
                case "after-write":
                    return exports.indexItem(item);
                break;
                case "after-remove":
                    return exports.removeItem(_id);
                break;
            }
        } catch(e) {
            print(e, e.stack);
        }
    }
}

/**
 *
 */
exports.search = function(query, options) {
    print(query);
    query = query.replace(/\s+/g, "+");

    options = options || {};
    var {highlight, fields, limit} = options;

    var url = search_url;
    if (fields && fields.length)
        var url = url.replace(/fl=[^&]+/, "fl=" + fields.join(","));
    if (limit)
        var url = url.replace(/rows=[^&]+/, "rows=" + limit);

    var res = httpclient.get(url + query);
    if (res.status === 200)
        return JSON.parse(res.content);
}

/**
 *
 */
exports.searchByDay = function(day) {
    var query = "published:[" + day + "T00:00:00Z+TO+" + day + "T23:59:59.999Z]";
    return this.search(query);
}

/**
 *
 */
exports.searchByMonth = function(month) {
    var query = "published:[" + month + "-01T00:00:00Z+TO+" + month + "-01T00:00:00Z%2B1MONTH]";
    return this.search(query);
}

/**
 *
 */
exports.searchByYear = function(year) {
    var query = "published:[" + year + "-01-01T00:00:00Z+TO+" + year + "-12-31T23:59:59.999Z]";
    return this.search(query);
}

/**
 *
 */
exports.getLatestDocs = function (options) {
    return this.search("*:*", options).response;
}
<|endoftext|>


<commit_before>import style from './style.css'

export default {
  render(h) {
return (
<md-layout md-gutter>

<md-card  md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33">
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>

  <md-card-content>
  Actually austin tbh 3 wolf moon single-origin coffee farm-to-table bespoke. Everyday carry occupy try-hard pitchfork meggings. Tilde poke hashtag, raclette pour-over semiotics gochujang 8-bit echo park neutra roof party stumptown kitsch microdosing etsy. Roof party next level tofu meh humblebrag banh mi marfa master cleanse four loko small batch normcore gentrify truffaut fanny pack VHS. Tumeric photo booth microdosing, kickstarter copper mug succulents brooklyn godard air plant authentic kale chips single-origin coffee. VHS yuccie master cleanse unicorn
  </md-card-content>
</md-card>

<md-card  md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33">
  <md-card-header>
    <md-card-header-text>

Title goes here

Subtitle here

    </md-card-header-text>

    <md-menu md-size="4" md-direction="bottom left">
      <md-button class="md-icon-button" md-menu-trigger>
        <md-icon>more_vert</md-icon>
      </md-button>

      <md-menu-content>
        <md-menu-item>
          Call
          <md-icon>phone</md-icon>
        </md-menu-item>

        <md-menu-item>
          Send a message
          <md-icon>message</md-icon>
        </md-menu-item>
      </md-menu-content>
    </md-menu>
  </md-card-header>

  <md-card-media>

  </md-card-media>

  <md-card-content>
  quinoa swag af ramps snackwave irony poke. Aesthetic actually XOXO lomo, DIY beard poke vinyl godard trust fund hoodie skateboard seitan hot chicken mixtape. Drinking vinegar organic occupy, truffaut vinyl palo santo yuccie tbh. Seitan small batch unicorn pinterest 90's hoodie hashtag master cleanse dreamcatcher kitsch. Pinterest flexitarian craft beer celiac kogi crucifix kale chips adaptogen ramps synth portland asymmetrical roof party trust fund. Chillwave jean shorts man bun,
  </md-card-content>
</md-card>

<md-card  md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33">
  <md-card-media>

  </md-card-media>

  <md-card-content>
   wayfarers deep v vice twee try-hard gochujang hoodie schlitz humblebrag

  </md-card-content>
</md-card>

<md-card md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33">
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>
</md-card>
</md-layout>
    )
  }
}
<commit_msg>Added responsive css<commit_after>import style from './style.css'

export default {
  render(h) {
return (
<md-layout md-gutter>

<md-layout md-flex-large="15"> </md-layout>
  <md-layout md-flex-xsmall="100" md-flex-small="50" md-flex-medium="50" md-flex-large="70">
<md-card >
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>

  <md-card-content>
  Actually austin tbh 3 wolf moon single-origin coffee farm-to-table bespoke. Everyday carry occupy try-hard pitchfork meggings. Tilde poke hashtag, raclette pour-over semiotics gochujang 8-bit echo park neutra roof party stumptown kitsch microdosing etsy. Roof party next level tofu meh humblebrag banh mi marfa master cleanse four loko small batch normcore gentrify truffaut fanny pack VHS. Tumeric photo booth microdosing, kickstarter copper mug succulents brooklyn godard air plant authentic kale chips single-origin coffee. VHS yuccie master cleanse unicorn
  </md-card-content>
</md-card>
</md-layout>
<md-layout md-flex-large="15"> </md-layout>

<md-card  md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33">
  <md-card-header>
    <md-card-header-text>

Title goes here

Subtitle here

    </md-card-header-text>

    <md-menu md-size="4" md-direction="bottom left">
      <md-button class="md-icon-button" md-menu-trigger>
        <md-icon>more_vert</md-icon>
      </md-button>

      <md-menu-content>
        <md-menu-item>
          Call
          <md-icon>phone</md-icon>
        </md-menu-item>

        <md-menu-item>
          Send a message
          <md-icon>message</md-icon>
        </md-menu-item>
      </md-menu-content>
    </md-menu>
  </md-card-header>

  <md-card-media>

  </md-card-media>

  <md-card-content>
  quinoa swag af ramps snackwave irony poke. Aesthetic actually XOXO lomo, DIY beard poke vinyl godard trust fund hoodie skateboard seitan hot chicken mixtape. Drinking vinegar organic occupy, truffaut vinyl palo santo yuccie tbh. Seitan small batch unicorn pinterest 90's hoodie hashtag master cleanse dreamcatcher kitsch. Pinterest flexitarian craft beer celiac kogi crucifix kale chips adaptogen ramps synth portland asymmetrical roof party trust fund. Chillwave jean shorts man bun,
  </md-card-content>
</md-card>

<md-card  md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33">
  <md-card-media>

  </md-card-media>

  <md-card-content>
   wayfarers deep v vice twee try-hard gochujang hoodie schlitz humblebrag

  </md-card-content>
</md-card>

<md-card md-flex-xsmall="100" md-flex-small="50" md-flex-medium="33">
  <md-card-media>

  </md-card-media>

  <md-card-header>

Title goes here

Subtitle here

  </md-card-header>

  <md-card-actions>
    <md-button>Action</md-button>
    <md-button>Action</md-button>
  </md-card-actions>
</md-card>
</md-layout>
    )
  }
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import { reduxForm } from 'redux-form';

import { createPost } from '../actions/index';
import Header from './header';

class PostsNew extends Component {

  static contextTypes = {
    router: PropTypes.object
  };

  constructor(args) {
    super(args);

    this.onSubmit = this.onSubmit.bind(this);
  }

  onSubmit(props) {
    this.props.createPost(props)
      .then(() => {
        this.context.router.push('/');
      });
  }

  render() {

    const { fields: { title, categories, content }, handleSubmit } = this.props;

    const isValid = (field) => !(field.touched && field.invalid);

    return (

        <form onSubmit={handleSubmit(this.onSubmit)} className="content-container">

Create A New Post

          <div className={`form-group ${!isValid(title) ? 'has-danger' : ''}`}>
            Title
            <input type="text" className={`form-control ${!isValid(title) ? 'form-control-danger' : ''}`} {...title} />

              {title.touched ? title.error : ''}

          <div className={`form-group ${!isValid(categories) ? 'has-danger' : ''}`}>
            Categories
            <input type="text" className={`form-control ${!isValid(categories) ? 'form-control-danger' : ''}`} {...categories} />

              {categories.touched ? categories.error : ''}

          <div className={`form-group ${!isValid(content) ? 'has-danger' : ''}`}>
            Content
            <textarea className={`form-control ${!isValid(content) ? 'form-control-danger' : ''}`} {...content}/>

              {content.touched ? content.error : ''}

          <button type="submit" className="btn btn-outline-primary">Submit</button>
          <Link to="/" className="btn btn-outline-danger">Cancel</Link>
        </form>

    );
  };
};

function validate(values) {
  const errors = {};

  if (!values.title) {
    errors.title = 'Enter a username.';
  }

  if (!values.categories) {
    errors.categories = 'Enter categories.';
  }

  if (!values.content) {
    errors.content = 'Enter some content.';
  }

  return errors;
}

export default reduxForm({
  form: 'PostsNewForm',
  fields: [
    'title',
    'categories',
    'content'
  ],
  validate
}, null, { createPost })(PostsNew);
<commit_msg>New post form refactoring<commit_after>import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import { reduxForm } from 'redux-form';
import _ from 'lodash';

import { createPost } from '../actions/index';
import Header from './header';

const FIELDS = {
  title: {
    type: 'input',
    label: 'Title',
    requiredMessage: 'Title cannot be empty.'
  },
  categories: {
    type: 'input',
    label: 'Categories',
    requiredMessage: 'Please enter categories for the post.'
  },
  content: {
    type: 'textarea',
    label: 'Contents',
    requiredMessage: 'Post content cannot be empty.'
  }
}

class PostsNew extends Component {

  static contextTypes = {
    router: PropTypes.object
  };

  constructor(args) {
    super(args);

    ['onSubmit', 'renderField']
      .forEach((method) => this[method] = this[method].bind(this));
  }

  onSubmit(props) {
    this.props.createPost(props)
      .then(() => {
        this.context.router.push('/');
      });
  }

  renderField(fieldConfiguration, field) {
    const fieldHelper = this.props.fields[field];

    return (
      <div className={`form-group ${fieldHelper.touched && fieldHelper.invalid ? 'has-danger' : ''}`} key={field}>
        {fieldConfiguration.label}
        <fieldConfiguration.type type="text"
          className={`form-control ${fieldHelper.touched && fieldHelper.invalid ? 'form-control-danger' : ''}`}
          {...fieldHelper} />

          {fieldHelper.touched ? fieldHelper.error : ''}

    );
  }

  render() {

    return (

        <form onSubmit={this.props.handleSubmit(this.onSubmit)} className="content-container">

Create A New Post

          {_.map(FIELDS, this.renderField)}
          <button type="submit" className="btn btn-outline-primary">Submit</button>
          <Link to="/" className="btn btn-outline-danger">Cancel</Link>
        </form>

    );
  };
};

function validate(values) {
  const errors = {};

  _.each(FIELDS, (configuration, field) => {
    if (!values[field]) {
      errors[field] = configuration.requiredMessage;
    }
  });

  return errors;
}

export default reduxForm({
  form: 'PostsNewForm',
  fields: _.keys(FIELDS),
  validate
}, null, { createPost })(PostsNew);
<|endoftext|>


<commit_before>(function( global ) {

	var $at = function() {};
	if ( typeof ga !== "undefined" ) {
		$at = function( category, name, label, count, options ) {
			if ( arguments.length == 1 ) {
				ga( 'send', 'pageview', arguments[0] );
			} else {
				ga( 'send', 'event', category, name, label, count, options );
			}
		};
	} else if ( typeof wcs !== "undefined" ) {
		$at = function( category, name, label ) {
			if ( arguments.length == 1 ) return;
			if ( typeof label !== "undefined" || typeof label !== "null"  )  {
				name = name + "_" + label;
			}
			wcs.event( category, name );
		};
	}
    $at.MAIN = "Main";
	$at.CONTEXT = "Context";
	$at.CALLSTACK = "CallStack";
	$at.MIXEDVIEW = "MixedView";

	$at.CLK_APPLICATION = "ClickApplication";
	$at.CLK_TIME = "ClickTime";
	$at.CLK_SEARCH_NODE = "ClickSearchNode";
	$at.CLK_CLEAR_SEARCH = "ClickClearSearch";
	$at.CLK_NODE = "ClickNode";
	$at.CLK_LINK = "ClickLink";
	$at.CLK_UPDATE_TIME = "ClickUpdateTime";
	$at.CLK_HELP = "ClickHelp";
	$at.CLK_SCATTER_SETTING = "ClickScatterSetting";
	$at.CLK_DOWNLOAD_SCATTER = "ClickDownloadScatter";
	$at.CLK_RESPONSE_GRAPH = "ClickResponseGraph";
	$at.CLK_LOAD_GRAPH = "ClickLoadGraph";
	$at.CLK_SHOW_GRAPH = "ClickShowGraph";
	$at.CLK_FILTER_TRANSACTION = "ClickFilterTransaction";
	$at.CLK_FILTER_TRANSACTION_WIZARD = "ClickFilterTransactionWizard";
	$at.CLK_MORE = "ClickMore";
	$at.CLK_DISTRIBUTED_CALL_FLOW = "ClickDistributedCallFlow";
	$at.CLK_SERVER_MAP = "ClickServerMap";
	$at.CLK_RPC_TIMELINE = "ClickRPCTimeline";
	$at.CLK_CALL = "ClickCall";
	$at.CLK_TRANSACTION = "ClickTransaction";
	$at.CLK_HEAP = "ClickHeap";
	$at.CLK_PERM_GEN = "ClickPermGen";
	$at.CLK_CPU_LOAD = "ClickCpuLoad";
	$at.CLK_REFRESH = "ClickRefresh";

	$at.TG_DATE = "ToggleDate";
	$at.TG_UPDATE_ON = "ToggleUpdateOn";
	$at.TG_NODE_VIEW = "ToggleNodeView";
	$at.TG_SCATTER_SUCCESS = "ToggleScatterSuccess";
	$at.TG_SCATTER_FAILED = "ToggleScatterFailed";
	$at.TG_MERGE_TYPE = "ToggleMergeType";
	$at.TG_CALL_COUNT = "ToggleCallCount";
	$at.TG_TPS = "ToggleTPS";
	$at.TG_ROUTING = "ToggleRouting";
	$at.TG_CURVE = "ToggleCurve";

	$at.ST_ = "Sort";

	$at.ASCENDING = "ascending";
	$at.DESCENDING = "descending";

	$at.ON = "on";
	$at.OFF = "off";

	$at.MAIN_PAGE = "/main.page";
	$at.FILTEREDMAP_PAGE = "/filteredMap.page";
	$at.INSPECTOR_PAGE = "/inspector.page";
	$at.SCATTER_FULL_SCREEN_PAGE = "/scatterFullScreen.page";
	$at.TRANSACTION_DETAIL_PAGE = "/transactionDetail.page";
	$at.TRANSACTION_LIST_PAGE = "/transactionList.page";
	$at.TRANSACTION_VIEW_PAGE = "/transactionView.page";

	global.$at = $at;
})(window);<commit_msg>Added property for sendUsage configuration<commit_after>(function( global, $ ) {
	var bSendAllowed = true;
	var $at = function() {};
	if ( typeof ga !== "undefined" && bSendAllowed === true ) {
		$at = function( category, name, label, count, options ) {
			if ( arguments.length == 1 ) {
				ga( 'send', 'pageview', arguments[0] );
			} else {
				ga( 'send', 'event', category, name, label, count, options );
			}
		};
	} else if ( typeof wcs !== "undefined" ) {
		$at = function( category, name, label ) {
			if ( arguments.length == 1 ) return;
			if ( typeof label !== "undefined" || typeof label !== "null"  )  {
				name = name + "_" + label;
			}
			wcs.event( category, name );
		};
	}
    $at.MAIN = "Main";
	$at.CONTEXT = "Context";
	$at.CALLSTACK = "CallStack";
	$at.MIXEDVIEW = "MixedView";

	$at.CLK_APPLICATION = "ClickApplication";
	$at.CLK_TIME = "ClickTime";
	$at.CLK_SEARCH_NODE = "ClickSearchNode";
	$at.CLK_CLEAR_SEARCH = "ClickClearSearch";
	$at.CLK_NODE = "ClickNode";
	$at.CLK_LINK = "ClickLink";
	$at.CLK_UPDATE_TIME = "ClickUpdateTime";
	$at.CLK_HELP = "ClickHelp";
	$at.CLK_SCATTER_SETTING = "ClickScatterSetting";
	$at.CLK_DOWNLOAD_SCATTER = "ClickDownloadScatter";
	$at.CLK_RESPONSE_GRAPH = "ClickResponseGraph";
	$at.CLK_LOAD_GRAPH = "ClickLoadGraph";
	$at.CLK_SHOW_GRAPH = "ClickShowGraph";
	$at.CLK_FILTER_TRANSACTION = "ClickFilterTransaction";
	$at.CLK_FILTER_TRANSACTION_WIZARD = "ClickFilterTransactionWizard";
	$at.CLK_MORE = "ClickMore";
	$at.CLK_DISTRIBUTED_CALL_FLOW = "ClickDistributedCallFlow";
	$at.CLK_SERVER_MAP = "ClickServerMap";
	$at.CLK_RPC_TIMELINE = "ClickRPCTimeline";
	$at.CLK_CALL = "ClickCall";
	$at.CLK_TRANSACTION = "ClickTransaction";
	$at.CLK_HEAP = "ClickHeap";
	$at.CLK_PERM_GEN = "ClickPermGen";
	$at.CLK_CPU_LOAD = "ClickCpuLoad";
	$at.CLK_REFRESH = "ClickRefresh";

	$at.TG_DATE = "ToggleDate";
	$at.TG_UPDATE_ON = "ToggleUpdateOn";
	$at.TG_NODE_VIEW = "ToggleNodeView";
	$at.TG_SCATTER_SUCCESS = "ToggleScatterSuccess";
	$at.TG_SCATTER_FAILED = "ToggleScatterFailed";
	$at.TG_MERGE_TYPE = "ToggleMergeType";
	$at.TG_CALL_COUNT = "ToggleCallCount";
	$at.TG_TPS = "ToggleTPS";
	$at.TG_ROUTING = "ToggleRouting";
	$at.TG_CURVE = "ToggleCurve";

	$at.ST_ = "Sort";

	$at.ASCENDING = "ascending";
	$at.DESCENDING = "descending";

	$at.ON = "on";
	$at.OFF = "off";

	$at.MAIN_PAGE = "/main.page";
	$at.FILTEREDMAP_PAGE = "/filteredMap.page";
	$at.INSPECTOR_PAGE = "/inspector.page";
	$at.SCATTER_FULL_SCREEN_PAGE = "/scatterFullScreen.page";
	$at.TRANSACTION_DETAIL_PAGE = "/transactionDetail.page";
	$at.TRANSACTION_LIST_PAGE = "/transactionList.page";
	$at.TRANSACTION_VIEW_PAGE = "/transactionView.page";

	global.$at = $at;

	$.ajax({
		url: "/configuration.pinpoint"
	}).done(function( result ) {
		bSendAllowed = result.sendUsage;
	}).fail(function() {

	});
})(window, jQuery);<|endoftext|>


<commit_before>/**
 * Prepros
 * (c) Subash Pathak
 * sbshpthk@gmail.com
 * License: MIT
 */

/*jshint browser: true, node: true, loopfunc: true*/
/*global prepros, _*/

prepros.factory('javascript', function (config, utils) {

    'use strict';

    var fs = require('fs-extra'),
        path = require('path'),
        _id = utils.id;

    var format = function (filePath, projectPath) {

        //File name
        var name = path.basename(filePath);

        //Relative input path
        var shortInput = path.relative(projectPath, filePath).replace(/\\/g, '/');

        // Output path
        var output = path.join(path.dirname(filePath), 'min', path.basename(filePath).replace(/\.js/gi, '.min.js'));

        //Find short output path
        var shortOutput = output.replace(/\\/g, '/');

        //Show Relative path if output file is within project folder
        if (path.relative(projectPath, filePath).indexOf('.' + path.sep) === -1) {

            shortOutput = path.relative(projectPath, output).replace(/\\/g, '/');
        }

        return {

            id: _id(filePath),
            pid: _id(projectPath),
            name: name,
            type: 'JS',
            input: filePath,
            shortInput: shortInput,
            output: output,
            shortOutput: shortOutput,
            config: config.getUserOptions().javascript
        };
    };

    var compile = function (file, callback) {

        var ugly = require('uglify-js');

        var options = {};

        fs.readFile(file.input, { encoding: 'utf8' }, function (err, data) {
            if (err) {

                callback(true, err.message);

            } else {

                var javascript = data.toString(),
                    error;

                if (file.config.uglify) {

                    try {

                        javascript = ugly.minify(javascript, {fromString: true}).code;

                    } catch(e) {

                        error = true;

                        callback(true, 'Error on line '+ e.line + ' col ' + e.col + ' '+ e.message + ' of '+ file.input);
                    }
                }

                var importReg = {
                    append: /\/\/(?:\s|)@(?:prepros|codekit)-append\s+(.*)/gi,
                    prepend: /\/\/(?:\s|)@(?:prepros|codekit)-prepend\s+(.*)/gi
                };

                var read = function(filePathToRead) {

                    var importedFiles = {
                        append : [],
                        prepend : []
                    };

                    var regs = Object.keys(importReg);

                    _.each(regs, function(reg) {

                        var result;

                        while ((result = importReg[reg].exec(fs.readFileSync(filePathToRead))) !== null) {

                            var importedFile;

                            result[1] = result[1].replace(/'|"/gi, '').trim();

                            //Check if path is full or just relative
                            if (result[1].indexOf(':') >= 0) {

                                importedFile = path.normalize(result[1]);

                            } else {

                                importedFile = path.join(path.dirname(filePathToRead), result[1]);
                            }

                            //Check if file exists
                            if (fs.existsSync(importedFile)) {

                                importedFiles[reg].push(importedFile);

                            } else {

                                error = true;

                                callback(true, 'Imported file "'+ importedFile +'" not found \n' + file.input);
                            }
                        }
                    });

                    return {
                        append: importedFiles.append,
                        prepend: importedFiles.prepend.reverse()
                    };
                };

                var get = function(append){

                    var imps =[];
                    imps[0] = (append)? read(file.input).append: read(file.input).prepend;

                    //Get imports of imports up to four levels
                    for(var i=1; i<5; i++) {

                        imps[i] = [];

                        _.each(imps[i-1], function(importedFile){

                            imps[i] = _.uniq(_.union(imps[i], (append)? read(importedFile).append: read(importedFile).prepend));
                        });
                    }

                    return _.uniq(_.flatten(imps));

                };

                var join = function(files, append) {

                    try {

                        //Remove repeated imports
                        _.each(_.uniq(_.flatten(files)), function(imp) {

                            var js = fs.readFileSync(imp).toString();

                            if (file.config.uglify && !/min.js$/.exec(path.basename(imp))) {

                                try {

                                    js = ugly.minify(js, {fromString: true}).code;

                                } catch(e) {

                                    error = true;

                                    callback(true, 'Error on line '+ e.line + ' col ' + e.col + ' '+ e.message + ' of '+ imp);
                                }
                            }

                            if(append) {

                                javascript = javascript + '\n'  + js;

                            } else {

                                javascript = js + '\n' + javascript;
                            }

                        });

                    } catch(e) {

                        error = true;

                        callback(true, e.message);
                    }
                };

                //Join Files
                var appends = get(true);
                var prepends = get(false);
                join(appends, true);
                join(prepends, false);

                if(!error) {

                    _.each(importReg, function(reg){

                        javascript = javascript.replace(new RegExp(reg.source + '\n', 'gi'), '');

                    });

                    fs.outputFile(file.output, javascript, function (err) {

                        if (err) {

                            callback(true, err.message);

                        } else {

                            callback(false, file.input);
                        }
                    });
                }
            }
        });
    };

    return {
        format: format,
        compile: compile
    };
});<commit_msg>removed newline #97<commit_after>/**
 * Prepros
 * (c) Subash Pathak
 * sbshpthk@gmail.com
 * License: MIT
 */

/*jshint browser: true, node: true, loopfunc: true*/
/*global prepros, _*/

prepros.factory('javascript', function (config, utils) {

    'use strict';

    var fs = require('fs-extra'),
        path = require('path'),
        _id = utils.id;

    var format = function (filePath, projectPath) {

        //File name
        var name = path.basename(filePath);

        //Relative input path
        var shortInput = path.relative(projectPath, filePath).replace(/\\/g, '/');

        // Output path
        var output = path.join(path.dirname(filePath), 'min', path.basename(filePath).replace(/\.js/gi, '.min.js'));

        //Find short output path
        var shortOutput = output.replace(/\\/g, '/');

        //Show Relative path if output file is within project folder
        if (path.relative(projectPath, filePath).indexOf('.' + path.sep) === -1) {

            shortOutput = path.relative(projectPath, output).replace(/\\/g, '/');
        }

        return {

            id: _id(filePath),
            pid: _id(projectPath),
            name: name,
            type: 'JS',
            input: filePath,
            shortInput: shortInput,
            output: output,
            shortOutput: shortOutput,
            config: config.getUserOptions().javascript
        };
    };

    var compile = function (file, callback) {

        var ugly = require('uglify-js');

        var options = {};

        fs.readFile(file.input, { encoding: 'utf8' }, function (err, data) {
            if (err) {

                callback(true, err.message);

            } else {

                var javascript = data.toString(),
                    error;

                if (file.config.uglify) {

                    try {

                        javascript = ugly.minify(javascript, {fromString: true}).code;

                    } catch(e) {

                        error = true;

                        callback(true, 'Error on line '+ e.line + ' col ' + e.col + ' '+ e.message + ' of '+ file.input);
                    }
                }

                var importReg = {
                    append: /\/\/(?:\s|)@(?:prepros|codekit)-append\s+(.*)/gi,
                    prepend: /\/\/(?:\s|)@(?:prepros|codekit)-prepend\s+(.*)/gi
                };

                var read = function(filePathToRead) {

                    var importedFiles = {
                        append : [],
                        prepend : []
                    };

                    var regs = Object.keys(importReg);

                    _.each(regs, function(reg) {

                        var result;

                        while ((result = importReg[reg].exec(fs.readFileSync(filePathToRead))) !== null) {

                            var importedFile;

                            result[1] = result[1].replace(/'|"/gi, '').trim();

                            //Check if path is full or just relative
                            if (result[1].indexOf(':') >= 0) {

                                importedFile = path.normalize(result[1]);

                            } else {

                                importedFile = path.join(path.dirname(filePathToRead), result[1]);
                            }

                            //Check if file exists
                            if (fs.existsSync(importedFile)) {

                                importedFiles[reg].push(importedFile);

                            } else {

                                error = true;

                                callback(true, 'Imported file "'+ importedFile +'" not found \n' + file.input);
                            }
                        }
                    });

                    return {
                        append: importedFiles.append,
                        prepend: importedFiles.prepend.reverse()
                    };
                };

                var get = function(append){

                    var imps =[];
                    imps[0] = (append)? read(file.input).append: read(file.input).prepend;

                    //Get imports of imports up to four levels
                    for(var i=1; i<5; i++) {

                        imps[i] = [];

                        _.each(imps[i-1], function(importedFile){

                            imps[i] = _.uniq(_.union(imps[i], (append)? read(importedFile).append: read(importedFile).prepend));
                        });
                    }

                    return _.uniq(_.flatten(imps));

                };

                var join = function(files, append) {

                    try {

                        //Remove repeated imports
                        _.each(_.uniq(_.flatten(files)), function(imp) {

                            var js = fs.readFileSync(imp).toString();

                            if (file.config.uglify && !/min.js$/.exec(path.basename(imp))) {

                                try {

                                    js = ugly.minify(js, {fromString: true}).code;

                                } catch(e) {

                                    error = true;

                                    callback(true, 'Error on line '+ e.line + ' col ' + e.col + ' '+ e.message + ' of '+ imp);
                                }
                            }

                            if(append) {

                                javascript = javascript + js;

                            } else {

                                javascript = js + javascript;
                            }

                        });

                    } catch(e) {

                        error = true;

                        callback(true, e.message);
                    }
                };

                //Join Files
                var appends = get(true);
                var prepends = get(false);
                join(appends, true);
                join(prepends, false);

                if(!error) {

                    _.each(importReg, function(reg){

                        javascript = javascript.replace(new RegExp(reg.source + '\n', 'gi'), '');

                    });

                    fs.outputFile(file.output, javascript, function (err) {

                        if (err) {

                            callback(true, err.message);

                        } else {

                            callback(false, file.input);
                        }
                    });
                }
            }
        });
    };

    return {
        format: format,
        compile: compile
    };
});<|endoftext|>


<commit_before>'use strict';

starChat.Stream = (function () {
    var Stream = function (packetProcessor) {
        this.packetProcessor_ = packetProcessor;
        this.continuingErrorNum_ = 0;
        this.ajax_ = null;
    };
    Stream.prototype.start = function (view) {
        if (this.ajax_) {
            return;
        }
        this.continuingErrorNum_ = 0;
        var self = this;
        var session = view.session();
        var streamReadIndex = 0;
        var url = '/users/' + encodeURIComponent(session.userName()) + '/stream';
        var startStream = function () {
            self.start(view);
        };
        var callbacks = {
            onprogress: function () {
                // TODO: Reconnecting if overflow
                var xhr = this;
                var text = xhr.responseText;
                var subText = text.substring(streamReadIndex);
                while (true) {
                    var tokenLength = subText.search("\n");
                    if (tokenLength === -1) {
                        break;
                    }
                    streamReadIndex += tokenLength + 1;
                    var token = subText.substring(0, tokenLength);
                    subText = subText.substring(tokenLength + 1);
                    try {
                        var packet = JSON.parse(token);
                    } catch (e) {
                        console.log(e);
                        continue;
                    }
                    self.continuingErrorNum_ = 0;
                    self.packetProcessor_.process(packet, view);
                }
                view.update();
            },
            success: function (data, textStatus, jqXHR) {
                self.continuingErrorNum_ = 0;
                setTimeout(startStream, 0);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                console.log(textStatus);
                self.continuingErrorNum_++;
                if (10 <= self.ontinuingErrorNum_) {
                    console.log('Too many errors!');
                    // TODO: implement showing error message
                    return;
                }
                setTimeout(startStream, 1000);
            },
        };
        this.ajax_ = starChat.ajax(session.userName(), session.password(),
                                   url,
                                   'GET',
                                   callbacks);
    };
    Stream.prototype.stop = function () {
        if (!this.ajax_) {
            return;
        }
        this.continuingErrorNum_ = 0;
        this.ajax_.abort();
        this.ajax_ = null;
    };
    return Stream;
})();
<commit_msg>Bug fix: reconnecting stream<commit_after>'use strict';

starChat.Stream = (function () {
    var Stream = function (packetProcessor) {
        this.packetProcessor_ = packetProcessor;
        this.continuingErrorNum_ = 0;
        this.ajax_ = null;
    };
    Stream.prototype.start = function (view) {
        if (this.ajax_) {
            console.error('An ajax object already exists!');
            return;
        }
        this.continuingErrorNum_ = 0;
        var self = this;
        var session = view.session();
        var streamReadIndex = 0;
        var url = '/users/' + encodeURIComponent(session.userName()) + '/stream';
        var startStream = function () {
            if (self.ajax_) {
                self.ajax_.abort();
                self.ajax_ = null;
            }
            self.start(view);
        };
        var callbacks = {
            onprogress: function () {
                console.log('Reading stream...');
                // TODO: Reconnecting if overflow
                var xhr = this;
                var text = xhr.responseText;
                var subText = text.substring(streamReadIndex);
                while (true) {
                    var tokenLength = subText.search("\n");
                    if (tokenLength === -1) {
                        break;
                    }
                    streamReadIndex += tokenLength + 1;
                    var token = subText.substring(0, tokenLength);
                    subText = subText.substring(tokenLength + 1);
                    try {
                        var packet = JSON.parse(token);
                    } catch (e) {
                        console.log(e);
                        continue;
                    }
                    self.continuingErrorNum_ = 0;
                    self.packetProcessor_.process(packet, view);
                }
                view.update();
            },
            success: function (data, textStatus, jqXHR) {
                self.continuingErrorNum_ = 0;
                setTimeout(startStream, 0);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                console.error('Stream Error!');
                console.error(textStatus);
                self.continuingErrorNum_++;
                if (20 <= self.ontinuingErrorNum_) {
                    console.error('Too many errors!');
                    // TODO: implement showing error message
                    return;
                }
                setTimeout(startStream, 1000);
            },
        };
        console.log('Connection stream...');
        this.ajax_ = starChat.ajax(session.userName(), session.password(),
                                   url,
                                   'GET',
                                   callbacks);
    };
    Stream.prototype.stop = function () {
        if (!this.ajax_) {
            return;
        }
        this.continuingErrorNum_ = 0;
        this.ajax_.abort();
        this.ajax_ = null;
    };
    return Stream;
})();
<|endoftext|>