<commit_before>/*
  backgrid-paginator
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  Licensed under the MIT @license.
*/
(function (root, factory) {

  // CommonJS
  if (typeof exports == "object") {
    module.exports = factory(require("underscore"),
                             require("backbone"),
                             require("backgrid"),
                             require("backbone-pageable"));
  }
  // Browser
  else {
    factory(root._, root.Backbone, root.Backgrid);
  }

}(this, function (_, Backbone, Backgrid) {

  "use strict";

  /**
     PageHandle is a class that renders the actual page handles and reacts to
     click events for pagination.

     This class acts in two modes - control or discrete page handle modes. If
     one of the `is*` flags is `true`, an instance of this class is under
     control page handle mode. Setting a `pageIndex` to an instance of this
     class under control mode has no effect and the correct page index will
     always be inferred from the `is*` flag. Only one of the `is*` flags should
     be set to `true` at a time. For example, an instance of this class cannot
     simultaneously be a rewind control and a fast forward control. A `label`
     and a `title` template or a string are required to be passed to the
     constuctor under this mode. If a `title` template is provided, it __MUST__
     accept a parameter `label`. When the `label` is provided to the `title`
     template function, its result will be used to render the generated anchor's
     title attribute.

     If all of the `is*` flags is set to `false`, which is the default, an
     instance of this class will be in discrete page handle mode. An instance
     under this mode requires the `pageIndex` to be passed from the constructor
     as an option and it __MUST__ be a 0-based index of the list of page numbers
     to render. The constuctor will normalize the base to the same base the
     underlying PageableCollection collection instance uses. A `label` is not
     required under this mode, which will default to the equivalent 1-based page
     index calculated from `pageIndex` and the underlying PageableCollection
     instance. A provided `label` will still be honored however. The `title`
     parameter is also not required under this mode, in which case the default
     `title` template will be used. You are encouraged to provide your own
     `title` template however if you wish to localize the title strings.

     If this page handle represents the current page, an `active` class will be
     placed on the root list element.

     If this page handle is at the border of the list of pages, a `disabled`
     class will be placed on the root list element.

     Only page handles that are neither `active` nor `disabled` will respond to
     click events and triggers pagination.

     @class Backgrid.Extension.PageHandle
  */
  var PageHandle = Backgrid.Extension.PageHandle = Backbone.View.extend({

    /** @property */
    tagName: "li",

    /** @property */
    events: {
      "click a": "changePage"
    },

    /**
       @property {string|function(Object.<string, string>): string} title
       The title to use for the `title` attribute of the generated page handle
       anchor elements. It can be a string or an Underscore template function
       that takes a mandatory `label` parameter.
    */
    title: _.template('Page <%- label %>', null, {variable: null}),

    /**
       @property {boolean} isRewind Whether this handle represents a rewind
       control
    */
    isRewind: false,

    /**
       @property {boolean} isBack Whether this handle represents a back
       control
    */
    isBack: false,

    /**
       @property {boolean} isForward Whether this handle represents a forward
       control
    */
    isForward: false,

    /**
       @property {boolean} isFastForward Whether this handle represents a fast
       forward control
    */
    isFastForward: false,

    /**
       Initializer.

       @param {Object} options
       @param {Backbone.Collection} options.collection
       @param {number} pageIndex 0-based index of the page number this handle
       handles. This parameter will be normalized to the base the underlying
       PageableCollection uses.
       @param {string} [options.label] If provided it is used to render the
       anchor text, otherwise the normalized pageIndex will be used
       instead. Required if any of the `is*` flags is set to `true`.
       @param {string} [options.title]
       @param {boolean} [options.isRewind=false]
       @param {boolean} [options.isBack=false]
       @param {boolean} [options.isForward=false]
       @param {boolean} [options.isFastForward=false]
    */
    initialize: function (options) {
      var collection = this.collection;
      var state = collection.state;
      var currentPage = state.currentPage;
      var firstPage = state.firstPage;
      var lastPage = state.lastPage;

      _.extend(this, _.pick(options,
                            ["isRewind", "isBack", "isForward", "isFastForward"]));

      var pageIndex;
      if (this.isRewind) pageIndex = firstPage;
      else if (this.isBack) pageIndex = Math.max(firstPage, currentPage - 1);
      else if (this.isForward) pageIndex = Math.min(lastPage, currentPage + 1);
      else if (this.isFastForward) pageIndex = lastPage;
      else {
        pageIndex = +options.pageIndex;
        pageIndex = (firstPage ? pageIndex + 1 : pageIndex);
      }
      this.pageIndex = pageIndex;

      this.label = (options.label || (firstPage ? pageIndex : pageIndex + 1)) + '';
      var title = options.title || this.title;
      this.title = _.isFunction(title) ? title({label: this.label}) : title;
    },

    /**
       Renders a clickable anchor element under a list item.
    */
    render: function () {
      this.$el.empty();
      var anchor = document.createElement("a");
      anchor.href = '#';
      if (this.title) anchor.title = this.title;
      anchor.innerHTML = this.label;
      this.el.appendChild(anchor);

      var collection = this.collection;
      var state = collection.state;
      var currentPage = state.currentPage;
      var pageIndex = this.pageIndex;

      if (this.isRewind && currentPage == state.firstPage ||
         this.isBack && !collection.hasPrevious() ||
         this.isForward && !collection.hasNext() ||
         this.isFastForward && (currentPage == state.lastPage || state.totalPages < 1)) {
        this.$el.addClass("disabled");
      }
      else if (!(this.isRewind ||
                 this.isBack ||
                 this.isForward ||
                 this.isFastForward) &&
               state.currentPage == pageIndex) {
        this.$el.addClass("active");
      }

      this.delegateEvents();
      return this;
    },

    /**
       jQuery click event handler. Goes to the page this PageHandle instance
       represents. No-op if this page handle is currently active or disabled.
    */
    changePage: function (e) {
      e.preventDefault();
      var $el = this.$el, col = this.collection;
      if (!$el.hasClass("active") && !$el.hasClass("disabled")) {
        if (this.isRewind) col.getFirstPage();
        else if (this.isBack) col.getPreviousPage();
        else if (this.isForward) col.getNextPage();
        else if (this.isFastForward) col.getLastPage();
        else col.getPage(this.pageIndex, {reset: true});
      }
      return this;
    }

  });

  /**
     Paginator is a Backgrid extension that renders a series of configurable
     pagination handles. This extension is best used for splitting a large data
     set across multiple pages. If the number of pages is larger then a
     threshold, which is set to 10 by default, the page handles are rendered
     within a sliding window, plus the rewind, back, forward and fast forward
     control handles. The individual control handles can be turned off.

     @class Backgrid.Extension.Paginator
  */
  var Paginator = Backgrid.Extension.Paginator = Backbone.View.extend({

    /** @property */
    className: "backgrid-paginator",

    /** @property */
    windowSize: 10,

    /**
       @property {number} slideScale the number used by #slideHowMuch to scale
       `windowSize` to yield the number of pages to slide. For example, the
       default windowSize(10) * slideScale(0.5) yields 5, which means the window
       will slide forward 5 pages as soon as you've reached page 6. The smaller
       the scale factor the less pages to slide, and vice versa.

       Also See:

       - #slideMaybe
       - #slideHowMuch
    */
    slideScale: 0.5,

    /**
       @property {Object.<string, Object.<string, string>>} controls You can
       disable specific control handles by setting the keys in question to
       null. The defaults will be merged with your controls object, with your
       changes taking precedent.
    */
    controls: {
      rewind: {
        label: "《",
        title: "First"
      },
      back: {
        label: "〈",
        title: "Previous"
      },
      forward: {
        label: "〉",
        title: "Next"
      },
      fastForward: {
        label: "》",
        title: "Last"
      }
    },

    /** @property */
    renderIndexedPageHandles: true,

    /**
       @property {Backgrid.Extension.PageHandle} pageHandle. The PageHandle
       class to use for rendering individual handles
    */
    pageHandle: PageHandle,

    /** @property */
    goBackFirstOnSort: true,

    /**
       Initializer.

       @param {Object} options
       @param {Backbone.Collection} options.collection
       @param {boolean} [options.controls]
       @param {boolean} [options.pageHandle=Backgrid.Extension.PageHandle]
       @param {boolean} [options.goBackFirstOnSort=true]
    */
    initialize: function (options) {
      var self = this;
      self.controls = _.defaults(options.controls || {}, self.controls,
                                 Paginator.prototype.controls);

      _.extend(self, _.pick(options || {}, "windowSize", "pageHandle",
                            "slideScale", "goBackFirstOnSort",
                            "renderIndexedPageHandles"));

      var col = self.collection;
      self.listenTo(col, "add", self.render);
      self.listenTo(col, "remove", self.render);
      self.listenTo(col, "reset", self.render);
      self.listenTo(col, "backgrid:sorted", function () {
        if (self.goBackFirstOnSort) col.getFirstPage({reset: true});
      });
    },

    /**
      Decides whether the window should slide. This method should return 1 if
      sliding should occur and 0 otherwise. The default is sliding should occur
      if half of the pages in a window has been reached.

      __Note__: All the parameters have been normalized to be 0-based.

      @param {number} firstPage
      @param {number} lastPage
      @param {number} currentPage
      @param {number} windowSize
      @param {number} slideScale

      @return {0|1}
     */
    slideMaybe: function (firstPage, lastPage, currentPage, windowSize, slideScale) {
      return Math.round(currentPage % windowSize / windowSize);
    },

    /**
      Decides how many pages to slide when sliding should occur. The default
      simply scales the `windowSize` to arrive at a fraction of the `windowSize`
      to increment.

      __Note__: All the parameters have been normalized to be 0-based.

      @param {number} firstPage
      @param {number} lastPage
      @param {number} currentPage
      @param {number} windowSize
      @param {number} slideScale

      @return {number}
     */
    slideThisMuch: function (firstPage, lastPage, currentPage, windowSize, slideScale) {
      return ~~(windowSize * slideScale);
    },

    _calculateWindow: function () {
      var collection = this.collection;
      var state = collection.state;

      // convert all indices to 0-based here
      var firstPage = state.firstPage;
      var lastPage = +state.lastPage;
      lastPage = Math.max(0, firstPage ? lastPage - 1 : lastPage);
      var currentPage = Math.max(state.currentPage, state.firstPage);
      currentPage = firstPage ? currentPage - 1 : currentPage;
      var windowSize = this.windowSize;
      var slideScale = this.slideScale;
      var windowStart = Math.floor(currentPage / windowSize) * windowSize;
      if (currentPage <= lastPage - this.slideThisMuch()) {
        windowStart += (this.slideMaybe(firstPage, lastPage, currentPage, windowSize, slideScale) *
                        this.slideThisMuch(firstPage, lastPage, currentPage, windowSize, slideScale));
      }
      var windowEnd = Math.min(lastPage + 1, windowStart + windowSize);
      return [windowStart, windowEnd];
    },

    /**
       Creates a list of page handle objects for rendering.

       @return {Array.<Object>} an array of page handle objects hashes
    */
    makeHandles: function () {

      var handles = [];
      var collection = this.collection;

      var window = this._calculateWindow();
      var winStart = window[0], winEnd = window[1];

      if (this.renderIndexedPageHandles) {
        for (var i = winStart; i < winEnd; i++) {
          handles.push(new this.pageHandle({
            collection: collection,
            pageIndex: i
          }));
        }
      }

      var controls = this.controls;
      _.each(["back", "rewind", "forward", "fastForward"], function (key) {
        var value = controls[key];
        if (value) {
          var handleCtorOpts = {
            collection: collection,
            title: value.title,
            label: value.label
          };
          handleCtorOpts["is" + key.slice(0, 1).toUpperCase() + key.slice(1)] = true;
          var handle = new this.pageHandle(handleCtorOpts);
          if (key == "rewind" || key == "back") handles.unshift(handle);
          else handles.push(handle);
        }
      }, this);

      return handles;
    },

    /**
       Render the paginator handles inside an unordered list.
    */
    render: function () {
      this.$el.empty();

      if (this.handles) {
        for (var i = 0, l = this.handles.length; i < l; i++) {
          this.handles[i].remove();
        }
      }

      var handles = this.handles = this.makeHandles();

      var ul = document.createElement("ul");
      for (var i = 0; i < handles.length; i++) {
        ul.appendChild(handles[i].render().el);
      }

      this.el.appendChild(ul);

      return this;
    }

  });

}));
<commit_msg>Fix #18 Replace hasPrevious and hasNext to hasPreviousPage and hasNextPage<commit_after>/*
  backgrid-paginator
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  Licensed under the MIT @license.
*/
(function (root, factory) {

  // CommonJS
  if (typeof exports == "object") {
    module.exports = factory(require("underscore"),
                             require("backbone"),
                             require("backgrid"),
                             require("backbone-pageable"));
  }
  // Browser
  else {
    factory(root._, root.Backbone, root.Backgrid);
  }

}(this, function (_, Backbone, Backgrid) {

  "use strict";

  /**
     PageHandle is a class that renders the actual page handles and reacts to
     click events for pagination.

     This class acts in two modes - control or discrete page handle modes. If
     one of the `is*` flags is `true`, an instance of this class is under
     control page handle mode. Setting a `pageIndex` to an instance of this
     class under control mode has no effect and the correct page index will
     always be inferred from the `is*` flag. Only one of the `is*` flags should
     be set to `true` at a time. For example, an instance of this class cannot
     simultaneously be a rewind control and a fast forward control. A `label`
     and a `title` template or a string are required to be passed to the
     constuctor under this mode. If a `title` template is provided, it __MUST__
     accept a parameter `label`. When the `label` is provided to the `title`
     template function, its result will be used to render the generated anchor's
     title attribute.

     If all of the `is*` flags is set to `false`, which is the default, an
     instance of this class will be in discrete page handle mode. An instance
     under this mode requires the `pageIndex` to be passed from the constructor
     as an option and it __MUST__ be a 0-based index of the list of page numbers
     to render. The constuctor will normalize the base to the same base the
     underlying PageableCollection collection instance uses. A `label` is not
     required under this mode, which will default to the equivalent 1-based page
     index calculated from `pageIndex` and the underlying PageableCollection
     instance. A provided `label` will still be honored however. The `title`
     parameter is also not required under this mode, in which case the default
     `title` template will be used. You are encouraged to provide your own
     `title` template however if you wish to localize the title strings.

     If this page handle represents the current page, an `active` class will be
     placed on the root list element.

     If this page handle is at the border of the list of pages, a `disabled`
     class will be placed on the root list element.

     Only page handles that are neither `active` nor `disabled` will respond to
     click events and triggers pagination.

     @class Backgrid.Extension.PageHandle
  */
  var PageHandle = Backgrid.Extension.PageHandle = Backbone.View.extend({

    /** @property */
    tagName: "li",

    /** @property */
    events: {
      "click a": "changePage"
    },

    /**
       @property {string|function(Object.<string, string>): string} title
       The title to use for the `title` attribute of the generated page handle
       anchor elements. It can be a string or an Underscore template function
       that takes a mandatory `label` parameter.
    */
    title: _.template('Page <%- label %>', null, {variable: null}),

    /**
       @property {boolean} isRewind Whether this handle represents a rewind
       control
    */
    isRewind: false,

    /**
       @property {boolean} isBack Whether this handle represents a back
       control
    */
    isBack: false,

    /**
       @property {boolean} isForward Whether this handle represents a forward
       control
    */
    isForward: false,

    /**
       @property {boolean} isFastForward Whether this handle represents a fast
       forward control
    */
    isFastForward: false,

    /**
       Initializer.

       @param {Object} options
       @param {Backbone.Collection} options.collection
       @param {number} pageIndex 0-based index of the page number this handle
       handles. This parameter will be normalized to the base the underlying
       PageableCollection uses.
       @param {string} [options.label] If provided it is used to render the
       anchor text, otherwise the normalized pageIndex will be used
       instead. Required if any of the `is*` flags is set to `true`.
       @param {string} [options.title]
       @param {boolean} [options.isRewind=false]
       @param {boolean} [options.isBack=false]
       @param {boolean} [options.isForward=false]
       @param {boolean} [options.isFastForward=false]
    */
    initialize: function (options) {
      var collection = this.collection;
      var state = collection.state;
      var currentPage = state.currentPage;
      var firstPage = state.firstPage;
      var lastPage = state.lastPage;

      _.extend(this, _.pick(options,
                            ["isRewind", "isBack", "isForward", "isFastForward"]));

      var pageIndex;
      if (this.isRewind) pageIndex = firstPage;
      else if (this.isBack) pageIndex = Math.max(firstPage, currentPage - 1);
      else if (this.isForward) pageIndex = Math.min(lastPage, currentPage + 1);
      else if (this.isFastForward) pageIndex = lastPage;
      else {
        pageIndex = +options.pageIndex;
        pageIndex = (firstPage ? pageIndex + 1 : pageIndex);
      }
      this.pageIndex = pageIndex;

      this.label = (options.label || (firstPage ? pageIndex : pageIndex + 1)) + '';
      var title = options.title || this.title;
      this.title = _.isFunction(title) ? title({label: this.label}) : title;
    },

    /**
       Renders a clickable anchor element under a list item.
    */
    render: function () {
      this.$el.empty();
      var anchor = document.createElement("a");
      anchor.href = '#';
      if (this.title) anchor.title = this.title;
      anchor.innerHTML = this.label;
      this.el.appendChild(anchor);

      var collection = this.collection;
      var state = collection.state;
      var currentPage = state.currentPage;
      var pageIndex = this.pageIndex;

      if (this.isRewind && currentPage == state.firstPage ||
         this.isBack && !collection.hasPreviousPage() ||
         this.isForward && !collection.hasNextPage() ||
         this.isFastForward && (currentPage == state.lastPage || state.totalPages < 1)) {
        this.$el.addClass("disabled");
      }
      else if (!(this.isRewind ||
                 this.isBack ||
                 this.isForward ||
                 this.isFastForward) &&
               state.currentPage == pageIndex) {
        this.$el.addClass("active");
      }

      this.delegateEvents();
      return this;
    },

    /**
       jQuery click event handler. Goes to the page this PageHandle instance
       represents. No-op if this page handle is currently active or disabled.
    */
    changePage: function (e) {
      e.preventDefault();
      var $el = this.$el, col = this.collection;
      if (!$el.hasClass("active") && !$el.hasClass("disabled")) {
        if (this.isRewind) col.getFirstPage();
        else if (this.isBack) col.getPreviousPage();
        else if (this.isForward) col.getNextPage();
        else if (this.isFastForward) col.getLastPage();
        else col.getPage(this.pageIndex, {reset: true});
      }
      return this;
    }

  });

  /**
     Paginator is a Backgrid extension that renders a series of configurable
     pagination handles. This extension is best used for splitting a large data
     set across multiple pages. If the number of pages is larger then a
     threshold, which is set to 10 by default, the page handles are rendered
     within a sliding window, plus the rewind, back, forward and fast forward
     control handles. The individual control handles can be turned off.

     @class Backgrid.Extension.Paginator
  */
  var Paginator = Backgrid.Extension.Paginator = Backbone.View.extend({

    /** @property */
    className: "backgrid-paginator",

    /** @property */
    windowSize: 10,

    /**
       @property {number} slideScale the number used by #slideHowMuch to scale
       `windowSize` to yield the number of pages to slide. For example, the
       default windowSize(10) * slideScale(0.5) yields 5, which means the window
       will slide forward 5 pages as soon as you've reached page 6. The smaller
       the scale factor the less pages to slide, and vice versa.

       Also See:

       - #slideMaybe
       - #slideHowMuch
    */
    slideScale: 0.5,

    /**
       @property {Object.<string, Object.<string, string>>} controls You can
       disable specific control handles by setting the keys in question to
       null. The defaults will be merged with your controls object, with your
       changes taking precedent.
    */
    controls: {
      rewind: {
        label: "《",
        title: "First"
      },
      back: {
        label: "〈",
        title: "Previous"
      },
      forward: {
        label: "〉",
        title: "Next"
      },
      fastForward: {
        label: "》",
        title: "Last"
      }
    },

    /** @property */
    renderIndexedPageHandles: true,

    /**
       @property {Backgrid.Extension.PageHandle} pageHandle. The PageHandle
       class to use for rendering individual handles
    */
    pageHandle: PageHandle,

    /** @property */
    goBackFirstOnSort: true,

    /**
       Initializer.

       @param {Object} options
       @param {Backbone.Collection} options.collection
       @param {boolean} [options.controls]
       @param {boolean} [options.pageHandle=Backgrid.Extension.PageHandle]
       @param {boolean} [options.goBackFirstOnSort=true]
    */
    initialize: function (options) {
      var self = this;
      self.controls = _.defaults(options.controls || {}, self.controls,
                                 Paginator.prototype.controls);

      _.extend(self, _.pick(options || {}, "windowSize", "pageHandle",
                            "slideScale", "goBackFirstOnSort",
                            "renderIndexedPageHandles"));

      var col = self.collection;
      self.listenTo(col, "add", self.render);
      self.listenTo(col, "remove", self.render);
      self.listenTo(col, "reset", self.render);
      self.listenTo(col, "backgrid:sorted", function () {
        if (self.goBackFirstOnSort) col.getFirstPage({reset: true});
      });
    },

    /**
      Decides whether the window should slide. This method should return 1 if
      sliding should occur and 0 otherwise. The default is sliding should occur
      if half of the pages in a window has been reached.

      __Note__: All the parameters have been normalized to be 0-based.

      @param {number} firstPage
      @param {number} lastPage
      @param {number} currentPage
      @param {number} windowSize
      @param {number} slideScale

      @return {0|1}
     */
    slideMaybe: function (firstPage, lastPage, currentPage, windowSize, slideScale) {
      return Math.round(currentPage % windowSize / windowSize);
    },

    /**
      Decides how many pages to slide when sliding should occur. The default
      simply scales the `windowSize` to arrive at a fraction of the `windowSize`
      to increment.

      __Note__: All the parameters have been normalized to be 0-based.

      @param {number} firstPage
      @param {number} lastPage
      @param {number} currentPage
      @param {number} windowSize
      @param {number} slideScale

      @return {number}
     */
    slideThisMuch: function (firstPage, lastPage, currentPage, windowSize, slideScale) {
      return ~~(windowSize * slideScale);
    },

    _calculateWindow: function () {
      var collection = this.collection;
      var state = collection.state;

      // convert all indices to 0-based here
      var firstPage = state.firstPage;
      var lastPage = +state.lastPage;
      lastPage = Math.max(0, firstPage ? lastPage - 1 : lastPage);
      var currentPage = Math.max(state.currentPage, state.firstPage);
      currentPage = firstPage ? currentPage - 1 : currentPage;
      var windowSize = this.windowSize;
      var slideScale = this.slideScale;
      var windowStart = Math.floor(currentPage / windowSize) * windowSize;
      if (currentPage <= lastPage - this.slideThisMuch()) {
        windowStart += (this.slideMaybe(firstPage, lastPage, currentPage, windowSize, slideScale) *
                        this.slideThisMuch(firstPage, lastPage, currentPage, windowSize, slideScale));
      }
      var windowEnd = Math.min(lastPage + 1, windowStart + windowSize);
      return [windowStart, windowEnd];
    },

    /**
       Creates a list of page handle objects for rendering.

       @return {Array.<Object>} an array of page handle objects hashes
    */
    makeHandles: function () {

      var handles = [];
      var collection = this.collection;

      var window = this._calculateWindow();
      var winStart = window[0], winEnd = window[1];

      if (this.renderIndexedPageHandles) {
        for (var i = winStart; i < winEnd; i++) {
          handles.push(new this.pageHandle({
            collection: collection,
            pageIndex: i
          }));
        }
      }

      var controls = this.controls;
      _.each(["back", "rewind", "forward", "fastForward"], function (key) {
        var value = controls[key];
        if (value) {
          var handleCtorOpts = {
            collection: collection,
            title: value.title,
            label: value.label
          };
          handleCtorOpts["is" + key.slice(0, 1).toUpperCase() + key.slice(1)] = true;
          var handle = new this.pageHandle(handleCtorOpts);
          if (key == "rewind" || key == "back") handles.unshift(handle);
          else handles.push(handle);
        }
      }, this);

      return handles;
    },

    /**
       Render the paginator handles inside an unordered list.
    */
    render: function () {
      this.$el.empty();

      if (this.handles) {
        for (var i = 0, l = this.handles.length; i < l; i++) {
          this.handles[i].remove();
        }
      }

      var handles = this.handles = this.makeHandles();

      var ul = document.createElement("ul");
      for (var i = 0; i < handles.length; i++) {
        ul.appendChild(handles[i].render().el);
      }

      this.el.appendChild(ul);

      return this;
    }

  });

}));
<|endoftext|>


