<commit_before>define([ 'jquery', 'underscore', 'widgy.backbone', 'widgy.contents',
    'text!nodes/node_view.html',
    'text!nodes/node_preview_view.html',
    'text!nodes/drop_target_view.html'
    ], function($, _, Backbone, contents,
      node_view_template,
      node_preview_view_template,
      drop_target_view_template
      ) {

  /**
   * Nodes provide structure in the tree.  Nodes only hold data that deals with
   * structure.  Any other data lives in it's content.
   *
   * A node will have two properties: `children` and `content`.  `children` is
   * a NodeCollection which is basically just a list of child nodes. `content`
   * is model containing all non-structure information about a node.  The
   * actual Model Class that defines the content property depends on the
   * content type.  See `widgy.contents.js` for more information.
   */
  var Node = Backbone.Model.extend({
    urlRoot: '/admin/widgy/node/',

    initialize: function() {
      Backbone.Model.prototype.initialize.apply(this, arguments);

      _.bindAll(this,
        'instantiateContent',
        'checkDidReposition'
        );

      this
        .on('change', this.checkDidReposition)
        .on('change:content', this.loadContent);

      // same as content.  We need to actually instantiate the NodeCollection
      // and set it as a property, not an attribute.
      var children = this.get('children');
      this.children = new NodeCollection(children);
      this.unset('children');
    },

    checkDidReposition: function() {
      if ( this.hasChanged('parent_id') ||
          (this.hasChanged('right_id') && this.id !== this.get('right_id')) )
      {
        this.trigger('reposition', this, this.get('parent_id'), this.get('right_id'));
      }
    },

    checkIsContentLoaded: function() {
      if ( this.content && this.content instanceof contents.Content ) {
        this.trigger('load:content', this.content);
      } else {
        this.loadContent(this, this.get('content'));
      }
    },

    loadContent: function(model, content) {
      if ( content )
      {
        // content gets set because it is in the JSON for the node.  We need to
        // unset it as it is not an attribute, but a property.  We also need to
        // instantiate it as a real Content Model.
        this.unset('content');

        // This is asynchronous because of requirejs.
        contents.getModel(content.__module_name__, _.bind(this.instantiateContent, this, content));
      }
    },

    instantiateContent: function(content, model_class) {
      this.content = new model_class(content);

      this.trigger('load:content', this.content);
    }
  });

  /**
   * NodeCollections provide the children interface for nodes and also an
   * interface to NodeViews for how to handle child NodeViews.
   */
  var NodeCollection = Backbone.Collection.extend({
    model: Node
  });

  /**
   * Provides an interface for a draggable NodeView.  See NodeView for more
   * specific Node functionality related definition.  NodeViewBase is exposed
   * for subclassing by a NodePreviewView and NodeView.
   *
   * TODO: Does NodePreviewView do anything that NodeViewBase doesn't?
   */
  var NodeViewBase = Backbone.View.extend({
    className: 'node',

    events: {
      'mousedown .drag_handle': 'startDrag'
    },

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

      _.bindAll(this,
        'startDrag',
        'afterStartDrag',
        'followMouse',
        'stopDrag',
        'reposition',
        'addDropTargets',
        'clearDropTargets'
      );

      this.model
        .on('remove', this.close)
        .on('destroy', this.close)
        .on('reposition', this.reposition);

      this.app = options.app;
      this.app.node_view_list.push(this);

      this.drop_targets_list = new Backbone.ViewList;
    },

    /**
     * `startDrag`, `stopDrag`, `followMouse`, and `reposition` all deal with a
     * NodeView itself being dragged around.
     */
    startDrag: function(event) {
      event.preventDefault();
      event.stopPropagation();

      this.app.startDrag(this);
      this.afterStartDrag();

      // Store the mouse offset in this container for followMouse to
      // use.
      this.offsetY = event.offsetY;
      this.offsetX = event.offsetX;

      // follow mouse really quick, just in case they haven't moved their mouse
      // yet.
      this.followMouse(event);

      this.$el.addClass('being_dragged');
    },

    /**
     * Hook called when a node is starting to be dragged.
     */
    afterStartDrag: function() {},

    stopDrag: function() {
      this.$el.css({
        position: 'static'
      });

      this.$el.removeClass('being_dragged');
    },

    followMouse: function(event) {
      this.$el.css({
        position: 'absolute',
        top: event.pageY - this.offsetY,
        left: event.pageX - this.offsetX
      });
    },

    /**
     * If the node has been put into a different parent, we need to update the
     * collection.  That parent will be listening for adding and it will handle
     * the positioning.  Otherwise, this node is still in the right parent and
     * it just needs to be positioned in the parent.
     *
     * This is a callback to the reposition event on the Node model.
     */
    reposition: function(model, parent_id, right_id) {
      var parent_view = this.app.node_view_list.findById(parent_id);

      // This line is a little confusing.  For a model, the
      // `collection` property is its parent collection, for a view,
      // the `collection` is a child.  If a model has the same
      // `collection` as a view, that means the view is the parent
      // of the model.
      if ( model.collection !== parent_view.collection ) {
        model.collection.remove(model);
        parent_view.collection.add(model);
      } else {
        parent_view.position(this);
      }
    },

    /**
     * `addDropTargets` and `clearDropTargets` are required as an API for the
     * AppView. See NodeView for the actual implementation.
     */
    addDropTargets: function() {},
    clearDropTargets: function() {}
  });

  /**
   * The NodeView provides an interface to the node.  It will also create a
   * ContentView for the node's content.  Additionally, it will create child
   * NodeViews for all of the nodes in children.
   *
   * Properties:
   *
   * -  `this.model` is the node.
   * -  `this.collection` is the node's children (Also available in
   *    `this.model.children`.
   * -  `this.app` is the instance of AppView
   */
  var NodeView = NodeViewBase.extend({
    template: node_view_template,

    events: function() {
      return _.extend({}, NodeViewBase.prototype.events , {
        'click .delete': 'delete'
      });
    },

    initialize: function() {
      NodeViewBase.prototype.initialize.apply(this, arguments);

      _.bindAll(this,
        'addAll',
        'addOne',
        'position',
        'createDropTarget',
        'dropChildView',
        'renderContent'
        );

      this.model
        .on('load:content', this.renderContent);

      this.collection = this.model.children;

      this.collection
        .on('reset', this.addAll)
        .on('add', this.addOne);
    },

    addAll: function() {
      this.collection.each(this.addOne);
    },

    addOne: function(node) {
      var node_view = new NodeView({
        model: node,
        app: this.app
      });

      this.position(node_view.render());
    },

    'delete': function(event) {
      event.stopPropagation();
      this.model.destroy();
    },

    /**
     * `addDropTargets`, `createDropTarget`, `clearDropTargets`, `position`,
     * and `dropChildView` all deal with a possible child NodeView being
     * dragged.  It is confusing that these methods are on the same class that
     * the methods dealing with being dragged around are on, but that's the
     * nature of the beast with recursive nodes.
     */
    addDropTargets: function() {
      var $children = this.$children,
          that = this;

      $children.prepend(this.createDropTarget().el);
      $children.children('.node').each(function(index, elem) {
        $(elem).after(that.createDropTarget().el);
      });
    },

    createDropTarget: function() {
      var drop_target = new DropTargetView;
      drop_target.on('dropped', this.dropChildView);
      this.drop_targets_list.push(drop_target);

      return drop_target.render();
    },

    clearDropTargets: function() {
      this.drop_targets_list.closeAll();
    },

    position: function(child_node_view) {
      var child_node = child_node_view.model;

      if ( child_node.get('right_id') ) {
        var right_id = child_node.get('right_id'),
          right_view = this.app.node_view_list.findById(right_id);
          right_view.$el.before(child_node_view.el);
      } else {
        this.$children.append(child_node_view.el);
      }
    },

    /**
     * This is the method that is called when the NodeView that is being
     * dragged is dropped on one of my drop targets.
     */
    dropChildView: function(view) {
      var $children = this.$children;
      var index = view.$el.index() / 2;

      // We need to stop the drag before finding the right node.
      // `this.app.stopDrag` will clear all of the drop targets, so we need to
      // remove them before we can get elements by index.
      var dragged_view = this.app.stopDrag();

      var right_id = null;

      // If index is the length of $children.children there is no right element
      // and we want it set to null.  Otherwise there is a right and we need
      // its id.
      //
      // ($children.children() refers to DOM elements.)
      if ( index !== $children.children().length ) {
        var right_el = $children.children().eq(index)[0],
            right_view = this.app.node_view_list.findByEl(right_el);

        right_id = right_view.model.id;
      }

      // Dragged into my own drop target.
      if ( dragged_view === right_view )
        return;

      // pessimistic save (for now).
      dragged_view.model.save({
        parent_id: this.model.id,
        right_id: right_id
      }, {wait: true});
    },

    afterStartDrag: function() {
      // hide drop target behind me.
      this.$el.next().hide();
      this.clearDropTargets();
    },

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

      this.$children = this.$('.children:first');
      this.$content = this.$('.content:first');

      // TODO: this could be like a document.ready sorta?
      this.model.checkIsContentLoaded();

      // TODO: investigate possible problems with this.
      this.addAll();

      return this;
    },

    renderContent: function(content) {
      var view_class = content.getViewClass();

      content_view = new view_class({
        model: content
      });

      this.$content.html(content_view.render().el);
    }
  });

  var NodePreviewView = NodeViewBase.extend({
    tagName: 'li',
    template: node_preview_view_template
  });

  var DropTargetView = Backbone.View.extend({
    className: 'node_drop_target',

    template: drop_target_view_template,

    triggers: {
      'mouseup': 'dropped'
    },

    events: {
      'mouseenter': 'activate',
      'mouseleave': 'deactivate'
    },

    activate: function(event) {
      this.$el.addClass('active');
    },

    deactivate: function(event) {
      this.$el.removeClass('active');
    }
  });

  return {
    DropTargetView: DropTargetView,
    Node: Node,
    NodeCollection: NodeCollection,
    NodeView: NodeView,
    NodePreviewView: NodePreviewView,
    NodeViewBase: NodeViewBase
  };

});
<commit_msg>only drag on left click<commit_after>define([ 'jquery', 'underscore', 'widgy.backbone', 'widgy.contents',
    'text!nodes/node_view.html',
    'text!nodes/node_preview_view.html',
    'text!nodes/drop_target_view.html'
    ], function($, _, Backbone, contents,
      node_view_template,
      node_preview_view_template,
      drop_target_view_template
      ) {

  /**
   * Nodes provide structure in the tree.  Nodes only hold data that deals with
   * structure.  Any other data lives in it's content.
   *
   * A node will have two properties: `children` and `content`.  `children` is
   * a NodeCollection which is basically just a list of child nodes. `content`
   * is model containing all non-structure information about a node.  The
   * actual Model Class that defines the content property depends on the
   * content type.  See `widgy.contents.js` for more information.
   */
  var Node = Backbone.Model.extend({
    urlRoot: '/admin/widgy/node/',

    initialize: function() {
      Backbone.Model.prototype.initialize.apply(this, arguments);

      _.bindAll(this,
        'instantiateContent',
        'checkDidReposition'
        );

      this
        .on('change', this.checkDidReposition)
        .on('change:content', this.loadContent);

      // same as content.  We need to actually instantiate the NodeCollection
      // and set it as a property, not an attribute.
      var children = this.get('children');
      this.children = new NodeCollection(children);
      this.unset('children');
    },

    checkDidReposition: function() {
      if ( this.hasChanged('parent_id') ||
          (this.hasChanged('right_id') && this.id !== this.get('right_id')) )
      {
        this.trigger('reposition', this, this.get('parent_id'), this.get('right_id'));
      }
    },

    checkIsContentLoaded: function() {
      if ( this.content && this.content instanceof contents.Content ) {
        this.trigger('load:content', this.content);
      } else {
        this.loadContent(this, this.get('content'));
      }
    },

    loadContent: function(model, content) {
      if ( content )
      {
        // content gets set because it is in the JSON for the node.  We need to
        // unset it as it is not an attribute, but a property.  We also need to
        // instantiate it as a real Content Model.
        this.unset('content');

        // This is asynchronous because of requirejs.
        contents.getModel(content.__module_name__, _.bind(this.instantiateContent, this, content));
      }
    },

    instantiateContent: function(content, model_class) {
      this.content = new model_class(content);

      this.trigger('load:content', this.content);
    }
  });

  /**
   * NodeCollections provide the children interface for nodes and also an
   * interface to NodeViews for how to handle child NodeViews.
   */
  var NodeCollection = Backbone.Collection.extend({
    model: Node
  });

  /**
   * Provides an interface for a draggable NodeView.  See NodeView for more
   * specific Node functionality related definition.  NodeViewBase is exposed
   * for subclassing by a NodePreviewView and NodeView.
   *
   * TODO: Does NodePreviewView do anything that NodeViewBase doesn't?
   */
  var NodeViewBase = Backbone.View.extend({
    className: 'node',

    events: {
      'mousedown .drag_handle': 'startDrag'
    },

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

      _.bindAll(this,
        'startDrag',
        'afterStartDrag',
        'followMouse',
        'stopDrag',
        'reposition',
        'addDropTargets',
        'clearDropTargets'
      );

      this.model
        .on('remove', this.close)
        .on('destroy', this.close)
        .on('reposition', this.reposition);

      this.app = options.app;
      this.app.node_view_list.push(this);

      this.drop_targets_list = new Backbone.ViewList;
    },

    /**
     * `startDrag`, `stopDrag`, `followMouse`, and `reposition` all deal with a
     * NodeView itself being dragged around.
     */
    startDrag: function(event) {
      // only on a left click.
      if ( event.button !== 0 )
        return;

      event.preventDefault();
      event.stopPropagation();

      this.app.startDrag(this);
      this.afterStartDrag();

      // Store the mouse offset in this container for followMouse to
      // use.
      this.offsetY = event.offsetY;
      this.offsetX = event.offsetX;

      // follow mouse really quick, just in case they haven't moved their mouse
      // yet.
      this.followMouse(event);

      this.$el.addClass('being_dragged');
    },

    /**
     * Hook called when a node is starting to be dragged.
     */
    afterStartDrag: function() {},

    stopDrag: function() {
      this.$el.css({
        position: 'static'
      });

      this.$el.removeClass('being_dragged');
    },

    followMouse: function(event) {
      this.$el.css({
        position: 'absolute',
        top: event.pageY - this.offsetY,
        left: event.pageX - this.offsetX
      });
    },

    /**
     * If the node has been put into a different parent, we need to update the
     * collection.  That parent will be listening for adding and it will handle
     * the positioning.  Otherwise, this node is still in the right parent and
     * it just needs to be positioned in the parent.
     *
     * This is a callback to the reposition event on the Node model.
     */
    reposition: function(model, parent_id, right_id) {
      var parent_view = this.app.node_view_list.findById(parent_id);

      // This line is a little confusing.  For a model, the
      // `collection` property is its parent collection, for a view,
      // the `collection` is a child.  If a model has the same
      // `collection` as a view, that means the view is the parent
      // of the model.
      if ( model.collection !== parent_view.collection ) {
        model.collection.remove(model);
        parent_view.collection.add(model);
      } else {
        parent_view.position(this);
      }
    },

    /**
     * `addDropTargets` and `clearDropTargets` are required as an API for the
     * AppView. See NodeView for the actual implementation.
     */
    addDropTargets: function() {},
    clearDropTargets: function() {}
  });

  /**
   * The NodeView provides an interface to the node.  It will also create a
   * ContentView for the node's content.  Additionally, it will create child
   * NodeViews for all of the nodes in children.
   *
   * Properties:
   *
   * -  `this.model` is the node.
   * -  `this.collection` is the node's children (Also available in
   *    `this.model.children`.
   * -  `this.app` is the instance of AppView
   */
  var NodeView = NodeViewBase.extend({
    template: node_view_template,

    events: function() {
      return _.extend({}, NodeViewBase.prototype.events , {
        'click .delete': 'delete'
      });
    },

    initialize: function() {
      NodeViewBase.prototype.initialize.apply(this, arguments);

      _.bindAll(this,
        'addAll',
        'addOne',
        'position',
        'createDropTarget',
        'dropChildView',
        'renderContent'
        );

      this.model
        .on('load:content', this.renderContent);

      this.collection = this.model.children;

      this.collection
        .on('reset', this.addAll)
        .on('add', this.addOne);
    },

    addAll: function() {
      this.collection.each(this.addOne);
    },

    addOne: function(node) {
      var node_view = new NodeView({
        model: node,
        app: this.app
      });

      this.position(node_view.render());
    },

    'delete': function(event) {
      event.stopPropagation();
      this.model.destroy();
    },

    /**
     * `addDropTargets`, `createDropTarget`, `clearDropTargets`, `position`,
     * and `dropChildView` all deal with a possible child NodeView being
     * dragged.  It is confusing that these methods are on the same class that
     * the methods dealing with being dragged around are on, but that's the
     * nature of the beast with recursive nodes.
     */
    addDropTargets: function() {
      var $children = this.$children,
          that = this;

      $children.prepend(this.createDropTarget().el);
      $children.children('.node').each(function(index, elem) {
        $(elem).after(that.createDropTarget().el);
      });
    },

    createDropTarget: function() {
      var drop_target = new DropTargetView;
      drop_target.on('dropped', this.dropChildView);
      this.drop_targets_list.push(drop_target);

      return drop_target.render();
    },

    clearDropTargets: function() {
      this.drop_targets_list.closeAll();
    },

    position: function(child_node_view) {
      var child_node = child_node_view.model;

      if ( child_node.get('right_id') ) {
        var right_id = child_node.get('right_id'),
          right_view = this.app.node_view_list.findById(right_id);
          right_view.$el.before(child_node_view.el);
      } else {
        this.$children.append(child_node_view.el);
      }
    },

    /**
     * This is the method that is called when the NodeView that is being
     * dragged is dropped on one of my drop targets.
     */
    dropChildView: function(view) {
      var $children = this.$children;
      var index = view.$el.index() / 2;

      // We need to stop the drag before finding the right node.
      // `this.app.stopDrag` will clear all of the drop targets, so we need to
      // remove them before we can get elements by index.
      var dragged_view = this.app.stopDrag();

      var right_id = null;

      // If index is the length of $children.children there is no right element
      // and we want it set to null.  Otherwise there is a right and we need
      // its id.
      //
      // ($children.children() refers to DOM elements.)
      if ( index !== $children.children().length ) {
        var right_el = $children.children().eq(index)[0],
            right_view = this.app.node_view_list.findByEl(right_el);

        right_id = right_view.model.id;
      }

      // Dragged into my own drop target.
      if ( dragged_view === right_view )
        return;

      // pessimistic save (for now).
      dragged_view.model.save({
        parent_id: this.model.id,
        right_id: right_id
      }, {wait: true});
    },

    afterStartDrag: function() {
      // hide drop target behind me.
      this.$el.next().hide();
      this.clearDropTargets();
    },

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

      this.$children = this.$('.children:first');
      this.$content = this.$('.content:first');

      // TODO: this could be like a document.ready sorta?
      this.model.checkIsContentLoaded();

      // TODO: investigate possible problems with this.
      this.addAll();

      return this;
    },

    renderContent: function(content) {
      var view_class = content.getViewClass();

      content_view = new view_class({
        model: content
      });

      this.$content.html(content_view.render().el);
    }
  });

  var NodePreviewView = NodeViewBase.extend({
    tagName: 'li',
    template: node_preview_view_template
  });

  var DropTargetView = Backbone.View.extend({
    className: 'node_drop_target',

    template: drop_target_view_template,

    triggers: {
      'mouseup': 'dropped'
    },

    events: {
      'mouseenter': 'activate',
      'mouseleave': 'deactivate'
    },

    activate: function(event) {
      this.$el.addClass('active');
    },

    deactivate: function(event) {
      this.$el.removeClass('active');
    }
  });

  return {
    DropTargetView: DropTargetView,
    Node: Node,
    NodeCollection: NodeCollection,
    NodeView: NodeView,
    NodePreviewView: NodePreviewView,
    NodeViewBase: NodeViewBase
  };

});
<|endoftext|>


<commit_before>// Use the right jQuery source on the test page (and iframes)
(function() {
	/* global loadTests: true, testSubproject: false */
	/* jshint eqeqeq: false */

	var i, len,
		src = /^(.*)test\//.exec( window.location.pathname )[1],
		QUnit = window.QUnit || parent.QUnit,
		require = window.require || parent.require;

	// Config parameter to force basic code paths
	QUnit.config.urlConfig.push({
		id: "basic",
		label: "Bypass optimizations",
		tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition"
	});
	if ( QUnit.urlParams.basic ) {
		document.querySelectorAll = null;
		document.documentElement.contains = null;
		document.documentElement.compareDocumentPosition = null;
	}

	// iFrames won't load AMD (the iframe tests synchronously expect jQuery to be there)
	QUnit.config.urlConfig.push({
		id: "amd",
		label: "Load with AMD",
		tooltip: "Load the AMD jQuery file (and its dependencies)"
	});
	if ( QUnit.urlParams.amd && parent == window ) {
		require.config({ baseUrl: src });
		src = "src/jquery";
		// Include tests if specified
		if ( typeof loadTests !== "undefined" ) {
			QUnit.config.autostart = false;
			require( [ src ], function() {
				// Ensure load order (to preserve test numbers)
				(function loadDep() {
					var dep = loadTests.shift();
					if ( dep ) {
						require( [ dep ], loadDep );
					} else {
						// Subproject tests must be last because they replace our test fixture
						testSubproject( "Sizzle", "../bower_components/sizzle/test/", /^unit\/.*\.js$/ );

						QUnit.start();
					}
				})();
			});
		} else {
			require( [ src ] );
		}
		return;
	}

	// Config parameter to use minified jQuery
	QUnit.config.urlConfig.push({
		id: "dev",
		label: "Load unminified",
		tooltip: "Load the development (unminified) jQuery file"
	});
	if ( QUnit.urlParams.dev ) {
		src += "dist/jquery.js";
	} else {
		src += "dist/jquery.min.js";
	}

	// Load jQuery
	document.write( "<script id='jquery-js' src='" + src + "'><\x2Fscript>" );

	// Load tests synchronously if available
	if ( typeof loadTests !== "undefined" ) {
		for ( i = 0, len = loadTests.length; i < len; i++ ) {
			document.write( "<script src='" + loadTests.shift() + "'><\x2Fscript>" );
		}
	}
})();
<commit_msg>Correct QUnit check, this might fix swarm failures<commit_after>// Use the right jQuery source on the test page (and iframes)
(function() {
	/* global loadTests: true, testSubproject: false */
	/* jshint eqeqeq: false */

	var i, len,
		src = window.location.pathname.split( "test" )[ 0 ],
		QUnit = window.QUnit || parent.QUnit,
		require = window.require || parent.require;

	// Config parameter to force basic code paths
	QUnit.config.urlConfig.push({
		id: "basic",
		label: "Bypass optimizations",
		tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition"
	});
	if ( QUnit.urlParams.basic ) {
		document.querySelectorAll = null;
		document.documentElement.contains = null;
		document.documentElement.compareDocumentPosition = null;
	}

	// iFrames won't load AMD (the iframe tests synchronously expect jQuery to be there)
	QUnit.config.urlConfig.push({
		id: "amd",
		label: "Load with AMD",
		tooltip: "Load the AMD jQuery file (and its dependencies)"
	});
	if ( QUnit.urlParams.amd && parent == window ) {
		require.config({ baseUrl: src });
		src = "src/jquery";
		// Include tests if specified
		if ( typeof loadTests !== "undefined" ) {
			QUnit.config.autostart = false;
			require( [ src ], function() {
				// Ensure load order (to preserve test numbers)
				(function loadDep() {
					var dep = loadTests.shift();
					if ( dep ) {
						require( [ dep ], loadDep );
					} else {
						// Subproject tests must be last because they replace our test fixture
						testSubproject( "Sizzle", "../bower_components/sizzle/test/", /^unit\/.*\.js$/ );

						QUnit.start();
					}
				})();
			});
		} else {
			require( [ src ] );
		}
		return;
	}

	// Config parameter to use minified jQuery
	QUnit.config.urlConfig.push({
		id: "dev",
		label: "Load unminified",
		tooltip: "Load the development (unminified) jQuery file"
	});
	if ( QUnit.urlParams.dev ) {
		src += "dist/jquery.js";
	} else {
		src += "dist/jquery.min.js";
	}

	// Load jQuery
	document.write( "<script id='jquery-js' src='" + src + "'><\x2Fscript>" );

	// Load tests synchronously if available
	if ( typeof loadTests !== "undefined" ) {
		for ( i = 0, len = loadTests.length; i < len; i++ ) {
			document.write( "<script src='" + loadTests.shift() + "'><\x2Fscript>" );
		}
	}
})();
<|endoftext|>