<commit_before>// Essentially, this is a fstream.DirReader class, but with a
// bit of special logic to read the specified sort of ignore files,
// and a filter that prevents it from picking up anything excluded
// by those files.

var Minimatch = require("minimatch").Minimatch
, fstream = require("fstream")
, DirReader = fstream.DirReader
, inherits = require("inherits")
, path = require("path")

module.exports = IgnoreReader

inherits(IgnoreReader, DirReader)

function IgnoreReader (props) {
  if (!(this instanceof IgnoreReader)) {
    return new IgnoreReader(props)
  }

  // must be a Directory type
  if (typeof props === "string") {
    props = { path: props }
  }

  props.type = "Directory"
  props.Directory = true

  if (!props.ignoreFiles) props.ignoreFiles = [".ignore"]
  this.ignoreFiles = props.ignoreFiles

  this.ignoreRules = null

  // get the set of ancestors that have ignore rules.
  // we unshift so that they'll be in the right order
  // to be iterated over from 0->length later.
  this.ancestorIgnores = []
  for (var p = this.parent; p; p = p.parent) {
    if (p.ignoreRules) this.ancestorIgnores.unshift(p)
  }
  // then, push this one in, so that it's the highest precedence
  this.ancestorIgnores.push(this)

  if (props.filter) this._filter = props.filter
  props.filter = this.filter.bind(this)

  // ensure that .ignore files always show up at the top of the list
  // that way, they can be read before proceeding to handle other
  // entries in that same folder
  if (props.sort) {
    this._sort = props.sort === "alpha" ? alphasort : props.sort
  }
  props.sort = this.sort.bind(this)

  this.on("entry", function (e) {
    // if this is an ignore file, then process it for rules
    var isIg = this.ignoreFiles.indexOf(e.basename) !== -1
    if (!isIg) return

    this.emit("ignoreFile", e)

    // call e.abort() in the above event to prevent its inclusion
    if (!e._aborted)
      this.addIgnoreFile(e)
  })

  DirReader.call(this, props)
}