<commit_before>
var myutil = (function(){

var myutil = {};

myutil.toObject = function(key, value) {
  if (typeof key === 'object') {
    return key;
  }
  var obj = {};
  obj[key] = value;
  return obj;
};

myutil.flag = function(flags) {
  if (typeof flags === 'boolean') {
    return flags;
  }
  for (var i = 1, ii = arguments.length; i < ii; ++i) {
    if (flags[arguments[i]]) {
      return true;
    }
  }
  return false;
};

myutil.toFlags = function(flags) {
  if (typeof flags === 'string') {
    flags = myutil.toObject(flags, true);
  } else {
    flags = !!flags;
  }
  return flags;
};

if (typeof module !== 'undefined') {
  module.exports = myutil;
}

return myutil;

})();
<commit_msg>Add myutil unset and makeAccessor<commit_after>
var myutil = (function(){

var myutil = {};

myutil.toObject = function(key, value) {
  if (typeof key === 'object') {
    return key;
  }
  var obj = {};
  obj[key] = value;
  return obj;
};

myutil.flag = function(flags) {
  if (typeof flags === 'boolean') {
    return flags;
  }
  for (var i = 1, ii = arguments.length; i < ii; ++i) {
    if (flags[arguments[i]]) {
      return true;
    }
  }
  return false;
};

myutil.toFlags = function(flags) {
  if (typeof flags === 'string') {
    flags = myutil.toObject(flags, true);
  } else {
    flags = !!flags;
  }
  return flags;
};

myutil.unset = function(obj, k) {
  if (typeof k === 'undefined') {
    k = obj;
    obj = this.state;
  }
  if (typeof obj === 'object') {
    delete obj[k];
  }
};

myutil.makeAccessor = function(k) {
  return function(value) {
    if (arguments.length === 0) {
      return this.state[k];
    } else {
      this.state[k] = value;
      if (this._prop() === this) {
        this._prop(k, value);
      }
      return this;
    }
  };
};

if (typeof module !== 'undefined') {
  module.exports = myutil;
}

return myutil;

})();
<|endoftext|>


<commit_before>'use babel'
/* global atom */

import { EventEmitter } from 'events'
import fs from 'fs'
import gitIgnoreParser from 'git-ignore-parser';
import _ from 'underscore-plus'
import minimatch from 'minimatch'
import { Directory, File } from 'atom'
import { execPromise } from './utils'

export default class PathsCache extends EventEmitter {
  constructor () {
    super()

    const rebuildCacheBound = this.rebuildCache.bind(this)
    this._projectChangeWatcher = atom.project.onDidChangePaths(rebuildCacheBound)

    const _onDidChangeFilesBound = this._onDidChangeFiles.bind(this)
    this._projectWatcher = atom.project.onDidChangeFiles(_onDidChangeFilesBound)

    this._repositories = []
    this._filePathsByProjectDirectory = new Map()
    this._filePathsByDirectory = new Map()
    this._fileWatchersByDirectory = new Map()

    this.updateConfig()
  }

  updateConfig() {
    this.config = {
      excludeVcsIgnoredPaths: atom.config.get('core.excludeVcsIgnoredPaths'),
      ignoreSubmodules: atom.config.get('autocomplete-paths.ignoreSubmodules'),
      shouldIgnoredNames: atom.config.get('autocomplete-paths.ignoredNames'),
      ignoredNames: atom.config.get('core.ignoredNames'),
      ignoredPatterns: atom.config.get('autocomplete-paths.ignoredPatterns'),
      maxFileCount: atom.config.get('autocomplete-paths.maxFileCount')
    }
  }

  /**
   * Checks if the given path is ignored
   * @param  {String}  path
   * @return {Boolean}
   * @private
   */
  _isPathIgnored (path) {
    let ignored = false
    if (this.config.excludeVcsIgnoredPaths) {
      this._repositories.forEach(repository => {
        if (ignored) return
        const isIgnoredSubmodule = this.config.ignoreSubmodules && repository.isSubmodule(path)
        if (repository.isPathIgnored(path) || isIgnoredSubmodule) {
          ignored = true
        }
      })
    }

    if (this.config.shouldIgnoredNames) {
      this.config.ignoredNames.forEach(ignoredName => {
        if (ignored) return
        ignored = ignored || minimatch(path, ignoredName, { matchBase: true, dot: true })
      })
    }

    if (this.config.ignoredPatterns) {
      this.config.ignoredPatterns.forEach(ignoredPattern => {
        if (ignored) return
        ignored = ignored || minimatch(path, ignoredPattern, { dot: true })
      })
    }

    return ignored
  }

  /**
   * Caches the project paths and repositories
   * @return {Promise}
   * @private
   */
  async _cacheProjectPathsAndRepositories () {
    this._projectDirectories = atom.project.getDirectories()

    // get the repositories asynchronously
    const projectNum = this._projectDirectories.length
    let repositoriesP = new Array(projectNum)
    for (let i = 0; i < projectNum; i++) {
      repositoriesP[i] = atom.project.repositoryForDirectory(this._projectDirectories[i])
    }
    this._repositories = await Promise.all(repositoriesP)
  }

  /**
   * Invoked when the content of the given `directory` has changed
   * @param  {Directory} projectDirectory
   * @param  {Directory} directory
   * @private
   */
  _onDirectoryChanged (projectDirectory, directory) {
    this._removeFilePathsForDirectory(projectDirectory, directory)
    this._cleanWatchersForDirectory(directory)
    this._cacheDirectoryFilePaths(projectDirectory, directory)
  }

  /**
   * Removes all watchers inside the given directory
   * @param  {Directory} directory
   * @private
   */
  _cleanWatchersForDirectory (directory) {
    this._fileWatchersByDirectory.forEach((watcher, otherDirectory) => {
      if (directory.contains(otherDirectory.path)) {
        watcher.dispose()
        this._fileWatchersByDirectory.delete(otherDirectory)
      }
    })
  }

  /**
   * Removes all cached file paths in the given directory
   * @param  {Directory} projectDirectory
   * @param  {Directory} directory
   * @private
   */
  _removeFilePathsForDirectory (projectDirectory, directory) {
    let filePaths = this._filePathsByProjectDirectory.get(projectDirectory.path)
    if (!filePaths) return

    filePaths = filePaths.filter(path => !directory.contains(path))
    this._filePathsByProjectDirectory.set(projectDirectory.path, filePaths)

    this._filePathsByDirectory.delete(directory.path)
  }

  /**
   * Caches file paths for the given directory
   * @param  {Directory} projectDirectory
   * @param  {Directory} directory
   * @return {Promise}
   * @private
   */
  async _cacheDirectoryFilePaths (projectDirectory, directory) {
    if (this._cancelled) return []

    if (process.platform !== 'win32') {
      let watcher = this._fileWatchersByDirectory.get(directory)
      if (!watcher) {
        watcher = directory.onDidChange(() =>
          this._onDirectoryChanged(projectDirectory, directory)
        )
        this._fileWatchersByDirectory.set(directory, watcher)
      }
    }
    const entries = await this._getDirectoryEntries(directory)
    if (this._cancelled) return []

    // Filter: Files that are not ignored
    let filePaths = []
    for (let i = 0, len = entries.length; i < len; i++) {
      const entry = entries[i]
      if (entry instanceof File && !this._isPathIgnored(entry.path)) {
        filePaths.push(entry.path)
      }
    }

    // Merge file paths into existing array (which contains *all* file paths)
    let filePathsArray = this._filePathsByProjectDirectory.get(projectDirectory.path) || []
    const newPathsCount = filePathsArray.length + filePaths.length

    if (newPathsCount > this.config.maxFileCount && !this._cancelled) {
      atom.notifications.addError('autocomplete-paths', {
        description: `Maximum file count of ${this.config.maxFileCount} has been exceeded. Path autocompletion will not work in this project.

Click here to learn more.`,
        dismissable: true
      })

      this._filePathsByProjectDirectory.clear()
      this._filePathsByDirectory.clear()
      this._cancelled = true
      this.emit('rebuild-cache-done')
      return
    }

    this._filePathsByProjectDirectory.set(projectDirectory.path,
      _.union(filePathsArray, filePaths)
    )

    // Merge file paths into existing array (which contains file paths for a specific directory)
    filePathsArray = this._filePathsByDirectory.get(directory.path) || []
    this._filePathsByDirectory.set(directory.path,
      _.union(filePathsArray, filePaths)
    )

    let directories = []
    for (let i = 0, len = entries.length; i < len; i++) {
      const entry = entries[i]
      if (entry instanceof Directory && !this._isPathIgnored(entry.path)) {
        directories.push(entry)
      }
    }

    return Promise.all(directories.map(directory =>
      this._cacheDirectoryFilePaths(projectDirectory, directory)
    ))
  }

  /**
   * Promisified version of Directory#getEntries
   * @param  {Directory} directory
   * @return {Promise}
   * @private
   */
  _getDirectoryEntries (directory) {
    return new Promise((resolve, reject) => {
      directory.getEntries((err, entries) => {
        if (err) return reject(err)
        resolve(entries)
      })
    })
  }

  /**
   * Rebuilds the paths cache
   */
  rebuildCache () {
    this.dispose()

    this._cancelled = false
    this.emit('rebuild-cache')

    return execPromise('find --version')
      .then(
        // `find` is available
        () => this._buildInitialCacheWithFind(),
        // `find` is not available
        () => this._buildInitialCache()
      )
  }

  /**
   * Builds the initial file cache
   * @return {Promise}
   * @private
   */
  async _buildInitialCache () {
    await this._cacheProjectPathsAndRepositories()
    const result = await Promise.all(
      this._projectDirectories.map(projectDirectory => {
        return this._cacheDirectoryFilePaths(projectDirectory, projectDirectory)
      })
    )
    this.emit('rebuild-cache-done')
    return result
  }

  /**
   * Returns the file paths for the given project directory with the given (optional) relative path
   * @param  {Directory} projectDirectory
   * @param  {String} [relativeToPath=null]
   * @return {String[]}
   */
  getFilePathsForProjectDirectory (projectDirectory, relativeToPath = null) {
    let filePaths = this._filePathsByProjectDirectory.get(projectDirectory.path) || []
    if (relativeToPath) {
      return filePaths.filter(filePath => filePath.indexOf(relativeToPath) === 0)
    }
    return filePaths
  }

  /**
   * Disposes this PathsCache
   */
  dispose(isPackageDispose) {
    this._fileWatchersByDirectory.forEach((watcher) => {
      watcher.dispose()
    })
    this._fileWatchersByDirectory = new Map()
    this._filePathsByProjectDirectory = new Map()
    this._filePathsByDirectory = new Map()
    this._repositories = []
    if (this._projectWatcher) {
      this._projectWatcher.dispose()
      this._projectWatcher = null
    }
    if (isPackageDispose && this._projectChangeWatcher) {
      this._projectChangeWatcher.dispose()
      this._projectChangeWatcher = null
    }
  }

  //
  // Cache with `find`
  //

  /**
   * Builds the initial file cache with `find`
   * @return {Promise}
   * @private
   */
  async _buildInitialCacheWithFind() {
    await this._cacheProjectPathsAndRepositories()
    const result = await Promise.all(
      this._projectDirectories.map(this._populateCacheFor.bind(this))
    );

    this.emit('rebuild-cache-done');
    return result;
  }

  _onDidChangeFiles(events) {
    events
      .filter(event => event.action !== 'modified')
      .forEach(event => {
        if (!this._projectDirectories) {
          return;
        }

        const { action, path, oldPath } = event;

        const projectDirectory = this._projectDirectories
          .find(projectDirectory => path.indexOf(projectDirectory.path) === 0);

        if (!projectDirectory) {
          return;
        }
        const directoryPath = projectDirectory.path;
        const ignored = this._isPathIgnored(path);

        if (ignored) {
          return;
        }

        const files = this._filePathsByProjectDirectory.get(directoryPath) || [];

        switch (action) {
          case 'created':
            files.push(path);
            break;

          case 'deleted':
            const i = files.indexOf(path);
            if (i > -1) {
              files.splice(i, 1);
            }
            break;

          case 'renamed':
            const j = files.indexOf(oldPath);
            if (j > -1) {
              files[j] = path;
            }
            break;
        }

        if (!this._filePathsByProjectDirectory.has(directoryPath)) {
          this._filePathsByProjectDirectory.set(directoryPath, files);
        }
      });
  }

  /**
   * Returns a list of ignore patterns for a directory
   * @param  {String} directoryPath
   * @return {String[]}
   * @private
   */
  _getIgnorePatterns(directoryPath) {
    const patterns = [];

    if (this.config.shouldIgnoredNames) {
      this.config.ignoredNames.forEach(pattern => patterns.push(pattern));
    }

    if (this.config.excludeVcsIgnoredPaths) {
      try {
        const gitIgnore = fs.readFileSync(directoryPath + '/.gitignore', 'utf-8');
        gitIgnoreParser(gitIgnore).forEach(pattern => patterns.push(pattern));
      }
      catch(err) {
        // .gitignore does not exist for this directory, ignoring
      }
    }

    if (this.config.ignoredPatterns) {
      this.config.ignoredPatterns.forEach(pattern => patterns.push(pattern));
    }

    return patterns;
  }

  /**
   * Populates cache for a project directory
   * @param  {Directory} projectDirectory
   * @return {Promise}
   * @private
   */
  async _populateCacheFor(projectDirectory) {
    const directoryPath = projectDirectory.path;

    const ignorePatterns = this._getIgnorePatterns(directoryPath);
    const ignorePatternsForFind = ignorePatterns.map(
      pattern => pattern
        .replace(/\./g, '\\.')
        .replace(/\*/g, '.*')
    );
    const ignorePattern = '\'.*\\(' + ignorePatternsForFind.join('\\|') + '\\).*\'';

    const cmd = [
      'find',
      '-L',
      directoryPath + '/',
      '-type', 'f',
      '-not', '-regex', ignorePattern,
    ].join(' ');

    const stdout = await execPromise(cmd, {
      maxBuffer: 1024 * 1024,
    })
    const files = _.compact(stdout.split('\n'));

    this._filePathsByProjectDirectory.set(directoryPath, files);

    return files;
  }
}

/**
 * Returns the project path for the given file / directory pathName
 * @param {Array<string>} projects the paths of the projects (this._projectPaths)
 * @param  {String} pathName
 * @return {String}
 * @private
 */
function getProjectPathForPath (projects, pathName) {
  for (let i = 0; i < projects.length; i++) {
    const projectPath = projects[i]
    if (pathName.indexOf(projectPath) === 0) {
      return projectPath
    }
  }
  return false
}
<commit_msg>fix: _cacheDirectoryFilePaths: fuse filtering loops<commit_after>'use babel'
/* global atom */

import { EventEmitter } from 'events'
import fs from 'fs'
import gitIgnoreParser from 'git-ignore-parser';
import _ from 'underscore-plus'
import minimatch from 'minimatch'
import { Directory, File } from 'atom'
import { execPromise } from './utils'

export default class PathsCache extends EventEmitter {
  constructor () {
    super()

    const rebuildCacheBound = this.rebuildCache.bind(this)
    this._projectChangeWatcher = atom.project.onDidChangePaths(rebuildCacheBound)

    const _onDidChangeFilesBound = this._onDidChangeFiles.bind(this)
    this._projectWatcher = atom.project.onDidChangeFiles(_onDidChangeFilesBound)

    this._repositories = []
    this._filePathsByProjectDirectory = new Map()
    this._filePathsByDirectory = new Map()
    this._fileWatchersByDirectory = new Map()

    this.updateConfig()
  }

  updateConfig() {
    this.config = {
      excludeVcsIgnoredPaths: atom.config.get('core.excludeVcsIgnoredPaths'),
      ignoreSubmodules: atom.config.get('autocomplete-paths.ignoreSubmodules'),
      shouldIgnoredNames: atom.config.get('autocomplete-paths.ignoredNames'),
      ignoredNames: atom.config.get('core.ignoredNames'),
      ignoredPatterns: atom.config.get('autocomplete-paths.ignoredPatterns'),
      maxFileCount: atom.config.get('autocomplete-paths.maxFileCount')
    }
  }

  /**
   * Checks if the given path is ignored
   * @param  {String}  path
   * @return {Boolean}
   * @private
   */
  _isPathIgnored (path) {
    let ignored = false
    if (this.config.excludeVcsIgnoredPaths) {
      this._repositories.forEach(repository => {
        if (ignored) return
        const isIgnoredSubmodule = this.config.ignoreSubmodules && repository.isSubmodule(path)
        if (repository.isPathIgnored(path) || isIgnoredSubmodule) {
          ignored = true
        }
      })
    }

    if (this.config.shouldIgnoredNames) {
      this.config.ignoredNames.forEach(ignoredName => {
        if (ignored) return
        ignored = ignored || minimatch(path, ignoredName, { matchBase: true, dot: true })
      })
    }

    if (this.config.ignoredPatterns) {
      this.config.ignoredPatterns.forEach(ignoredPattern => {
        if (ignored) return
        ignored = ignored || minimatch(path, ignoredPattern, { dot: true })
      })
    }

    return ignored
  }

  /**
   * Caches the project paths and repositories
   * @return {Promise}
   * @private
   */
  async _cacheProjectPathsAndRepositories () {
    this._projectDirectories = atom.project.getDirectories()

    // get the repositories asynchronously
    const projectNum = this._projectDirectories.length
    let repositoriesP = new Array(projectNum)
    for (let i = 0; i < projectNum; i++) {
      repositoriesP[i] = atom.project.repositoryForDirectory(this._projectDirectories[i])
    }
    this._repositories = await Promise.all(repositoriesP)
  }

  /**
   * Invoked when the content of the given `directory` has changed
   * @param  {Directory} projectDirectory
   * @param  {Directory} directory
   * @private
   */
  _onDirectoryChanged (projectDirectory, directory) {
    this._removeFilePathsForDirectory(projectDirectory, directory)
    this._cleanWatchersForDirectory(directory)
    this._cacheDirectoryFilePaths(projectDirectory, directory)
  }

  /**
   * Removes all watchers inside the given directory
   * @param  {Directory} directory
   * @private
   */
  _cleanWatchersForDirectory (directory) {
    this._fileWatchersByDirectory.forEach((watcher, otherDirectory) => {
      if (directory.contains(otherDirectory.path)) {
        watcher.dispose()
        this._fileWatchersByDirectory.delete(otherDirectory)
      }
    })
  }

  /**
   * Removes all cached file paths in the given directory
   * @param  {Directory} projectDirectory
   * @param  {Directory} directory
   * @private
   */
  _removeFilePathsForDirectory (projectDirectory, directory) {
    let filePaths = this._filePathsByProjectDirectory.get(projectDirectory.path)
    if (!filePaths) return

    filePaths = filePaths.filter(path => !directory.contains(path))
    this._filePathsByProjectDirectory.set(projectDirectory.path, filePaths)

    this._filePathsByDirectory.delete(directory.path)
  }

  /**
   * Caches file paths for the given directory
   * @param  {Directory} projectDirectory
   * @param  {Directory} directory
   * @return {Promise}
   * @private
   */
  async _cacheDirectoryFilePaths (projectDirectory, directory) {
    if (this._cancelled) return []

    if (process.platform !== 'win32') {
      let watcher = this._fileWatchersByDirectory.get(directory)
      if (!watcher) {
        watcher = directory.onDidChange(() =>
          this._onDirectoryChanged(projectDirectory, directory)
        )
        this._fileWatchersByDirectory.set(directory, watcher)
      }
    }
    const entries = await this._getDirectoryEntries(directory)
    if (this._cancelled) return []

    // Filter: Files and Directories that are not ignored
    let filePaths = []
    let directories = []
    for (let i = 0, len = entries.length; i < len; i++) {
      const entry = entries[i]
      if (entry instanceof File && !this._isPathIgnored(entry.path)) {
        filePaths.push(entry.path)
      } else if (entry instanceof Directory && !this._isPathIgnored(entry.path)) {
        directories.push(entry)
      }
    }

    // Merge file paths into existing array (which contains *all* file paths)
    let filePathsArray = this._filePathsByProjectDirectory.get(projectDirectory.path) || []
    const newPathsCount = filePathsArray.length + filePaths.length

    if (newPathsCount > this.config.maxFileCount && !this._cancelled) {
      atom.notifications.addError('autocomplete-paths', {
        description: `Maximum file count of ${this.config.maxFileCount} has been exceeded. Path autocompletion will not work in this project.

Click here to learn more.`,
        dismissable: true
      })

      this._filePathsByProjectDirectory.clear()
      this._filePathsByDirectory.clear()
      this._cancelled = true
      this.emit('rebuild-cache-done')
      return
    }

    this._filePathsByProjectDirectory.set(projectDirectory.path,
      _.union(filePathsArray, filePaths)
    )

    // Merge file paths into existing array (which contains file paths for a specific directory)
    filePathsArray = this._filePathsByDirectory.get(directory.path) || []
    this._filePathsByDirectory.set(directory.path,
      _.union(filePathsArray, filePaths)
    )

    return Promise.all(directories.map(directory =>
      this._cacheDirectoryFilePaths(projectDirectory, directory)
    ))
  }

  /**
   * Promisified version of Directory#getEntries
   * @param  {Directory} directory
   * @return {Promise}
   * @private
   */
  _getDirectoryEntries (directory) {
    return new Promise((resolve, reject) => {
      directory.getEntries((err, entries) => {
        if (err) return reject(err)
        resolve(entries)
      })
    })
  }