IgnoreReader.prototype.getChildProps = function (stat) {
  var props = DirReader.prototype.getChildProps.call(this, stat)
  props.ignoreFiles = this.ignoreFiles

  // Directories have to be read as IgnoreReaders
  // otherwise fstream.Reader will create a DirReader instead.
  if (stat.isDirectory()) {
    props.type = this.constructor
  }
  return props
}

IgnoreReader.prototype.addIgnoreFile = function (e) {
  // buffer the output.
  // these files won't be very big.
  var buf = new Buffer(e.size)
  , i = 0

  e.on("data", ondata)
  function ondata (c) {
    c.copy(buf, i)
    i += c.length
  }

  var onend = function onend () {
    // perhaps aborted.  do nothing.
    if (i === 0 || e._aborted) return
    var rules = this.readRules(buf, e)
    this.addIgnoreRules(rules)
  }.bind(this)
  e.on("end", onend)

  e.on("abort", function () {
    e.removeListener("data", ondata)
    e.removeListener("end", onend)
  })
}

IgnoreReader.prototype.readRules = function (buf, entry) {
  return buf.toString().split(/\r?\n/)
}

IgnoreReader.prototype.addIgnoreRules = function (set) {
  // filter out anything obvious
  set = set.filter(function (s) {
    s = s.trim()
    return s && !s.match(/^#/)
  })

  // no rules to add!
  if (!set.length) return

  // now get a minimatch object for each one of these.
  // Note that we need to allow dot files by default, and
  // not switch the meaning of their exclusion, so they're
  var mm = set.map(function (s) {
    return new Minimatch(s, { matchBase: true, dot: true, flipNegate: true })
  })

  if (!this.ignoreRules) this.ignoreRules = []
  this.ignoreRules.push.apply(this.ignoreRules, mm)
}

IgnoreReader.prototype.filter = function (entry) {
  this.applyIgnores(entry)
  if (entry.excluded) return false
  return this._filter ? this._filter.apply(entry, arguments) : true

}

// sets an "excluded" flag on the entry if it's excluded.
IgnoreReader.prototype.applyIgnores = function (entry) {
  // walk back up the family tree.
  // At each level, test the entry against all the rules
  // at that level, as if it was rooted there.
  // For example, when testing a/b/c, we'll test against a's rules
  // as /b/c and then against b's rules as /c.  This is because
  // a .ignore file at a/.ignore would igore the a/b/c file with
  // a rule of `/b/c`, but not with a rule of `/c`.
  //
  // Negated Rules
  // Since we're *ignoring* things here, negating means that a file
  // is re-included, if it would have been excluded by a previous
  // rule.  So, negated rules are only relevant if the file
  // has been excluded.
  //
  // Similarly, if a file has been excluded, then there's no point
  // trying it against rules that have already been applied
  //
  // We're using the "flipnegate" flag here, which tells minimatch
  // to set the "negate" for our information, but still report
  // whether the core pattern was a hit or a miss.

  if (this.parent) this.parent.applyIgnores(entry)
  if (!this.ignoreRules) return

  var test = entry.path.substr(this.path.length)
  // console.error("rules", this.ignoreRules.map(function (rule) {
  //   return (rule.negate ? "!" : "") + rule.pattern
  // }))

  this.ignoreRules.forEach(function (rule) {
    // negation means inclusion
    if (rule.negate && !entry.excluded ||
        !rule.negate && entry.excluded) return

    var match = rule.match(test) || rule.match(test.substr(1))
    if (match) {
      entry.excluded = !rule.negate
      // console.error(entry.path.substr(entry.root.path.length),
      //               rule.pattern, rule.negate, entry.excluded)
    }
  })
}

IgnoreReader.prototype.sort = function (a, b) {
  var aig = this.ignoreFiles.indexOf(a) !== -1
  , big = this.ignoreFiles.indexOf(b) !== -1
  if (aig && !big) return -1
  if (big && !aig) return 1
  return this._sort(a, b)
}

IgnoreReader.prototype._sort = function (a, b) {
  return 0
}

function alphasort (a, b) {
  return a === b ? 0
       : a.toLowerCase() > b.toLowerCase() ? 1
       : a.toLowerCase() < b.toLowerCase() ? -1
       : a > b ? 1
       : -1
}
<commit_msg>Expose entry in addRules method<commit_after>// Essentially, this is a fstream.DirReader class, but with a
// bit of special logic to read the specified sort of ignore files,
// and a filter that prevents it from picking up anything excluded
// by those files.

var Minimatch = require("minimatch").Minimatch
, fstream = require("fstream")
, DirReader = fstream.DirReader
, inherits = require("inherits")
, path = require("path")

module.exports = IgnoreReader

inherits(IgnoreReader, DirReader)

function IgnoreReader (props) {
  if (!(this instanceof IgnoreReader)) {
    return new IgnoreReader(props)
  }

  // must be a Directory type
  if (typeof props === "string") {
    props = { path: props }
  }

  props.type = "Directory"
  props.Directory = true

  if (!props.ignoreFiles) props.ignoreFiles = [".ignore"]
  this.ignoreFiles = props.ignoreFiles

  this.ignoreRules = null

  // get the set of ancestors that have ignore rules.
  // we unshift so that they'll be in the right order
  // to be iterated over from 0->length later.
  this.ancestorIgnores = []
  for (var p = this.parent; p; p = p.parent) {
    if (p.ignoreRules) this.ancestorIgnores.unshift(p)
  }
  // then, push this one in, so that it's the highest precedence
  this.ancestorIgnores.push(this)

  if (props.filter) this._filter = props.filter
  props.filter = this.filter.bind(this)

  // ensure that .ignore files always show up at the top of the list
  // that way, they can be read before proceeding to handle other
  // entries in that same folder
  if (props.sort) {
    this._sort = props.sort === "alpha" ? alphasort : props.sort
  }
  props.sort = this.sort.bind(this)

  this.on("entry", function (e) {
    // if this is an ignore file, then process it for rules
    var isIg = this.ignoreFiles.indexOf(e.basename) !== -1
    if (!isIg) return

    this.emit("ignoreFile", e)

    // call e.abort() in the above event to prevent its inclusion
    if (!e._aborted)
      this.addIgnoreFile(e)
  })

  DirReader.call(this, props)
}

IgnoreReader.prototype.getChildProps = function (stat) {
  var props = DirReader.prototype.getChildProps.call(this, stat)
  props.ignoreFiles = this.ignoreFiles

  // Directories have to be read as IgnoreReaders
  // otherwise fstream.Reader will create a DirReader instead.
  if (stat.isDirectory()) {
    props.type = this.constructor
  }
  return props
}

IgnoreReader.prototype.addIgnoreFile = function (e) {
  // buffer the output.
  // these files won't be very big.
  var buf = new Buffer(e.size)
  , i = 0

  e.on("data", ondata)
  function ondata (c) {
    c.copy(buf, i)
    i += c.length
  }

  var onend = function onend () {
    // perhaps aborted.  do nothing.
    if (i === 0 || e._aborted) return
    var rules = this.readRules(buf, e)
    this.addIgnoreRules(rules, e)
  }.bind(this)
  e.on("end", onend)

  e.on("abort", function () {
    e.removeListener("data", ondata)
    e.removeListener("end", onend)
  })
}

IgnoreReader.prototype.readRules = function (buf, entry) {
  return buf.toString().split(/\r?\n/)
}

IgnoreReader.prototype.addIgnoreRules = function (set, e) {
  // filter out anything obvious
  set = set.filter(function (s) {
    s = s.trim()
    return s && !s.match(/^#/)
  })

  // no rules to add!
  if (!set.length) return

  // now get a minimatch object for each one of these.
  // Note that we need to allow dot files by default, and
  // not switch the meaning of their exclusion, so they're
  var mm = set.map(function (s) {
    return new Minimatch(s, { matchBase: true, dot: true, flipNegate: true })
  })

  if (!this.ignoreRules) this.ignoreRules = []
  this.ignoreRules.push.apply(this.ignoreRules, mm)
}

IgnoreReader.prototype.filter = function (entry) {
  this.applyIgnores(entry)
  if (entry.excluded) return false
  return this._filter ? this._filter.apply(entry, arguments) : true

}

// sets an "excluded" flag on the entry if it's excluded.
IgnoreReader.prototype.applyIgnores = function (entry) {
  // walk back up the family tree.
  // At each level, test the entry against all the rules
  // at that level, as if it was rooted there.
  // For example, when testing a/b/c, we'll test against a's rules
  // as /b/c and then against b's rules as /c.  This is because
  // a .ignore file at a/.ignore would igore the a/b/c file with
  // a rule of `/b/c`, but not with a rule of `/c`.
  //
  // Negated Rules
  // Since we're *ignoring* things here, negating means that a file
  // is re-included, if it would have been excluded by a previous
  // rule.  So, negated rules are only relevant if the file
  // has been excluded.
  //
  // Similarly, if a file has been excluded, then there's no point
  // trying it against rules that have already been applied
  //
  // We're using the "flipnegate" flag here, which tells minimatch
  // to set the "negate" for our information, but still report
  // whether the core pattern was a hit or a miss.

  if (this.parent) this.parent.applyIgnores(entry)
  if (!this.ignoreRules) return

  var test = entry.path.substr(this.path.length)
  // console.error("rules", this.ignoreRules.map(function (rule) {
  //   return (rule.negate ? "!" : "") + rule.pattern
  // }))

  this.ignoreRules.forEach(function (rule) {
    // negation means inclusion
    if (rule.negate && !entry.excluded ||
        !rule.negate && entry.excluded) return

    var match = rule.match(test) || rule.match(test.substr(1))
    if (match) {
      entry.excluded = !rule.negate
    }
  })
}

IgnoreReader.prototype.sort = function (a, b) {
  var aig = this.ignoreFiles.indexOf(a) !== -1
  , big = this.ignoreFiles.indexOf(b) !== -1
  if (aig && !big) return -1
  if (big && !aig) return 1
  return this._sort(a, b)
}

IgnoreReader.prototype._sort = function (a, b) {
  return 0
}

function alphasort (a, b) {
  return a === b ? 0
       : a.toLowerCase() > b.toLowerCase() ? 1
       : a.toLowerCase() < b.toLowerCase() ? -1
       : a > b ? 1
       : -1
}
<|endoftext|>


<commit_before>const { Configuration } = require('@schul-cloud/commons');
const { createChannel } = require('../../utils/rabbitmq');
const { buildAddUserMessage } = require('./utils');

const QUEUE_INTERNAL = Configuration.get('RABBITMQ_MATRIX_QUEUE_INTERNAL');
const QUEUE_EXTERNAL = Configuration.get('RABBITMQ_MATRIX_QUEUE_EXTERNAL');

let channel;

const handleMessage = async (incomingMessage) => {
	const content = JSON.parse(incomingMessage.content.toString());
	const addUserMessageObject = await buildAddUserMessage(content);
	const outgoingMessage = JSON.stringify(addUserMessageObject);
	channel.sendToQueue(QUEUE_EXTERNAL, Buffer.from(outgoingMessage), { persistent: true });
	channel.ack(incomingMessage);
};

const setup = async (app) => {
	channel = await createChannel();

	// internal queue
	channel.assertQueue(QUEUE_INTERNAL, {
		durable: true,
	});
	channel.prefetch(30);
	channel.consume(QUEUE_INTERNAL, handleMessage, {
		noAck: false,
	});

	// external queue
	channel.assertQueue(QUEUE_EXTERNAL, {
		durable: false,
	});
};

module.exports = setup;
<commit_msg>add validation<commit_after>const { Configuration } = require('@schul-cloud/commons');
const { createChannel } = require('../../utils/rabbitmq');
const { buildAddUserMessage } = require('./utils');

const QUEUE_INTERNAL = Configuration.get('RABBITMQ_MATRIX_QUEUE_INTERNAL');
const QUEUE_EXTERNAL = Configuration.get('RABBITMQ_MATRIX_QUEUE_EXTERNAL');

let channel;

const validateMessage = (content) => {
	if (!content.userId) return false;
	if (!(content.course || content.team || content.schoolSync)) return false;
	return true;
};

const handleMessage = async (incomingMessage) => {
	const content = JSON.parse(incomingMessage.content.toString());
	if (validateMessage) {
		const addUserMessageObject = await buildAddUserMessage(content);
		const outgoingMessage = JSON.stringify(addUserMessageObject);
		channel.sendToQueue(QUEUE_EXTERNAL, Buffer.from(outgoingMessage), { persistent: true });
		channel.ack(incomingMessage);
	}
	channel.nack(incomingMessage);
};

const setup = async (app) => {
	if (Configuration.get('FEATURE_RABBITMQ_ENABLED')) {
		channel = await createChannel();

		// internal queue
		channel.assertQueue(QUEUE_INTERNAL, {
			durable: true,
		});
		channel.prefetch(30);
		channel.consume(QUEUE_INTERNAL, handleMessage, {
			noAck: false,
		});

		// external queue
		channel.assertQueue(QUEUE_EXTERNAL, {
			durable: false,
		});
	}
};

module.exports = setup;
<|endoftext|>


<commit_before>/*!
Cloud4all Preferences Management Tools

Copyright 2013 Astea

Licensed under the New BSD license. You may not use this file except in
compliance with this License.

You may obtain a copy of the License at
https://github.com/GPII/prefsEditors/LICENSE.txt
*/

(function ($, fluid) {
    fluid.defaults("gpii.prefsEditor", {
        gradeNames: ["fluid.prefs.GPIIEditor", "autoInit"],
        prefsEditor: {
            gradeNames: ["fluid.prefs.stringBundle", "gpii.prefs.gpiiStore"],
            members: {
                messageResolver: "{prefsEditorLoader}.msgBundle"
            },
            events: {
                onLogin: null,
                onLogout: null,
                onRequestPageTransition: null,
                onPageTransition: {
                    events: {
                        onSetSuccess: "onSetSuccess",
                        onRequestPageTransition: "onRequestPageTransition"
                    },
                    args: ["{that}"]
                }
            },
            model: {
                userLoggedIn: false
            },
            listeners: {
                "onCreate.setUserToken": {
                    listener: "fluid.set",
                    args: ["{gpiiSession}", ["options", "loggedUser"], {
                        expander: {
                            funcName: "gpii.prefsEditor.getUserToken"
                        }
                    }]
                },
                "onReady.setATTRsaveButton": {
                    "this": "{that}.dom.saveButton",
                    "method": "attr",
                    "args": ["value", "{that}.stringBundle.saveAndApplyText"]
                },
                "onReady.onApplySettings": {
                    "this": "{that}.dom.saveAndApply",
                    "method": "click",
                    "args": ["{that}.applySettings"]
                },
                "onReady.fullEditorLink": {
                    "this": "{that}.dom.fullEditorLink",
                    "method": "click",
                    "args": ["{that}.events.onRequestPageTransition.fire"]
                },
                "onRequestPageTransition.save": {
                    listener: "{that}.saveSettings",
                    args: ["{that}.model"]
                },
                "onRequestPageTransition.goToPMT": {
                    "funcName": "fluid.set",
                    "args": [window, "location.href", "{prefsEditorLoader}.options.pmtUrl"]
                },
                "onReady.setInitialModel": {
                    listener: "gpii.prefsEditor.setInitialModel",
                    args: ["{that}"]
                },
                "onLogin.setUserLoggedIn": {
                    listener: "{that}.applier.requestChange",
                    args: ["userLoggedIn", true]
                },
                "onLogin.showSaveMessage": {
                    "this": "{that}.dom.messageLineLabel",
                    "method": "text",
                    "args": ["{that}.stringBundle.preferencesModified"]
                },
                "onLogin.showUserStatusBar": {
                    "listener": "{that}.showUserStatusBar"
                },
                "onReset.triggerLogoutEvent": {
                    "listener": "{that}.events.onLogout.fire"
                },
                "onLogout.setUserLoggedIn": {
                    listener: "{that}.applier.requestChange",
                    args: ["userLoggedIn", false]
                },
                "onLogout.clearMessage": {
                    "this": "{that}.dom.messageLineLabel",
                    "method": "text",
                    "args": [""]
                },
                "onReady.setSaveAndApplyButtonText": {
                    "this": "{that}.dom.saveAndApplyButtonLabel",
                    "method": "attr",
                    "args": ["value", "{that}.stringBundle.saveAndApplyText"]
                },
                "onReady.setFullEditorLinkText": {
                    "this": "{that}.dom.fullEditorLink",
                    "method": "text",
                    "args": ["{that}.stringBundle.fullEditorText"]
                },
                "onReady.setLogoutLinkText": {
                    "this": "{that}.dom.logoutLink",
                    "method": "text",
                    "args": ["{that}.stringBundle.logoutText"]
                }
            },
            invokers: {
                applySettings: {
                    "funcName": "gpii.applySettings",
                    "args": "{that}"
                },
                showUserStatusBar: {
                    "this": "{that}.dom.userStatusBar",
                    "method": "slideDown"
                },
                saveSettings: "{gpiiStore}.set"
            },
            selectors: {
                saveAndApply: ".flc-prefsEditor-save",
                saveAndApplyButtonLabel: ".flc-prefsEditor-save",
                messageLineLabel: ".gpiic-prefsEditor-messageLine",
                fullEditorLink: ".gpiic-prefsEditor-fullEditorLink",
                logoutLink: ".gpiic-prefsEditor-userLogoutLink"
            },
            selectorsToIgnore: ["saveAndApply"]
        }
    });

    gpii.prefsEditor.getUserToken = function (that) {
        return window.location.search.substring(1);
    };

    gpii.prefsEditor.setInitialModel = function (that) {
        var initialModel = that.get();
        that.applier.requestChange("", initialModel);
    };

    gpii.applySettings = function (that) {
        var savedSettings = that.modelTransform(that.model);
        if (that.socket) {
            that.socket.emit("message", savedSettings, fluid.log);
        } else {
            that.socket = that.socket || io.connect("http://localhost:8081/update");
            that.socket.on("connect", function (){
                that.socket.emit("message", savedSettings, fluid.log);
            });
            fluid.each(["error", "disconnect"], function (event) {
                that.socket.on(event, function (data) {
                    fluid.log(data);
                    that.socket.disconnect();
                    delete that.socket;
                });
            });
        }
    };

    gpii.prefsEditor.triggerEvent = function (that, targetSelector, event) {
        that.locate(targetSelector).trigger(event);
    };

})(jQuery, fluid);
<commit_msg>GPII-546: Removed unused event.<commit_after>/*!
Cloud4all Preferences Management Tools

Copyright 2013 Astea

Licensed under the New BSD license. You may not use this file except in
compliance with this License.

You may obtain a copy of the License at
https://github.com/GPII/prefsEditors/LICENSE.txt
*/

(function ($, fluid) {
    fluid.defaults("gpii.prefsEditor", {
        gradeNames: ["fluid.prefs.GPIIEditor", "autoInit"],
        prefsEditor: {
            gradeNames: ["fluid.prefs.stringBundle", "gpii.prefs.gpiiStore"],
            members: {
                messageResolver: "{prefsEditorLoader}.msgBundle"
            },
            events: {
                onLogin: null,
                onLogout: null,
                onRequestPageTransition: null
            },
            model: {
                userLoggedIn: false
            },
            listeners: {
                "onCreate.setUserToken": {
                    listener: "fluid.set",
                    args: ["{gpiiSession}", ["options", "loggedUser"], {
                        expander: {
                            funcName: "gpii.prefsEditor.getUserToken"
                        }
                    }]
                },
                "onReady.setATTRsaveButton": {
                    "this": "{that}.dom.saveButton",
                    "method": "attr",
                    "args": ["value", "{that}.stringBundle.saveAndApplyText"]
                },
                "onReady.onApplySettings": {
                    "this": "{that}.dom.saveAndApply",
                    "method": "click",
                    "args": ["{that}.applySettings"]
                },
                "onReady.fullEditorLink": {
                    "this": "{that}.dom.fullEditorLink",
                    "method": "click",
                    "args": ["{that}.events.onRequestPageTransition.fire"]
                },
                "onRequestPageTransition.save": {
                    listener: "{that}.saveSettings",
                    args: ["{that}.model"]
                },
                "onRequestPageTransition.goToPMT": {
                    "funcName": "fluid.set",
                    "args": [window, "location.href", "{prefsEditorLoader}.options.pmtUrl"]
                },
                "onReady.setInitialModel": {
                    listener: "gpii.prefsEditor.setInitialModel",
                    args: ["{that}"]
                },
                "onLogin.setUserLoggedIn": {
                    listener: "{that}.applier.requestChange",
                    args: ["userLoggedIn", true]
                },
                "onLogin.showSaveMessage": {
                    "this": "{that}.dom.messageLineLabel",
                    "method": "text",
                    "args": ["{that}.stringBundle.preferencesModified"]
                },
                "onLogin.showUserStatusBar": {
                    "listener": "{that}.showUserStatusBar"
                },
                "onReset.triggerLogoutEvent": {
                    "listener": "{that}.events.onLogout.fire"
                },
                "onLogout.setUserLoggedIn": {
                    listener: "{that}.applier.requestChange",
                    args: ["userLoggedIn", false]
                },
                "onLogout.clearMessage": {
                    "this": "{that}.dom.messageLineLabel",
                    "method": "text",
                    "args": [""]
                },
                "onReady.setSaveAndApplyButtonText": {
                    "this": "{that}.dom.saveAndApplyButtonLabel",
                    "method": "attr",
                    "args": ["value", "{that}.stringBundle.saveAndApplyText"]
                },
                "onReady.setFullEditorLinkText": {
                    "this": "{that}.dom.fullEditorLink",
                    "method": "text",
                    "args": ["{that}.stringBundle.fullEditorText"]
                },
                "onReady.setLogoutLinkText": {
                    "this": "{that}.dom.logoutLink",
                    "method": "text",
                    "args": ["{that}.stringBundle.logoutText"]
                }
            },
            invokers: {
                applySettings: {
                    "funcName": "gpii.applySettings",
                    "args": "{that}"
                },
                showUserStatusBar: {
                    "this": "{that}.dom.userStatusBar",
                    "method": "slideDown"
                },
                saveSettings: "{gpiiStore}.set"
            },
            selectors: {
                saveAndApply: ".flc-prefsEditor-save",
                saveAndApplyButtonLabel: ".flc-prefsEditor-save",
                messageLineLabel: ".gpiic-prefsEditor-messageLine",
                fullEditorLink: ".gpiic-prefsEditor-fullEditorLink",
                logoutLink: ".gpiic-prefsEditor-userLogoutLink"
            },
            selectorsToIgnore: ["saveAndApply"]
        }
    });

    gpii.prefsEditor.getUserToken = function (that) {
        return window.location.search.substring(1);
    };

    gpii.prefsEditor.setInitialModel = function (that) {
        var initialModel = that.get();
        that.applier.requestChange("", initialModel);
    };

    gpii.applySettings = function (that) {
        var savedSettings = that.modelTransform(that.model);
        if (that.socket) {
            that.socket.emit("message", savedSettings, fluid.log);
        } else {
            that.socket = that.socket || io.connect("http://localhost:8081/update");
            that.socket.on("connect", function (){
                that.socket.emit("message", savedSettings, fluid.log);
            });
            fluid.each(["error", "disconnect"], function (event) {
                that.socket.on(event, function (data) {
                    fluid.log(data);
                    that.socket.disconnect();
                    delete that.socket;
                });
            });
        }
    };

    gpii.prefsEditor.triggerEvent = function (that, targetSelector, event) {
        that.locate(targetSelector).trigger(event);
    };

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


<commit_before>#!/usr/bin/env node

var Benchmark = require("benchmark"),
    sprintf = require("sprintf").sprintf;

var Graph = require("graphlib").Graph,
    rank = require("../lib/rank"),
    layout = require("..").layout;

function runBenchmark(name, fn) {
  var options = {};
  options.onComplete = function(bench) {
    var target = bench.target,
        hz = target.hz,
        stats = target.stats,
        rme = stats.rme,
        samples = stats.sample.length,
        msg = sprintf("    %25s: %13s ops/sec \xb1 %s%% (%3d run(s) sampled)",
                      target.name,
                      Benchmark.formatNumber(hz.toFixed(2)),
                      rme.toFixed(2),
                      samples);
    console.log(msg);
  };
  options.onError = function(bench) {
    console.error("    " + bench.target.error);
  };
  options.setup = function() {
    this.count = Math.random() * 1000;
    this.nextInt = function(range) {
      return Math.floor(this.count++ % range );
    };
  };
  new Benchmark(name, fn, options).run();
}

var g = new Graph()
  .setDefaultNodeLabel(function() { return {}; })
  .setDefaultEdgeLabel(function() { return { minlen: 1, weight: 1 }; })
  .setPath(["a", "b", "c", "d", "h"])
  .setPath(["a", "e", "g", "h"])
  .setPath(["a", "f", "g"]);

runBenchmark("longest-path ranker", function() {
  rank(g, "longest-path");
});

runBenchmark("tight-tree ranker", function() {
  rank(g, "tight-tree");
});

runBenchmark("network-simplex ranker", function() {
  rank(g, "network-simplex");
});

runBenchmark("layout", function() {
  layout(g);
});
<commit_msg>Add width and height to benchmark<commit_after>#!/usr/bin/env node

var Benchmark = require("benchmark"),
    sprintf = require("sprintf").sprintf;

var Graph = require("graphlib").Graph,
    rank = require("../lib/rank"),
    layout = require("..").layout;

function runBenchmark(name, fn) {
  var options = {};
  options.onComplete = function(bench) {
    var target = bench.target,
        hz = target.hz,
        stats = target.stats,
        rme = stats.rme,
        samples = stats.sample.length,
        msg = sprintf("    %25s: %13s ops/sec \xb1 %s%% (%3d run(s) sampled)",
                      target.name,
                      Benchmark.formatNumber(hz.toFixed(2)),
                      rme.toFixed(2),
                      samples);
    console.log(msg);
  };
  options.onError = function(bench) {
    console.error("    " + bench.target.error);
  };
  options.setup = function() {
    this.count = Math.random() * 1000;
    this.nextInt = function(range) {
      return Math.floor(this.count++ % range );
    };
  };
  new Benchmark(name, fn, options).run();
}

var g = new Graph()
  .setDefaultNodeLabel(function() { return { width: 1, height: 1}; })
  .setDefaultEdgeLabel(function() { return { minlen: 1, weight: 1 }; })
  .setPath(["a", "b", "c", "d", "h"])
  .setPath(["a", "e", "g", "h"])
  .setPath(["a", "f", "g"]);

runBenchmark("longest-path ranker", function() {
  rank(g, "longest-path");
});

runBenchmark("tight-tree ranker", function() {
  rank(g, "tight-tree");
});

runBenchmark("network-simplex ranker", function() {
  rank(g, "network-simplex");
});

runBenchmark("layout", function() {
  layout(g);
});
<|endoftext|>


<commit_before>var net = require('net');
var commands = require('./commands.js');

    var BIRCDServer = function(options, connectionListener) {
        var connectionListenerWrapper = function(c) {

            // Preamble
            var clientId = c.remoteAddress + ':' + c.remotePort;
              console.log(clientId + ' connected.');
              c.setTimeout(30000, function() {
                 console.log(clientId + ' timed out.');
              });
              c.setKeepAlive(true, 0);
              c.on('end', function() {
                console.log(clientId + ' disconnected.');
              });
              c.on('data', function(buffer) {
                 console.log(buffer);
              });

            // IRC Logic

              c.write('hello\r\n');
              c.pipe(c);

            // New connection
            if (connectionListener && 'function' == typeof(connectionListener))
            {
                connectionListener(this);
            }
        };

        var server = net.createServer(options, connectionListenerWrapper);
        server.on('error', function (e) {
          if (e.code == 'EADDRINUSE') {
            console.log('Address in use, retrying...');
            setTimeout(function () {
              server.close();
              server.listen(this.port, this.address);
            }, 1000);
          }
        });

        this.server = server;
    };

    BIRCDServer.prototype.listen = function (port, address) {
        var self = this;
        self.port = port;
        self.address = address;
        self.server.listen(port, address, function() {
            self.port = this.address().port;
            self.address = this.address().address;
            console.log('bIRCd instance successfully bound to ' + self.address + ':' + self.port)
        })
    }

exports.createServer = function(options, connectionListener)
{
    return new BIRCDServer(options, connectionListener);
}<commit_msg>Implemented low-level message parser for IRC RFC 2459.<commit_after>var net = require('net');
var commands = require('./commands.js');

    var BIRCSession = function(socket) {
        var self = this;
        self.bufferSize = 512;
        self.buffer = new Buffer(this.bufferSize);
        self.size = 0;

        var getLastByteOnBuffer = function() {
            if (this.size === 0) {
                return null;
            }

           var lastByteIndex = (this.position + this.size) % this.bufferSize;
           return this.buffer[lastByteIndex];
        };

        var appendChunkToBuffer = function(chunk) {
            if (chunk.length + self.size > self.bufferSize) {
                throw 'buffer overflow';
            }

            chunk.copy(self.buffer, self.size);
            self.size += chunk.length;
        }

        var readToSpace = function(buffer, position) {
            var index = position;
            while (index < buffer.length && buffer[index] != 0x20) {
                index++;
            }
            var endOfData = index;

            while (index < buffer.length && buffer[index] == 0x20) {
                index++;
            }

            return { buffer: buffer.slice(position, endOfData), index: index };
        }

        var parseCommand = function(buffer)
        {
            var param;
            var readResult;
            var prefix = null;
            var command = null;
            var params = [];
            var index = 0;
            if (buffer[index] === 0x3A)
            {
                readResult = readToSpace(buffer, ++index);
                prefix = readResult.buffer.toString('utf8');
                index = readResult.index;
            }

            readResult = readToSpace(buffer, index);
            command = readResult.buffer.toString('utf8');
            index = readResult.index;

            while (index !== buffer.length)
            {
                if (buffer[index] === 0x3A) {
                    index++;
                    param = buffer.slice(index, buffer.length);
                    index = buffer.length;
                }
                else {
                    readResult = readToSpace(buffer, index);
                    param = readResult.buffer;
                    index = readResult.index;
                }

                params.push(param.toString('utf8'));
            }

            return { prefix: prefix, command: command, params: params };
        }

        var clearBuffer = function() {
            self.size = 0;
        };

        // Preamble
        var clientId = socket.remoteAddress + ':' + socket.remotePort;
          console.log(clientId + ' connected.');
          socket.setTimeout(30000, function() {
             console.log(clientId + ' timed out.');
          });
          socket.setKeepAlive(true, 0);
          socket.on('end', function() {
            console.log(clientId + ' disconnected.');
          });
          socket.on('data', function(chunk) {
              // Check for CR LF
              var startOfChunk = 0;
              for (var i = 0; i < chunk.length; i++)
              {
                  if (chunk[i] === 0x0A) {
                    if (i === 0) {
                        // Check last byte on buffer
                        if (getLastByteOnBuffer() == 0x0D)
                        {
                            // Concatenate with buffer and process command
                            appendChunkToBuffer(chunk.slice(0, i));
                            var command = parseCommand(self.buffer.slice(0, self.size - 2 + 1));
                            console.log(command);
                            clearBuffer();
                            startOfChunk = i + 1
                        }
                        else
                        {
                            throw 'unexpected line feed character.';
                        }
                    }
                    else
                    {
                        if (chunk[i-1] === 0x0D)
                        {
                            // Concatenate with buffer and process command
                            appendChunkToBuffer(chunk.slice(0, i));
                            var command = parseCommand(self.buffer.slice(0, self.size - 2 + 1));
                            console.log(command);
                            clearBuffer();
                            startOfChunk = i + 1;
                        }
                        else
                        {
                            // TODO: ERROR!
                            throw 'unexpected line feed character.';
                        }
                    }
                  }
              }

              if (startOfChunk < chunk.length) {
                appendChunkToBuffer(chunk.slice(startOfChunk, chunk.length - startOfChunk));
              }
          });
    };

    var BIRCDServer = function(options, connectionListener) {
        var self = this;
        self.sessions = [];
        var connectionListenerWrapper = function(c) {

            var session = new BIRCSession(c);
            self.sessions.push(session);

            // New connection
            if (connectionListener && 'function' == typeof(connectionListener))
            {
                connectionListener(session);
            }
        };

        var server = net.createServer(options, connectionListenerWrapper);
        server.on('error', function (e) {
          if (e.code == 'EADDRINUSE') {
            console.log('Address in use, retrying...');
            setTimeout(function () {
              server.close();
              server.listen(this.port, this.address);
            }, 1000);
          }
        });

        this.server = server;
    };

    BIRCDServer.prototype.listen = function (port, address) {
        var self = this;
        self.port = port;
        self.address = address;
        self.server.listen(port, address, function() {
            self.port = this.address().port;
            self.address = this.address().address;
            console.log('bIRCd instance successfully bound to ' + self.address + ':' + self.port)
        })
    }

exports.createServer = function(options, connectionListener)
{
    return new BIRCDServer(options, connectionListener);
}<|endoftext|>


<commit_before>import Sequelize from 'sequelize';
import {DIALECTS} from '../app/constants/constants';
import parse from './parse';
import {merge} from 'ramda';
import {ARGS} from './args';
import {APP_NOT_CONNECTED, AUTHENTICATION} from './errors';
import YAML from 'yamljs';

const PREBUILT_QUERY = {
    SHOW_DATABASES: 'SHOW_DATABASES',
    SHOW_TABLES: 'SHOW_TABLES',
    SHOW5ROWS: 'SHOW5ROWS'
};

const timestamp = () => (new Date()).toTimeString();

const EMPTY_TABLE = {
    columnnames: ['NA'],
    rows: [['empty table']],
    ncols: 1,
    nrows: 1
};

const isEmpty = (table) => {
    return table.length === 0;
};

const intoArray = (objects) => {
    return objects.map(obj => obj[Object.keys(obj)]);
};

const assembleTablesPreviewMessage = (tablePreviews) => {

    /*
        topRows is an array of one or many responses of top 5 rows queries
        [ {'table1':[top5rows]}, {'table2': [top5rows]} ...]
    */

    let parsedRows;

    return tablePreviews.map( (tablePreview) => {
        const tableName = Object.keys(tablePreview);
        const rawData = tablePreview[tableName];
        parsedRows = (isEmpty(rawData)) ? EMPTY_TABLE : parse(rawData);
        return {[tableName]: parsedRows};
    });

};

export class SequelizeManager {

    constructor(Logger) {
        this.log = Logger.log;
    }

    getDialect() {
        return this.connection.options.dialect;
    }

    setQueryType(type) {
        /*
         * set sequelize's query property type
         * helps sequelize to predict what kind of response it will get
         */
        return {type: this.connection.QueryTypes[type]};
    }

    intoTablesArray(results) {

        let tables;

        if (this.getDialect() === DIALECTS.SQLITE) {
            // sqlite returns an array by default
            tables = results;
        } else {
            /*
             * others return list of objects such as
             *  [ { Tables_in_testdb: 'consumercomplaints' },
             *    { Tables_in_testdb: 'test' } ]
             */
            tables = intoArray(results);
        }

        return tables;

    }

    getConnection(responseSender) {
        return () => responseSender({error: null});
    }

    createConnection(configuration) {

        const {
            username, password, database, port,
            dialect, storage, host
            } = configuration;

        this.log(`Creating a connection for user ${username}`, 1);

        let redshiftOptions = null;
        if (this.connection.config.dialect === 'redshift') {
            // http://stackoverflow.com/questions/32037385/using-sequelize-with-redshift
            // avoid auto-detection and typarray/pg_type error
            Sequelize.HSTORE.types.postgres.oids.push('dummy');
            this.connection.config.dialect = 'postgres';
            redshiftOptions = {
                pool: false,
                keepDefaultTimezone: true, // avoid SET TIMEZONE
                databaseVersion: '8.0.2' // avoid SHOW SERVER_VERSION
            };
        }

        this.connection = new Sequelize(database, username, password, {
            dialect,
            host,
            port,
            storage
        }, redshiftOptions);

        if (this.connection.config.dialect === 'mssql') {
            this.connection.config.dialectOptions = {encrypt: true};
        }

    }

    connect(configFromApp) {

        if (ARGS.headless) {
            const configFromFile = YAML.load(ARGS.configpath);
            /*
             * if server is sending a headless app a new database,
             * use that one instead of the one in the config file
             */
            if (configFromApp.database) {
                configFromFile.database = configFromApp.database;
            }
            this.createConnection(configFromFile);
        } else {
            this.createConnection(configFromApp);
        }

        return this.connection.authenticate();

    }

    authenticate(responseSender) {

        this.log('Authenticating connection.');
        // when already logged in and simply want to check connection

        if (!this.connection) {
			this.raiseError(
                merge(
                    {message: APP_NOT_CONNECTED},
                    {type: 'connection'}
                ),
                responseSender
			);
		} else {
            // this.connection.authenticate() returns a promise
            return this.connection.authenticate()
            .catch((error) => {
                this.raiseError(
                    merge(
                        {mesage: AUTHENTICATION(error)},
                        {type: 'connection'}),
                    responseSender
                );
            });
        }

    }

    raiseError(errorMessage, responseSender) {
        const errorLog = merge(errorMessage, {timestamp: timestamp()});
        this.log(errorMessage, 0);
        responseSender({error: errorLog}, 400);
    }

    showDatabases(responseSender) {

        const query = this.getPresetQuery(PREBUILT_QUERY.SHOW_DATABASES);
        const dialect = this.getDialect();

        // deal with sqlite -> has no databases list
        if (dialect === DIALECTS.SQLITE) {
            responseSender({
                databases: ['SQLITE database accessed'],
                error: null,
                tables: null
            });
            // skip SHOW_DATABASES query and send SHOW_TABLES query right away
            return this.showTables(responseSender);
        }

        this.log(`Querying: ${query}`, 1);

        return () => this.connection.query(query, this.setQueryType('SELECT'))
        .then(results => {
            this.log('Results recieved.', 1);
            responseSender({
                databases: intoArray(results),
                error: null,
                /*
                    if user wants to see all databases/schemes, clear
                    tables from previously selected database/schemes
                */
                tables: null
            });
        });

    }

    // built-in query to show available tables in a database/scheme
    showTables(responseSender) {

        const showtables = this.getPresetQuery(PREBUILT_QUERY.SHOW_TABLES);
        this.log(`Querying: ${showtables}`, 1);

        return () => this.connection
        .query(showtables, this.setQueryType('SELECT'))
        .then(results => {
            this.log('Results received', 1);
            // TODO: when switching fornt end to v1, simply send back an array
            const tablesObject = this.intoTablesArray(results).map(table => {
                return {[table]: {}};
            });

            responseSender({
                error: null,
                tables: tablesObject
            });
        });

    }

    previewTables(tables, responseSender) {

        const promises = tables.map(table => {

            const show5rows = this.getPresetQuery(
                PREBUILT_QUERY.SHOW5ROWS, table
            );
            this.log(`Querying: ${show5rows}`, 1);

            // sends the query for a single table
            return this.connection
            .query(show5rows, this.setQueryType('SELECT'))
            .then(selectTableResults => {
                return {
                    [table]: selectTableResults
                };
            });

        });

        return Promise.all(promises)
        .then(tablePreviews => {
            this.log('Sending tables\' previews.', 1);
            responseSender({
                error: null,
                previews: assembleTablesPreviewMessage(tablePreviews)
            });
        });

    }

    sendRawQuery(query, responseSender) {

        this.log(`Querying: ${query}`, 1);

        return this.connection.query(query, this.setQueryType('SELECT'))
        .catch( error => {
            this.raiseError(error, responseSender);
        })
        .then((results) => {
            this.log('Results received.', 1);
            responseSender(merge(parse(results), {error: null}));
        });

    }

    disconnect(responseSender) {

        /*
            this.connection.close() does not return a promise for now.
            open issue here:
            https://github.com/sequelize/sequelize/pull/5776
        */

        this.log('Disconnecting', 1);
        this.connection.close();
        responseSender({
            databases: null, error: null, tables: null, previews: null
        });

    }

    getPresetQuery(showQuerySelector, table = null) {

        const dialect = this.getDialect();

        switch (showQuerySelector) {

            case PREBUILT_QUERY.SHOW_DATABASES:
                switch (dialect) {
                    case DIALECTS.MYSQL:
                    case DIALECTS.SQLITE:
                    case DIALECTS.MARIADB:
                        return 'SHOW DATABASES';
                    case DIALECTS.POSTGRES:
                        return 'SELECT datname AS database FROM ' +
                        'pg_database WHERE datistemplate = false;';
                    case DIALECTS.MSSQL:
                        return 'SELECT name FROM Sys.Databases';
                    default:
                        throw new Error('could not build a presetQuery');
                }

            case PREBUILT_QUERY.SHOW_TABLES:
                switch (dialect) {
                    case DIALECTS.MYSQL:
                    case DIALECTS.MARIADB:
                        return 'SHOW TABLES';
                    case DIALECTS.POSTGRES:
                        return 'SELECT table_name FROM ' +
                            'information_schema.tables WHERE ' +
                            'table_schema = \'public\'';
                    case DIALECTS.MSSQL:
                        return 'SELECT TABLE_NAME FROM ' +
                            'information_schema.tables';
                    case DIALECTS.SQLITE:
                        return 'SELECT name FROM ' +
                        'sqlite_master WHERE type="table"';
                    default:
                        throw new Error('could not build a presetQuery');
                }

            case PREBUILT_QUERY.SHOW5ROWS:
                switch (dialect) {
                    case DIALECTS.MYSQL:
                    case DIALECTS.SQLITE:
                    case DIALECTS.MARIADB:
                    case DIALECTS.POSTGRES:
                        return `SELECT * FROM ${table} LIMIT 5`;
                    case DIALECTS.MSSQL:
                        return 'SELECT TOP 5 * FROM ' +
                            `${this.connection.config.database}.dbo.${table}`;
                    default:
                        throw new Error('could not build a presetQuery');
                }

            default: {
                throw new Error('could not build a presetQuery');
            }

        }
    }
}

// need this in main, can't import directly due to circular dependancy
export const OPTIONS = ARGS;
<commit_msg>:wrench: rework options for sequelize connection<commit_after>import Sequelize from 'sequelize';
import {DIALECTS} from '../app/constants/constants';
import parse from './parse';
import {merge} from 'ramda';
import {ARGS} from './args';
import {APP_NOT_CONNECTED, AUTHENTICATION} from './errors';
import YAML from 'yamljs';

const PREBUILT_QUERY = {
    SHOW_DATABASES: 'SHOW_DATABASES',
    SHOW_TABLES: 'SHOW_TABLES',
    SHOW5ROWS: 'SHOW5ROWS'
};

const timestamp = () => (new Date()).toTimeString();

const EMPTY_TABLE = {
    columnnames: ['NA'],
    rows: [['empty table']],
    ncols: 1,
    nrows: 1
};

const isEmpty = (table) => {
    return table.length === 0;
};

const intoArray = (objects) => {
    return objects.map(obj => obj[Object.keys(obj)]);
};

const assembleTablesPreviewMessage = (tablePreviews) => {

    /*
        topRows is an array of one or many responses of top 5 rows queries
        [ {'table1':[top5rows]}, {'table2': [top5rows]} ...]
    */

    let parsedRows;

    return tablePreviews.map( (tablePreview) => {
        const tableName = Object.keys(tablePreview);
        const rawData = tablePreview[tableName];
        parsedRows = (isEmpty(rawData)) ? EMPTY_TABLE : parse(rawData);
        return {[tableName]: parsedRows};
    });

};

export class SequelizeManager {

    constructor(Logger) {
        this.log = Logger.log;
    }

    getDialect() {
        return this.connection.options.dialect;
    }

    setQueryType(type) {
        /*
         * set sequelize's query property type
         * helps sequelize to predict what kind of response it will get
         */
        return {type: this.connection.QueryTypes[type]};
    }

    intoTablesArray(results) {

        let tables;

        if (this.getDialect() === DIALECTS.SQLITE) {
            // sqlite returns an array by default
            tables = results;
        } else {
            /*
             * others return list of objects such as
             *  [ { Tables_in_testdb: 'consumercomplaints' },
             *    { Tables_in_testdb: 'test' } ]
             */
            tables = intoArray(results);
        }

        return tables;

    }

    getConnection(responseSender) {
        return () => responseSender({error: null});
    }

    createConnection(configuration) {

        let {
            username, password, database, port,
            dialect, storage, host
            } = configuration;

        let options = {
            dialect,
            host,
            port,
            storage
        };

        this.log(`Creating a connection for user ${username}`, 1);

        let redshiftOptions = null;
        if (dialect === 'redshift') {
            // http://stackoverflow.com/questions/32037385/using-sequelize-with-redshift
            Sequelize.HSTORE.types.postgres.oids.push('dummy');
            redshiftOptions = {
                dialect: 'postgres',
                pool: false,
                keepDefaultTimezone: true, // avoid SET TIMEZONE
                databaseVersion: '8.0.2' // avoid SHOW SERVER_VERSION
            };
            options = merge(options, redshiftOptions);
        }

        this.connection = new Sequelize(database, username, password, options);

        if (this.connection.config.dialect === 'mssql') {
            this.connection.config.dialectOptions = {encrypt: true};
        }

    }

    connect(configFromApp) {

        if (ARGS.headless) {
            const configFromFile = YAML.load(ARGS.configpath);
            /*
             * if server is sending a headless app a new database,
             * use that one instead of the one in the config file
             */
            if (configFromApp.database) {
                configFromFile.database = configFromApp.database;
            }
            this.createConnection(configFromFile);
        } else {
            this.createConnection(configFromApp);
        }

        return this.connection.authenticate();

    }

    authenticate(responseSender) {

        this.log('Authenticating connection.');
        // when already logged in and simply want to check connection

        if (!this.connection) {
			this.raiseError(
                merge(
                    {message: APP_NOT_CONNECTED},
                    {type: 'connection'}
                ),
                responseSender
			);
		} else {
            // this.connection.authenticate() returns a promise
            return this.connection.authenticate()
            .catch((error) => {
                this.raiseError(
                    merge(
                        {mesage: AUTHENTICATION(error)},
                        {type: 'connection'}),
                    responseSender
                );
            });
        }

    }

    raiseError(errorMessage, responseSender) {
        const errorLog = merge(errorMessage, {timestamp: timestamp()});
        this.log(errorMessage, 0);
        responseSender({error: errorLog}, 400);
    }

    showDatabases(responseSender) {

        const query = this.getPresetQuery(PREBUILT_QUERY.SHOW_DATABASES);
        const dialect = this.getDialect();

        // deal with sqlite -> has no databases list
        if (dialect === DIALECTS.SQLITE) {
            responseSender({
                databases: ['SQLITE database accessed'],
                error: null,
                tables: null
            });
            // skip SHOW_DATABASES query and send SHOW_TABLES query right away
            return this.showTables(responseSender);
        }

        this.log(`Querying: ${query}`, 1);

        return () => this.connection.query(query, this.setQueryType('SELECT'))
        .then(results => {
            this.log('Results recieved.', 1);
            responseSender({
                databases: intoArray(results),
                error: null,
                /*
                    if user wants to see all databases/schemes, clear
                    tables from previously selected database/schemes
                */
                tables: null
            });
        });

    }

    // built-in query to show available tables in a database/scheme
    showTables(responseSender) {

        const showtables = this.getPresetQuery(PREBUILT_QUERY.SHOW_TABLES);
        this.log(`Querying: ${showtables}`, 1);

        return () => this.connection
        .query(showtables, this.setQueryType('SELECT'))
        .then(results => {
            this.log('Results received', 1);
            // TODO: when switching fornt end to v1, simply send back an array
            const tablesObject = this.intoTablesArray(results).map(table => {
                return {[table]: {}};
            });

            responseSender({
                error: null,
                tables: tablesObject
            });
        });

    }

    previewTables(tables, responseSender) {

        const promises = tables.map(table => {

            const show5rows = this.getPresetQuery(
                PREBUILT_QUERY.SHOW5ROWS, table
            );
            this.log(`Querying: ${show5rows}`, 1);

            // sends the query for a single table
            return this.connection
            .query(show5rows, this.setQueryType('SELECT'))
            .then(selectTableResults => {
                return {
                    [table]: selectTableResults
                };
            });

        });

        return Promise.all(promises)
        .then(tablePreviews => {
            this.log('Sending tables\' previews.', 1);
            responseSender({
                error: null,
                previews: assembleTablesPreviewMessage(tablePreviews)
            });
        });

    }

    sendRawQuery(query, responseSender) {

        this.log(`Querying: ${query}`, 1);

        return this.connection.query(query, this.setQueryType('SELECT'))
        .catch( error => {
            this.raiseError(error, responseSender);
        })
        .then((results) => {
            this.log('Results received.', 1);
            responseSender(merge(parse(results), {error: null}));
        });

    }

    disconnect(responseSender) {

        /*
            this.connection.close() does not return a promise for now.
            open issue here:
            https://github.com/sequelize/sequelize/pull/5776
        */

        this.log('Disconnecting', 1);
        this.connection.close();
        responseSender({
            databases: null, error: null, tables: null, previews: null
        });

    }

    getPresetQuery(showQuerySelector, table = null) {

        const dialect = this.getDialect();

        switch (showQuerySelector) {

            case PREBUILT_QUERY.SHOW_DATABASES:
                switch (dialect) {
                    case DIALECTS.MYSQL:
                    case DIALECTS.SQLITE:
                    case DIALECTS.MARIADB:
                        return 'SHOW DATABASES';
                    case DIALECTS.POSTGRES:
                        return 'SELECT datname AS database FROM ' +
                        'pg_database WHERE datistemplate = false;';
                    case DIALECTS.MSSQL:
                        return 'SELECT name FROM Sys.Databases';
                    default:
                        throw new Error('could not build a presetQuery');
                }

            case PREBUILT_QUERY.SHOW_TABLES:
                switch (dialect) {
                    case DIALECTS.MYSQL:
                    case DIALECTS.MARIADB:
                        return 'SHOW TABLES';
                    case DIALECTS.POSTGRES:
                        return 'SELECT table_name FROM ' +
                            'information_schema.tables WHERE ' +
                            'table_schema = \'public\'';
                    case DIALECTS.MSSQL:
                        return 'SELECT TABLE_NAME FROM ' +
                            'information_schema.tables';
                    case DIALECTS.SQLITE:
                        return 'SELECT name FROM ' +
                        'sqlite_master WHERE type="table"';
                    default:
                        throw new Error('could not build a presetQuery');
                }

            case PREBUILT_QUERY.SHOW5ROWS:
                switch (dialect) {
                    case DIALECTS.MYSQL:
                    case DIALECTS.SQLITE:
                    case DIALECTS.MARIADB:
                    case DIALECTS.POSTGRES:
                        return `SELECT * FROM ${table} LIMIT 5`;
                    case DIALECTS.MSSQL:
                        return 'SELECT TOP 5 * FROM ' +
                            `${this.connection.config.database}.dbo.${table}`;
                    default:
                        throw new Error('could not build a presetQuery');
                }

            default: {
                throw new Error('could not build a presetQuery');
            }

        }
    }
}

// need this in main, can't import directly due to circular dependancy
export const OPTIONS = ARGS;
<|endoftext|>