  /**
   * Rebuilds the paths cache
   */
  rebuildCache () {
    this.dispose()

    this._cancelled = false
    this.emit('rebuild-cache')

    return execPromise('find --version')
      .then(
        // `find` is available
        () => this._buildInitialCacheWithFind(),
        // `find` is not available
        () => this._buildInitialCache()
      )
  }

  /**
   * Builds the initial file cache
   * @return {Promise}
   * @private
   */
  async _buildInitialCache () {
    await this._cacheProjectPathsAndRepositories()
    const result = await Promise.all(
      this._projectDirectories.map(projectDirectory => {
        return this._cacheDirectoryFilePaths(projectDirectory, projectDirectory)
      })
    )
    this.emit('rebuild-cache-done')
    return result
  }

  /**
   * Returns the file paths for the given project directory with the given (optional) relative path
   * @param  {Directory} projectDirectory
   * @param  {String} [relativeToPath=null]
   * @return {String[]}
   */
  getFilePathsForProjectDirectory (projectDirectory, relativeToPath = null) {
    let filePaths = this._filePathsByProjectDirectory.get(projectDirectory.path) || []
    if (relativeToPath) {
      return filePaths.filter(filePath => filePath.indexOf(relativeToPath) === 0)
    }
    return filePaths
  }

  /**
   * Disposes this PathsCache
   */
  dispose(isPackageDispose) {
    this._fileWatchersByDirectory.forEach((watcher) => {
      watcher.dispose()
    })
    this._fileWatchersByDirectory = new Map()
    this._filePathsByProjectDirectory = new Map()
    this._filePathsByDirectory = new Map()
    this._repositories = []
    if (this._projectWatcher) {
      this._projectWatcher.dispose()
      this._projectWatcher = null
    }
    if (isPackageDispose && this._projectChangeWatcher) {
      this._projectChangeWatcher.dispose()
      this._projectChangeWatcher = null
    }
  }

  //
  // Cache with `find`
  //

  /**
   * Builds the initial file cache with `find`
   * @return {Promise}
   * @private
   */
  async _buildInitialCacheWithFind() {
    await this._cacheProjectPathsAndRepositories()
    const result = await Promise.all(
      this._projectDirectories.map(this._populateCacheFor.bind(this))
    );

    this.emit('rebuild-cache-done');
    return result;
  }

  _onDidChangeFiles(events) {
    events
      .filter(event => event.action !== 'modified')
      .forEach(event => {
        if (!this._projectDirectories) {
          return;
        }

        const { action, path, oldPath } = event;

        const projectDirectory = this._projectDirectories
          .find(projectDirectory => path.indexOf(projectDirectory.path) === 0);

        if (!projectDirectory) {
          return;
        }
        const directoryPath = projectDirectory.path;
        const ignored = this._isPathIgnored(path);

        if (ignored) {
          return;
        }

        const files = this._filePathsByProjectDirectory.get(directoryPath) || [];

        switch (action) {
          case 'created':
            files.push(path);
            break;

          case 'deleted':
            const i = files.indexOf(path);
            if (i > -1) {
              files.splice(i, 1);
            }
            break;

          case 'renamed':
            const j = files.indexOf(oldPath);
            if (j > -1) {
              files[j] = path;
            }
            break;
        }

        if (!this._filePathsByProjectDirectory.has(directoryPath)) {
          this._filePathsByProjectDirectory.set(directoryPath, files);
        }
      });
  }

  /**
   * Returns a list of ignore patterns for a directory
   * @param  {String} directoryPath
   * @return {String[]}
   * @private
   */
  _getIgnorePatterns(directoryPath) {
    const patterns = [];

    if (this.config.shouldIgnoredNames) {
      this.config.ignoredNames.forEach(pattern => patterns.push(pattern));
    }

    if (this.config.excludeVcsIgnoredPaths) {
      try {
        const gitIgnore = fs.readFileSync(directoryPath + '/.gitignore', 'utf-8');
        gitIgnoreParser(gitIgnore).forEach(pattern => patterns.push(pattern));
      }
      catch(err) {
        // .gitignore does not exist for this directory, ignoring
      }
    }

    if (this.config.ignoredPatterns) {
      this.config.ignoredPatterns.forEach(pattern => patterns.push(pattern));
    }

    return patterns;
  }

  /**
   * Populates cache for a project directory
   * @param  {Directory} projectDirectory
   * @return {Promise}
   * @private
   */
  async _populateCacheFor(projectDirectory) {
    const directoryPath = projectDirectory.path;

    const ignorePatterns = this._getIgnorePatterns(directoryPath);
    const ignorePatternsForFind = ignorePatterns.map(
      pattern => pattern
        .replace(/\./g, '\\.')
        .replace(/\*/g, '.*')
    );
    const ignorePattern = '\'.*\\(' + ignorePatternsForFind.join('\\|') + '\\).*\'';

    const cmd = [
      'find',
      '-L',
      directoryPath + '/',
      '-type', 'f',
      '-not', '-regex', ignorePattern,
    ].join(' ');

    const stdout = await execPromise(cmd, {
      maxBuffer: 1024 * 1024,
    })
    const files = _.compact(stdout.split('\n'));

    this._filePathsByProjectDirectory.set(directoryPath, files);

    return files;
  }
}

/**
 * Returns the project path for the given file / directory pathName
 * @param {Array<string>} projects the paths of the projects (this._projectPaths)
 * @param  {String} pathName
 * @return {String}
 * @private
 */
function getProjectPathForPath (projects, pathName) {
  for (let i = 0; i < projects.length; i++) {
    const projectPath = projects[i]
    if (pathName.indexOf(projectPath) === 0) {
      return projectPath
    }
  }
  return false
}
<|endoftext|>


<commit_before>// Copyright 2012 Ofir Picazo. All Rights Reserved.

/**
 * @fileoverview Playing card object.
 *
 * @author ofirpicazo@gmail.com (Ofir Picazo)
 */

goog.provide('solitario.game.Card');

goog.require('goog.dom.dataset');
goog.require('goog.style');

/**
 * Class to represent a card in the game.
 *
 * @param {!Element} el DOM element with the card contents.
 * @constructor
 */
solitario.game.Card = function(el) {
  /**
   * DOM element with the card contents.
   * @type {Element}
   * @private
   */
  this.element_ = el;

  /**
   * Unique identifier for this card.
   * @type {string}
   */
  this.id = this.element_.id;

  /**
   * Number of this card, includes also non-digits (A, J, Q, K).
   * @type {string}
   */
  this.number = goog.dom.dataset.get(
      this.element_, solitario.game.Card.DataAttrs_.NUMBER);

  /**
   * Suit this card belongs to (club, diamond, heart, spade).
   * @type {string}
   */
  this.suit = goog.dom.dataset.get(
      this.element_, solitario.game.Card.DataAttrs_.SUIT);

  /**
   * Color of the card depending on the chosen suit.
   * Values are 'red' or 'black'.
   * @type {string}
   */
  this.color = (this.suit === 'heart' ||
                this.suit === 'diamond') ? 'red' : 'black';
};

/**
 * Card data attributes.
 * @enum {string}
 * @const
 * @private
 */
solitario.game.Card.DataAttrs_ = {
  NUMBER: 'number',
  SUIT: 'suit'
};

/**
 * Obtains the current z-index of this card.
 *
 * @return {Number} The z-index as an integer.
 */
solitario.game.Card.prototype.getZIndex = function() {
  return parseInt(this.element_.style.zIndex);
};

/**
 * Modifies the z-index of this card.
 *
 * @param {Number} zIndex The z-index as an integer.
 */
solitario.game.Card.prototype.setZIndex = function(zIndex) {
  this.element_.style.zIndex = zIndex + '';  // explicit string cast.
};

solitario.game.Card.prototype.isDraggable = function() {

};

solitario.game.Card.prototype.isDroppable = function() {

};

solitario.game.Card.prototype.isRevealed = function() {

};

/**
 * Sets the absolute position of the card in the viewport.
 *
 * @param {goog.math.Coordinate} position Absolute position to set the card to.
 */
solitario.game.Card.prototype.setPosition = function(position) {
  goog.style.setPosition(this.element_, position);
};
<commit_msg>Adds function to set the position of a card.<commit_after>// Copyright 2012 Ofir Picazo. All Rights Reserved.

/**
 * @fileoverview Playing card object.
 *
 * @author ofirpicazo@gmail.com (Ofir Picazo)
 */

goog.provide('solitario.game.Card');

goog.require('goog.dom.dataset');
goog.require('goog.style');

/**
 * Class to represent a card in the game.
 *
 * @param {!Element} el DOM element with the card contents.
 * @constructor
 */
solitario.game.Card = function(el) {
  /**
   * DOM element with the card contents.
   * @type {Element}
   * @private
   */
  this.element_ = el;

  /**
   * Unique identifier for this card.
   * @type {string}
   */
  this.id = this.element_.id;

  /**
   * Number of this card, includes also non-digits (A, J, Q, K).
   * @type {string}
   */
  this.number = goog.dom.dataset.get(
      this.element_, solitario.game.Card.DataAttrs_.NUMBER);

  /**
   * Suit this card belongs to (club, diamond, heart, spade).
   * @type {string}
   */
  this.suit = goog.dom.dataset.get(
      this.element_, solitario.game.Card.DataAttrs_.SUIT);

  /**
   * Color of the card depending on the chosen suit.
   * Values are 'red' or 'black'.
   * @type {string}
   */
  this.color = (this.suit === 'heart' ||
                this.suit === 'diamond') ? 'red' : 'black';
};

/**
 * Card data attributes.
 * @enum {string}
 * @const
 * @private
 */
solitario.game.Card.DataAttrs_ = {
  NUMBER: 'number',
  SUIT: 'suit'
};

/**
 * Obtains the current z-index of this card.
 *
 * @return {Number} The z-index as an integer.
 */
solitario.game.Card.prototype.getZIndex = function() {
  return parseInt(this.element_.style.zIndex);
};

/**
 * Modifies the z-index of this card.
 *
 * @param {Number} zIndex The z-index as an integer.
 */
solitario.game.Card.prototype.setZIndex = function(zIndex) {
  this.element_.style.zIndex = zIndex + '';  // explicit string cast.
};

solitario.game.Card.prototype.isDraggable = function() {

};

solitario.game.Card.prototype.isDroppable = function() {

};

solitario.game.Card.prototype.isRevealed = function() {

};

/**
 * Sets the absolute position of the card in the viewport, in ems.
 *
 * @param {goog.math.Coordinate} position Absolute position to set the card to.
 */
solitario.game.Card.prototype.setPosition = function(position) {
  var relativeFontSize = parseFloat(goog.dom.getDocument().body.style.fontSize);
  var left = (position.x / relativeFontSize).toFixed(0) + 'em';
  var top = (position.y / relativeFontSize).toFixed(0) + 'em';
  goog.style.setPosition(this.element_, left, top);
};
<|endoftext|>


<commit_before>var lib = require('./lib');

describe('integration', function() {
  it('should output a single dir', lib.runTest('test/artifacts/single-directory.json', 'test/expected/js-dir'));
  it('should output multiple files', lib.runTest('test/artifacts/multiple-files.json', 'test/expected/js-dir'));
  it('should output files in multiple modules', lib.runTest('test/artifacts/file-modules.json', 'test/expected/file-modules'));
  it('should handle multiple platforms', lib.runTest('test/artifacts/multiple-platforms.json', 'test/expected/multiple-platforms'));
  it('should handle multiple packages', lib.runTest('test/artifacts/multiple-packages.json', 'test/expected/multiple-packages'));

  it('should output module-map', lib.runTest('test/artifacts/module-map.json', 'test/expected/module-map'));
  it('should output template', lib.runTest('test/artifacts/template.json', 'test/expected/template'));

  // TODO : Error handling for Missing template cache definitions
  it('should output dev-config', lib.runTest('test/artifacts/package-config.json', 'test/expected/dev-config', {packageConfigFile: 'config/dev.json'}));
  it('should output production-config', lib.runTest('test/artifacts/package-config.json', 'test/expected/production-config', {packageConfigFile: 'config/production.json'}));

  it('should output application-namespace', lib.runTest('test/artifacts/application-namespace.json', 'test/expected/application-namespace', {packageConfigFile: 'config/dev.json'}));

  it('should output styles', lib.runTest('test/artifacts/styles.json', 'test/expected/styles'));
  it('should output stylus', lib.runTest('test/artifacts/stylus.json', 'test/expected/stylus'));
  it('should output inline-styles', lib.runTest('test/artifacts/inline-styles.json', 'test/expected/inline-styles'));

  it('should output trailing-slash', lib.runTest('test/artifacts/trailing-slash.json', 'test/expected/trailing-slash'));

  it('should output static', lib.runTest('test/artifacts/static.json', 'test/expected/static', undefined, '/**/*.*'));
  it('should output index-update', lib.runTest('test/artifacts/index-update.json', 'test/expected/index-update', undefined, '/**/*.{js,css,html}'));
  it('should output generated-load-prefix', lib.runTest('test/artifacts/generated-load-prefix.json', 'test/expected/generated-load-prefix', undefined, '/**/*.{js,css,html}'));
  it('should output json-plugins', lib.runTest('test/artifacts/json-plugins.json', 'test/expected/json-plugin'));
  // TODO : Test file not found and other cases
});
<commit_msg>Reorg core tests<commit_after>var lib = require('./lib');

describe('file output', function() {
  it('should output a single dir', lib.runTest('test/artifacts/single-directory.json', 'test/expected/js-dir'));
  it('should output multiple files', lib.runTest('test/artifacts/multiple-files.json', 'test/expected/js-dir'));
  it('should output files in multiple modules', lib.runTest('test/artifacts/file-modules.json', 'test/expected/file-modules'));
  it('should handle multiple platforms', lib.runTest('test/artifacts/multiple-platforms.json', 'test/expected/multiple-platforms'));
  it('should handle multiple packages', lib.runTest('test/artifacts/multiple-packages.json', 'test/expected/multiple-packages'));

  it('should output styles', lib.runTest('test/artifacts/styles.json', 'test/expected/styles'));
  it('should output static', lib.runTest('test/artifacts/static.json', 'test/expected/static', undefined, '/**/*.*'));
});

describe('integration', function() {
  it('should output stylus', lib.runTest('test/artifacts/stylus.json', 'test/expected/stylus'));
  it('should output inline-styles', lib.runTest('test/artifacts/inline-styles.json', 'test/expected/inline-styles'));
  it('should output module-map', lib.runTest('test/artifacts/module-map.json', 'test/expected/module-map'));
  it('should output template', lib.runTest('test/artifacts/template.json', 'test/expected/template'));

  // TODO : Error handling for Missing template cache definitions
  it('should output dev-config', lib.runTest('test/artifacts/package-config.json', 'test/expected/dev-config', {packageConfigFile: 'config/dev.json'}));
  it('should output production-config', lib.runTest('test/artifacts/package-config.json', 'test/expected/production-config', {packageConfigFile: 'config/production.json'}));

  it('should output application-namespace', lib.runTest('test/artifacts/application-namespace.json', 'test/expected/application-namespace', {packageConfigFile: 'config/dev.json'}));

  it('should output trailing-slash', lib.runTest('test/artifacts/trailing-slash.json', 'test/expected/trailing-slash'));

  it('should output index-update', lib.runTest('test/artifacts/index-update.json', 'test/expected/index-update', undefined, '/**/*.{js,css,html}'));
  it('should output generated-load-prefix', lib.runTest('test/artifacts/generated-load-prefix.json', 'test/expected/generated-load-prefix', undefined, '/**/*.{js,css,html}'));
  it('should output json-plugins', lib.runTest('test/artifacts/json-plugins.json', 'test/expected/json-plugin'));
  // TODO : Test file not found and other cases
});
<|endoftext|>


<commit_before>var $ = require('jquery');
var models = require('./models.js');

//---------------------------------------------------------------------
var StorageService = function(options) {
  options = options || {};
  this.siteContext = options.siteContext || '';
  this.sources = options.sources || [];
};
StorageService.prototype.load = function() {
  var deferred = $.Deferred();
  this._load(deferred);
  return deferred;
};
StorageService.prototype.save = function(obj) {
  var deferred = $.Deferred();
  var serialized = this._serialize(obj);
  this._save(deferred, serialized);
  return deferred;
};
StorageService.prototype.getDataKey = function() {
  var sourceHashes = this.sources.map(function(source) {
    return source.hash;
  });
  sourceHashes.sort(function(a, b) {
    return (a == b ? 0 : (a < b ? -1 : 1));
  });
  return this.siteContext.id + "::" + sourceHashes.join(",");
};
StorageService.prototype.getSourceMap = function() {
  var dict = {};
  this.sources.forEach(function(source) {
    dict[source.hash] = source;
  });
  return dict;
};
StorageService.prototype._load = function() {
  throw "Subclass responsibility";
};
StorageService.prototype._save = function() {
  throw "Subclass responsibility";
};
StorageService.prototype._serialize = function(obj) {
  return obj.serialize();
};
StorageService.prototype._parse = function(jsonData) {
  var sourceMap = this.getSourceMap();
  var result = JSON.parse(jsonData);
  var alignments = result.data.map(function(alignment) {
    var words = alignment.data.filter(function(item) {
      return item.type == 'word';
    }).map(function(word) {
      return models.Word.create({
        index: word.data.index,
        value: word.data.value,
        source: sourceMap[word.data.source]
      });
    });
    var comment_texts = alignment.data.filter(function(item) {
      return item.type == 'comment';
    }).map(function(comment) {
      return comment.data.text;
    });
    var alignment_obj = models.Alignments.createAlignment(words);
    if (comment_texts.length > 0) {
      alignment_obj.setComment(comment_texts[0]);
    }
    return alignment_obj;
  });
  return alignments;
};

//---------------------------------------------------------------------
var LocalStorageService = function() {
  if (window.Storage === undefined) {
    throw "LocalStorage not supported in this browser.";
  }
  StorageService.apply(this, arguments);
};
LocalStorageService.prototype = new StorageService();
LocalStorageService.prototype._load = function(deferred) {
  var jsonData = localStorage.getItem(this.getDataKey());
  if (jsonData === null) {
    deferred.resolve([]);
  } else {
    deferred.resolve(this._parse(jsonData));
  }
};
LocalStorageService.prototype._save = function(deferred, serialized) {
  localStorage.setItem(this.getDataKey(), serialized);
  deferred.resolve();
};

//---------------------------------------------------------------------
var SettingsService = {
  // Settings are keyed by domain name
  // If a domain name has no associated settings, use some default/fallback settings.
  _settings: {
    '*': {
      'sourceSelector': 'body'
    },
    'www.graeco-arabic-studies.org': {
      'sourceSelector': '.textboxcontent'
    }
  },
  get: function(siteContext) {
    var settings = false;
    console.log("Loading settings for site ID: ", siteContext.id);
    if (siteContext.id in this._settings) {
      settings = this._settings[siteContext.id];
      console.log("Site ID exists. Settings: ", settings);
    } else {
      settings = this._settings['*'];
      console.log("No such site ID exists. Using default settings: ", settings);
    }

    return settings;
  }
};

var ImportExportService = function(options) {
  this.sources = options.sources || [];
  this.alignments = options.alignments;
  this.siteContext = options.siteContext;
  this.import = this.import.bind(this);
  this.export = this.export.bind(this);
};
ImportExportService.prototype.getSourceMap = function() {
  return this.sources.reduce(function(dict, source) {
    dict[source.hash] = source;
    return dict;
  }, {});
};
ImportExportService.prototype.import = function(jsonData) {
  var sourceMap = this.getSourceMap();
  var retvalue = {"success": true, "message": ""};

  try {
    var result = JSON.parse(jsonData);

    // Preliminary error checking
    if (result.type != "site" || !result.hasOwnProperty("data")) {
      throw "Invalid import data. Top-level object must be of type 'site' with a 'data' attribute.";
    } else if(result.data.type != "alignments" || !result.data.hasOwnProperty("data")) {
      throw "Invalid import data. Object contained by 'site' must be of type 'alignments' with a 'data' attribute.";
    }

    // Attempt to create an array of alignment objects, each of which contains an array of word objects
    var batch = result.data.data.map(function(alignment, alignmentIdx) {
      if (!alignment.hasOwnProperty("data") || !Array.isArray(alignment.data)) {
        throw "Alignment item missing/invalid 'data'  attribute at: " + alignmentIdx;
      }
      var words = alignment.data.filter(function(item) {
        return item.type == "word";
      }).map(function(word, wordIdx) {
        var errpos = ["A", alignmentIdx, "W", wordIdx].join("");
        if (!word.hasOwnProperty("data")) {
          throw "Word item missing/invalid 'data' attribute at: " + errpos;
        }
        if (!sourceMap[word.data.source]) {
          throw "Word item (" + word.data.value + ") does not map to a valid source (" + word.data.source + ") at: " + errpos;
        }
        return models.Word.create({
          index: word.data.index,
          value: word.data.value,
          source: sourceMap[word.data.source]
        });
      });
      var comment_texts = alignment.data.filter(function(item) {
        return item.type == "comment";
      }).map(function(comment) {
        return comment.data.text;
      });
      var alignment_obj = models.Alignments.createAlignment(words);
      if (comment_texts.length > 0) {
        alignment_obj.setComment(comment_texts[0]);
      }
      return alignment_obj;
    });

    // Load the batch of alignment objects
    this.alignments.load(batch);

  } catch(e) {
    retvalue.success = false;
    retvalue.message = "An error occurred with the import. Error: " + e;
  }

  return retvalue;
};
ImportExportService.prototype.export = function(serialize) {
  var result = {
    'type': 'site',
    'id': this.siteContext.id,
    'url': this.siteContext.url,
    'data': this.alignments.toJSON()
  };
  return (serialize ? JSON.stringify(result, null, '\t') : result);
};

module.exports = {
  ImportExportService: ImportExportService,
  LocalStorageService: LocalStorageService,
  SettingsService: SettingsService
};<commit_msg>Added config entry for google sites and canvas.<commit_after>var $ = require('jquery');
var models = require('./models.js');

//---------------------------------------------------------------------
var StorageService = function(options) {
  options = options || {};
  this.siteContext = options.siteContext || '';
  this.sources = options.sources || [];
};
StorageService.prototype.load = function() {
  var deferred = $.Deferred();
  this._load(deferred);
  return deferred;
};
StorageService.prototype.save = function(obj) {
  var deferred = $.Deferred();
  var serialized = this._serialize(obj);
  this._save(deferred, serialized);
  return deferred;
};
StorageService.prototype.getDataKey = function() {
  var sourceHashes = this.sources.map(function(source) {
    return source.hash;
  });
  sourceHashes.sort(function(a, b) {
    return (a == b ? 0 : (a < b ? -1 : 1));
  });
  return this.siteContext.id + "::" + sourceHashes.join(",");
};
StorageService.prototype.getSourceMap = function() {
  var dict = {};
  this.sources.forEach(function(source) {
    dict[source.hash] = source;
  });
  return dict;
};
StorageService.prototype._load = function() {
  throw "Subclass responsibility";
};
StorageService.prototype._save = function() {
  throw "Subclass responsibility";
};
StorageService.prototype._serialize = function(obj) {
  return obj.serialize();
};
StorageService.prototype._parse = function(jsonData) {
  var sourceMap = this.getSourceMap();
  var result = JSON.parse(jsonData);
  var alignments = result.data.map(function(alignment) {
    var words = alignment.data.filter(function(item) {
      return item.type == 'word';
    }).map(function(word) {
      return models.Word.create({
        index: word.data.index,
        value: word.data.value,
        source: sourceMap[word.data.source]
      });
    });
    var comment_texts = alignment.data.filter(function(item) {
      return item.type == 'comment';
    }).map(function(comment) {
      return comment.data.text;
    });
    var alignment_obj = models.Alignments.createAlignment(words);
    if (comment_texts.length > 0) {
      alignment_obj.setComment(comment_texts[0]);
    }
    return alignment_obj;
  });
  return alignments;
};

//---------------------------------------------------------------------
var LocalStorageService = function() {
  if (window.Storage === undefined) {
    throw "LocalStorage not supported in this browser.";
  }
  StorageService.apply(this, arguments);
};
LocalStorageService.prototype = new StorageService();
LocalStorageService.prototype._load = function(deferred) {
  var jsonData = localStorage.getItem(this.getDataKey());
  if (jsonData === null) {
    deferred.resolve([]);
  } else {
    deferred.resolve(this._parse(jsonData));
  }
};
LocalStorageService.prototype._save = function(deferred, serialized) {
  localStorage.setItem(this.getDataKey(), serialized);
  deferred.resolve();
};

//---------------------------------------------------------------------
var SettingsService = {
  // Settings are keyed by domain name
  // If a domain name has no associated settings, use some default/fallback settings.
  _settings: {
    '*': {
      'sourceSelector': 'body'
    },
    'www.graeco-arabic-studies.org': {
      'sourceSelector': '.textboxcontent'
		},
		'sites.google.com': {
      'sourceSelector': '.textboxcontent'
		},
		'canvas.harvard.edu': {
      'sourceSelector': '.textboxcontent'
		}
  },
  get: function(siteContext) {
    var settings = false;
    console.log("Loading settings for site ID: ", siteContext.id);
    if (siteContext.id in this._settings) {
      settings = this._settings[siteContext.id];
      console.log("Site ID exists. Settings: ", settings);
    } else {
      settings = this._settings['*'];
      console.log("No such site ID exists. Using default settings: ", settings);
    }

    return settings;
  }
};

var ImportExportService = function(options) {
  this.sources = options.sources || [];
  this.alignments = options.alignments;
  this.siteContext = options.siteContext;
  this.import = this.import.bind(this);
  this.export = this.export.bind(this);
};
ImportExportService.prototype.getSourceMap = function() {
  return this.sources.reduce(function(dict, source) {
    dict[source.hash] = source;
    return dict;
  }, {});
};
ImportExportService.prototype.import = function(jsonData) {
  var sourceMap = this.getSourceMap();
  var retvalue = {"success": true, "message": ""};

  try {
    var result = JSON.parse(jsonData);

    // Preliminary error checking
    if (result.type != "site" || !result.hasOwnProperty("data")) {
      throw "Invalid import data. Top-level object must be of type 'site' with a 'data' attribute.";
    } else if(result.data.type != "alignments" || !result.data.hasOwnProperty("data")) {
      throw "Invalid import data. Object contained by 'site' must be of type 'alignments' with a 'data' attribute.";
    }

    // Attempt to create an array of alignment objects, each of which contains an array of word objects
    var batch = result.data.data.map(function(alignment, alignmentIdx) {
      if (!alignment.hasOwnProperty("data") || !Array.isArray(alignment.data)) {
        throw "Alignment item missing/invalid 'data'  attribute at: " + alignmentIdx;
      }
      var words = alignment.data.filter(function(item) {
        return item.type == "word";
      }).map(function(word, wordIdx) {
        var errpos = ["A", alignmentIdx, "W", wordIdx].join("");
        if (!word.hasOwnProperty("data")) {
          throw "Word item missing/invalid 'data' attribute at: " + errpos;
        }
        if (!sourceMap[word.data.source]) {
          throw "Word item (" + word.data.value + ") does not map to a valid source (" + word.data.source + ") at: " + errpos;
        }
        return models.Word.create({
          index: word.data.index,
          value: word.data.value,
          source: sourceMap[word.data.source]
        });
      });
      var comment_texts = alignment.data.filter(function(item) {
        return item.type == "comment";
      }).map(function(comment) {
        return comment.data.text;
      });
      var alignment_obj = models.Alignments.createAlignment(words);
      if (comment_texts.length > 0) {
        alignment_obj.setComment(comment_texts[0]);
      }
      return alignment_obj;
    });

    // Load the batch of alignment objects
    this.alignments.load(batch);

  } catch(e) {
    retvalue.success = false;
    retvalue.message = "An error occurred with the import. Error: " + e;
  }

  return retvalue;
};
ImportExportService.prototype.export = function(serialize) {
  var result = {
    'type': 'site',
    'id': this.siteContext.id,
    'url': this.siteContext.url,
    'data': this.alignments.toJSON()
  };
  return (serialize ? JSON.stringify(result, null, '\t') : result);
};

module.exports = {
  ImportExportService: ImportExportService,
  LocalStorageService: LocalStorageService,
  SettingsService: SettingsService
};
<|endoftext|>


<commit_before>//////////////////////////////////////////
// Needle -- Node Simple HTTP Client
// Written by Tomás Pollak <tomas@forkhq.com>
// (c) 2011 - Fork Ltd.
// MIT Licensed
//////////////////////////////////////////

var util = require('util'),
		fs = require('fs'),
		path = require('path'),
		http = require('http'),
		https = require('https'),
		url = require('url'),
		stringify = require('qs').stringify,
		version = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString()).version;

// http.globalAgent.maxSockets = 128;
exports.version = version;
try { var unzip = require('zlib').unzip; } catch(e){ /* zlib not supported */ }

var parsers = {
	'application/json': function(data, callback){
		callback(data && JSON.parse(data));
	}
};

try {
	var xml2js = require('xml2js');
	parsers['application/xml'] = function(data, callback){

		var xml_parser = new xml2js.Parser();

		xml_parser.on('end', callback);
		xml_parser.on('error', function(result) {
			// throw("Error parsing XML!")
			callback(null);
		});

		xml_parser.parseString(data);

	};
} catch(e) { }

// utility function for flattening params in multipart POST's
function flatten(object, into, prefix){
	into = into || {};

	for(key in object){
		var prefix_key = prefix ? prefix + "[" + key + "]" : key;
		var prop = object[key];

		if(prop && typeof prop === "object" && !((prop.buffer || prop.file) && prop.content_type))
			flatten(prop, into, prefix_key)
		else
			into[prefix_key] = prop;
	}

	return into;
}

var Needle = {

	default_boundary: '--------------------NODENEEDLEHTTPCLIENT',

	request: function(uri, method, data, options, callback){

		var self = this;
		var callback = (typeof options == 'function') ? options : callback;
		var options = options || {};
		if(uri.indexOf('http') == -1) uri = 'http://' + uri;

		var remote = options.proxy ? url.parse(options.proxy) : url.parse(uri);
		var is_https = remote.protocol === "https:";
		var port = remote.port || (is_https ? 443 : 80);
		var request_compressed = (options.compressed && typeof unzip != 'undefined') || false;
		var post_data = null;

		var request_opts = {
			encoding: options.multipart ? 'binary' : 'utf8',
			protocol: is_https ? https : http,
			parse_response: options.parse === false ? false : true,
			timeout: options.timeout || 10000 // 10 seconds timeout
		}

		var headers = {
			"Host" : options.proxy ? url.parse(uri).hostname : remote.hostname,
			"User-Agent": "Needle/" + version + " (NodeJS " + process.version + ")",
			"Connection": "close",
			"Accept": "*/*"
		}

		if(request_compressed) headers['Accept-Encoding'] = 'gzip,deflate';

		for(h in options.headers)
			headers[h] = options.headers[h];

		if(options.username && options.password){
			var b = new Buffer([options.username, options.password].join(':'));
			headers['Authorization'] = "Basic " + b.toString('base64');
		}

		var http_opts = {
			host: remote.hostname,
			port: port,
			path: options.proxy ? uri : remote.pathname + (remote.search || ''),
			method: method,
			headers: headers
		}

		if (typeof options.agent != 'undefined') http_opts.agent = options.agent;

		if(data) {
			if(options.multipart){

				var boundary = options.boundary || this.default_boundary;
				return this.build_multipart_body(data, boundary, function(err, body){

					if(err) throw(err);
					headers['Content-Type'] = 'multipart/form-data; boundary=' + boundary;
					headers['Content-Length'] = body.length;
					self.send_request(request_opts, http_opts, body, callback);

				});

			} else {
				post_data = (typeof(data) === "string") ? data : stringify(data);
				headers['Content-Type'] = 'application/x-www-form-urlencoded';
				headers['Content-Length'] = post_data.length;
			}

		}

		this.send_request(request_opts, http_opts, post_data, callback);

	},

	send_request: function(request_opts, http_opts, post_data, callback){

		if(process.env.DEBUG) console.log(http_opts + "\n\n" + post_data);
		var self = this, timer, response_opts = {parse_response: request_opts.parse_response};

		var request = request_opts.protocol.request(http_opts, function(response){
			if(timer) clearTimeout(timer);

			var body = '';
			var compressed = /gzip|deflate/.test(response.headers['content-encoding']);
			response.setEncoding(compressed ? 'binary' : 'utf8');

			response.on('data', function(chunk){
				body += chunk;
			});

			response.on('end', function() {
				if(typeof unzip != 'undefined' && compressed)
					unzip(new Buffer(body, 'binary'), function(err, buff){
						self.response_end(response_opts, response, buff.toString(), callback);
					});
				else
					self.response_end(response_opts, response, body, callback);
			});

		});

		if(request_opts.timeout) {
			timer = setTimeout(function() {
				request.abort();
			}, request_opts.timeout)
		}

		request.on('error', function(err) {
			if(process.env.DEBUG) console.log('Error on request: ' + err.toString());
			if(timer) clearTimeout(timer);
			if(callback) callback(err || new Error("Unkown error on request."));
		});

		if(post_data) request.write(post_data, request_opts.encoding);
		request.end();

	},

	response_end: function(opts, response, body, callback){

		if(process.env.DEBUG) console.log(response.headers);
		if(!callback) return;
		var content_type = response.headers['content-type'] && response.headers['content-type'].split(';')[0];

		if(opts.parse_response && parsers[content_type]) {
			parsers[content_type](body, function(result){
				callback(null, response, result);
			});
		} else {
			callback(null, response, body);
		}

	},

	build_multipart_body: function(data, boundary, callback){

		var body = '';
		var object = flatten(data);
		var count = Object.keys(object).length;

		for(var key in object){

			var value = object[key];
			if(value === null || typeof value == 'undefined') return --count;

			var part = (value.buffer || value.file) && value.content_type ? value : {value: value};

			this.generate_part(key, part, boundary, function(err, section){
				if(err) return callback(err);
				body += section;
				--count || callback(null, body + '--' + boundary + '--');
			});

		}

	},

	generate_part: function(name, part, boundary, callback){

		var return_part = '--' + boundary + "\r\n";
		return_part += "Content-Disposition: form-data; name=\"" + name + "\"";

		var append = function(data, filename){

			if(data){
				return_part += "; filename=\"" + encodeURIComponent(filename) + "\"\r\n";
				return_part += "Content-Type: " + part.content_type + "\r\n\r\n";
				return_part += (part.content_type.indexOf('text') == -1)
					? data.toString('binary')
					: data.toString('utf8');
			}

			callback(null, return_part + '\r\n');
		};

		if((part.file || part.buffer) && part.content_type){

			var filename = part.filename ? part.filename : part.file ? path.basename(part.file) : name;
			if(part.buffer) return append(part.buffer, filename);

			fs.readFile(part.file, function(err, data){

				if(err) return callback(err);
				append(data, filename);

			});

		} else {

			return_part += "\r\n\r\n";
			return_part += part.value;
			append();

		}

	}

}

exports.head = function(uri, options, callback){
	return Needle.request(uri, 'HEAD', null, options, callback);
}

exports.get = function(uri, options, callback){
	return Needle.request(uri, 'GET', null, options, callback);
}

exports.post = function(uri, data, options, callback){
	if(!data) throw('POST request expects data.');
	return Needle.request(uri, 'POST', data, options, callback);
}

exports.put = function(uri, data, options, callback){
	if(!data) throw('PUT request expects data.');
	return Needle.request(uri, 'PUT', data, options, callback);
}

exports.delete = function(uri, data, options, callback){
	return Needle.request(uri, 'DELETE', null, options, callback);
}
<commit_msg>Removed unused 'util' lib<commit_after>//////////////////////////////////////////
// Needle -- Node.js HTTP Client
// Written by Tomás Pollak <tomas@forkhq.com>
// (c) 2012 - Fork Ltd.
// MIT Licensed
//////////////////////////////////////////

var fs = require('fs'),
		path = require('path'),
		http = require('http'),
		https = require('https'),
		url = require('url'),
		stringify = require('qs').stringify,
		version = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString()).version;

// http.globalAgent.maxSockets = 128;
exports.version = version;
try { var unzip = require('zlib').unzip; } catch(e){ /* zlib not supported */ }

var parsers = {
	'application/json': function(data, callback){
		callback(data && JSON.parse(data));
	}
};

try {
	var xml2js = require('xml2js');
	parsers['application/xml'] = function(data, callback){

		var xml_parser = new xml2js.Parser();

		xml_parser.on('end', callback);
		xml_parser.on('error', function(result) {
			// throw("Error parsing XML!")
			callback(null);
		});

		xml_parser.parseString(data);

	};
} catch(e) { }

// utility function for flattening params in multipart POST's
function flatten(object, into, prefix){
	into = into || {};

	for(key in object){
		var prefix_key = prefix ? prefix + "[" + key + "]" : key;
		var prop = object[key];

		if(prop && typeof prop === "object" && !((prop.buffer || prop.file) && prop.content_type))
			flatten(prop, into, prefix_key)
		else
			into[prefix_key] = prop;
	}

	return into;
}

var Needle = {

	default_boundary: '--------------------NODENEEDLEHTTPCLIENT',

	request: function(uri, method, data, options, callback){

		var self = this;
		var callback = (typeof options == 'function') ? options : callback;
		var options = options || {};
		if(uri.indexOf('http') == -1) uri = 'http://' + uri;

		var remote = options.proxy ? url.parse(options.proxy) : url.parse(uri);
		var is_https = remote.protocol === "https:";
		var port = remote.port || (is_https ? 443 : 80);
		var request_compressed = (options.compressed && typeof unzip != 'undefined') || false;
		var post_data = null;

		var request_opts = {
			encoding: options.multipart ? 'binary' : 'utf8',
			protocol: is_https ? https : http,
			parse_response: options.parse === false ? false : true,
			timeout: options.timeout || 10000 // 10 seconds timeout
		}

		var headers = {
			"Host" : options.proxy ? url.parse(uri).hostname : remote.hostname,
			"User-Agent": "Needle/" + version + " (NodeJS " + process.version + ")",
			"Connection": "close",
			"Accept": "*/*"
		}

		if(request_compressed) headers['Accept-Encoding'] = 'gzip,deflate';

		for(h in options.headers)
			headers[h] = options.headers[h];

		if(options.username && options.password){
			var b = new Buffer([options.username, options.password].join(':'));
			headers['Authorization'] = "Basic " + b.toString('base64');
		}

		var http_opts = {
			host: remote.hostname,
			port: port,
			path: options.proxy ? uri : remote.pathname + (remote.search || ''),
			method: method,
			headers: headers
		}

		if (typeof options.agent != 'undefined') http_opts.agent = options.agent;

		if(data) {
			if(options.multipart){

				var boundary = options.boundary || this.default_boundary;
				return this.build_multipart_body(data, boundary, function(err, body){

					if(err) throw(err);
					headers['Content-Type'] = 'multipart/form-data; boundary=' + boundary;
					headers['Content-Length'] = body.length;
					self.send_request(request_opts, http_opts, body, callback);

				});

			} else {
				post_data = (typeof(data) === "string") ? data : stringify(data);
				headers['Content-Type'] = 'application/x-www-form-urlencoded';
				headers['Content-Length'] = post_data.length;
			}

		}

		this.send_request(request_opts, http_opts, post_data, callback);

	},

	send_request: function(request_opts, http_opts, post_data, callback){

		if(process.env.DEBUG) console.log(http_opts + "\n\n" + post_data);
		var self = this, timer, response_opts = {parse_response: request_opts.parse_response};

		var request = request_opts.protocol.request(http_opts, function(response){
			if(timer) clearTimeout(timer);

			var body = '';
			var compressed = /gzip|deflate/.test(response.headers['content-encoding']);
			response.setEncoding(compressed ? 'binary' : 'utf8');

			response.on('data', function(chunk){
				body += chunk;
			});

			response.on('end', function() {
				if(typeof unzip != 'undefined' && compressed)
					unzip(new Buffer(body, 'binary'), function(err, buff){
						self.response_end(response_opts, response, buff.toString(), callback);
					});
				else
					self.response_end(response_opts, response, body, callback);
			});

		});

		if(request_opts.timeout) {
			timer = setTimeout(function() {
				request.abort();
			}, request_opts.timeout)
		}

		request.on('error', function(err) {
			if(process.env.DEBUG) console.log('Error on request: ' + err.toString());
			if(timer) clearTimeout(timer);
			if(callback) callback(err || new Error("Unkown error on request."));
		});

		if(post_data) request.write(post_data, request_opts.encoding);
		request.end();

	},

	response_end: function(opts, response, body, callback){

		if(process.env.DEBUG) console.log(response.headers);
		if(!callback) return;
		var content_type = response.headers['content-type'] && response.headers['content-type'].split(';')[0];

		if(opts.parse_response && parsers[content_type]) {
			parsers[content_type](body, function(result){
				callback(null, response, result);
			});
		} else {
			callback(null, response, body);
		}

	},

	build_multipart_body: function(data, boundary, callback){

		var body = '';
		var object = flatten(data);
		var count = Object.keys(object).length;

		for(var key in object){

			var value = object[key];
			if(value === null || typeof value == 'undefined') return --count;

			var part = (value.buffer || value.file) && value.content_type ? value : {value: value};

			this.generate_part(key, part, boundary, function(err, section){
				if(err) return callback(err);
				body += section;
				--count || callback(null, body + '--' + boundary + '--');
			});

		}

	},

	generate_part: function(name, part, boundary, callback){

		var return_part = '--' + boundary + "\r\n";
		return_part += "Content-Disposition: form-data; name=\"" + name + "\"";

		var append = function(data, filename){

			if(data){
				return_part += "; filename=\"" + encodeURIComponent(filename) + "\"\r\n";
				return_part += "Content-Type: " + part.content_type + "\r\n\r\n";
				return_part += (part.content_type.indexOf('text') == -1)
					? data.toString('binary')
					: data.toString('utf8');
			}

			callback(null, return_part + '\r\n');
		};

		if((part.file || part.buffer) && part.content_type){

			var filename = part.filename ? part.filename : part.file ? path.basename(part.file) : name;
			if(part.buffer) return append(part.buffer, filename);

			fs.readFile(part.file, function(err, data){

				if(err) return callback(err);
				append(data, filename);

			});

		} else {

			return_part += "\r\n\r\n";
			return_part += part.value;
			append();

		}

	}

}

exports.head = function(uri, options, callback){
	return Needle.request(uri, 'HEAD', null, options, callback);
}

exports.get = function(uri, options, callback){
	return Needle.request(uri, 'GET', null, options, callback);
}

exports.post = function(uri, data, options, callback){
	if(!data) throw('POST request expects data.');
	return Needle.request(uri, 'POST', data, options, callback);
}

exports.put = function(uri, data, options, callback){
	if(!data) throw('PUT request expects data.');
	return Needle.request(uri, 'PUT', data, options, callback);
}

exports.delete = function(uri, data, options, callback){
	return Needle.request(uri, 'DELETE', null, options, callback);
}
<|endoftext|>


<commit_before>var express = require('express');
var cors = require('cors');
var request = require('request');

function helloRoute() {
  var hello = new express.Router();
  hello.use(cors());

  // POST REST endpoint - query params may or may not be populated
  hello.post('/', function(req, res) {
    console.log(new Date(), 'In create route POST /');
    var url = 'http://' + process.env.FUSE_ADDRESS + '/flights';

    var body = {
            "@xmlns:BAPI_FLCUST_CREATEFROMDATA":"http://sap.fusesource.org/rfc/NPL/BAPI_FLCUST_CREATEFROMDATA",
            "@TEST_RUN":"",
            "CUSTOMER_DATA":{
              "@CUSTNAME": req.body,
              "@FORM":"",
              "@STREET":"From FH",
              "@POBOX":"",
              "@POSTCODE":"",
              "@CITY":"",
              "@COUNTR":"",
              "@COUNTR_ISO":"",
              "@REGION":"",
              "@PHONE":"",
              "@EMAIL":"",
              "@CUSTTYPE":"P",
              "@DISCOUNT":"000",
              "@LANGU":"",
              "@LANGU_ISO":""
            },
            "EXTENSION_IN":[],
            "RETURN":[]
          }

		request({ url: url, method: 'POST', body: body , json: true }, function(error, response, body) {
      if (error) {
        res.status(500).send(error);
      } else if (response.statusCode == 200) {
        res.type('json').send(body);
      } else {
        res.status(500).send('unexpected http response code ' + response.statusCode);
      }
		});
  });

  return hello;
}

module.exports = helloRoute;

<commit_msg>update file /lib/create.js<commit_after>var express = require('express');
var cors = require('cors');
var request = require('request');
var bodyParser = require('body-parser');

function helloRoute() {
  var hello = new express.Router();
  hello.use(cors());
  hello.use(bodyParser.text());

  // POST REST endpoint - query params may or may not be populated
  hello.post('/', function(req, res) {
    console.log(new Date(), 'In create route POST /');
    var url = 'http://' + process.env.FUSE_ADDRESS + '/flights';

    var body = {
            "@xmlns:BAPI_FLCUST_CREATEFROMDATA":"http://sap.fusesource.org/rfc/NPL/BAPI_FLCUST_CREATEFROMDATA",
            "@TEST_RUN":"",
            "CUSTOMER_DATA":{
              "@CUSTNAME": req.body,
              "@FORM":"",
              "@STREET":"From FH",
              "@POBOX":"",
              "@POSTCODE":"",
              "@CITY":"",
              "@COUNTR":"",
              "@COUNTR_ISO":"",
              "@REGION":"",
              "@PHONE":"",
              "@EMAIL":"",
              "@CUSTTYPE":"P",
              "@DISCOUNT":"000",
              "@LANGU":"",
              "@LANGU_ISO":""
            },
            "EXTENSION_IN":[],
            "RETURN":[]
          }

		request({ url: url, method: 'POST', body: body , json: true }, function(error, response, body) {
      if (error) {
        res.status(500).send(error);
      } else if (response.statusCode == 200) {
        res.type('json').send(body);
      } else {
        res.status(500).send('unexpected http response code ' + response.statusCode);
      }
		});
  });

  return hello;
}

module.exports = helloRoute;

<|endoftext|>