<commit_before>//Translated from goban.rb using babyruby2js
'use strict';

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

var Grid = require('./Grid');
var Stone = require('./Stone');
var Group = require('./Group');
// Stores what we have on the board (namely, the stones and the empty spaces).
// - Giving coordinates, a Goban can return an existing stone.
// - It also remembers the list of stones played and can share this info for undo feature.
// - For console game and debug features, a goban can also "draw" its content as text.
// See Stone and Group classes for the layer above this.
//public read-only attribute: gsize, grid, scoring_grid, merged_groups, killed_groups, garbage_groups;

/** @class */
function Goban(gsize) {
    if (gsize === undefined) gsize = 19;
    this.gsize = gsize;
    this.grid = new Grid(gsize);
    this.scoring_grid = new Grid(gsize);
    this.ban = this.grid.yx;
    var i, j;
    for (j = 1; j <= gsize; j++) {
        for (i = 1; i <= gsize; i++) {
            this.ban[j][i] = new Stone(this, i, j, main.EMPTY);
        }
    }
    for (j = 1; j <= gsize; j++) {
        for (i = 1; i <= gsize; i++) {
            this.ban[j][i].find_neighbors();
        }
    }
    // sentinel for group list searches; NB: values like -100 helps detecting bugs when value is used by mistake
    Goban.sentinel = new Group(this, new Stone(this, -50, -50, main.EMPTY), -100, 0);
    this.killed_groups = [Goban.sentinel]; // so that we can always do @killed_groups.last.color, etc.
    this.merged_groups = [Goban.sentinel];
    this.garbage_groups = [];
    this.num_groups = 0;
    this.history = [];
}
module.exports = Goban;

// Prepares the goban for another game (same size, same number of players)
Goban.prototype.clear = function () {
    for (var j = 1; j <= this.gsize; j++) {
        for (var i = 1; i <= this.gsize; i++) {
            var stone = this.ban[j][i];
            if (stone.group) {
                stone.group.clear();
            }
        }
    }
    // Collect all the groups and put them into @garbage_groups
    this.killed_groups.shift(); // removes @@sentinel
    this.merged_groups.shift(); // removes @@sentinel
    this.garbage_groups.concat(this.killed_groups);
    this.garbage_groups.concat(this.merged_groups);
    this.killed_groups.clear();
    this.killed_groups.push(Goban.sentinel);
    this.merged_groups.clear();
    this.merged_groups.push(Goban.sentinel);
    this.num_groups = 0;
    return this.history.clear();
};

// Allocate a new group or recycles one from garbage list.
// For efficiency, call this one, do not call the regular Group.new method.
Goban.prototype.new_group = function (stone, lives) {
    var group = this.garbage_groups.pop();
    if (group) {
        return group.recycle(stone, lives);
    } else {
        this.num_groups += 1;
        return new Group(this, stone, lives, this.num_groups);
    }
};

Goban.prototype.image = function () {
    return this.grid.to_text2(false, ',', function (s) {
        return Grid.COLOR_CHARS[s.color];
    }).chop();
};

// For debugging only
Goban.prototype.debug_display = function () {
    console.log('Board:');
    console.log(this.grid.to_text());
    console.log('Groups:');
    console.log(this.grid.to_text(function (s) {
        if (s.group) {
            return '' + s.group.ndx;
        } else {
            return '.';
        }
    }));
    console.log('Full info on groups and stones:');
    var groups = {};
    for (var row, row_array = this.grid.yx, row_ndx = 0; row=row_array[row_ndx], row_ndx < row_array.length; row_ndx++) {
        for (var s, s_array = row, s_ndx = 0; s=s_array[s_ndx], s_ndx < s_array.length; s_ndx++) {
            if (s && s.group) {
                groups[s.group.ndx] = s.group;
            }
        }
    }
    for (var ndx = 1; ndx <= this.num_groups; ndx++) {
        if (groups[ndx]) {
            console.log(groups[ndx].debug_dump());
        }
    }
};

// This display is for debugging and text-only game
Goban.prototype.console_display = function () {
    console.log(this.grid.to_text(function (s) {
        return Grid.COLOR_CHARS[s.color];
    }));
};

// Basic validation only: coordinates and checks the intersection is empty
// See Stone class for evolved version of this (calling this one)
Goban.prototype.valid_move = function (i, j) {
    if (i < 1 || i > this.gsize || j < 1 || j > this.gsize) {
        return false;
    }
    return this.ban[j][i].empty();
};

Goban.prototype.stone_at = function (i, j) {
    return this.ban[j][i];
};

Goban.prototype.color = function (i, j) {
    var stone = this.ban[j][i];
    if (stone) {
        return stone.color;
    } // works because BORDER == nil
    return main.BORDER;
};

// No validity test here
Goban.prototype.empty = function (i, j) {
    return this.ban[j][i].empty();
};

Goban.prototype.move_number = function () {
    return this.history.length;
};

// Plays a stone and stores it in history
// Actually we simply return the existing stone and the caller will update it
Goban.prototype.play_at = function (i, j) {
    var stone = this.ban[j][i];
    if (stone.color !== main.EMPTY) {
        throw new Error('Tried to play on existing stone in ' + i + ',' + j);
    }
    this.history.push(stone);
    return stone;
};

// Removes the last stone played from the board
// Actually we simply return the existing stone and the caller will update it
Goban.prototype.undo = function () {
    return this.history.pop();
};

Goban.prototype.previous_stone = function () {
    return this.history[this.history.length-1];
};
<commit_msg>More camelCase<commit_after>//Translated from goban.rb using babyruby2js
'use strict';

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

var Grid = require('./Grid');
var Stone = require('./Stone');
var Group = require('./Group');
// Stores what we have on the board (namely, the stones and the empty spaces).
// - Giving coordinates, a Goban can return an existing stone.
// - It also remembers the list of stones played and can share this info for undo feature.
// - For console game and debug features, a goban can also "draw" its content as text.
// See Stone and Group classes for the layer above this.
//public read-only attribute: gsize, grid, scoringGrid, mergedGroups, killedGroups, garbageGroups;

/** @class */
function Goban(gsize) {
    if (gsize === undefined) gsize = 19;
    this.gsize = gsize;
    this.grid = new Grid(gsize);
    this.scoringGrid = new Grid(gsize);
    this.ban = this.grid.yx;
    var i, j;
    for (var j = 1; j <= gsize; j++) {
        for (var i = 1; i <= gsize; i++) {
            this.ban[j][i] = new Stone(this, i, j, main.EMPTY);
        }
    }
    for (var j = 1; j <= gsize; j++) {
        for (var i = 1; i <= gsize; i++) {
            this.ban[j][i].findNeighbors();
        }
    }
    // sentinel for group list searches; NB: values like -100 helps detecting bugs when value is used by mistake
    Goban.sentinel = new Group(this, new Stone(this, -50, -50, main.EMPTY), -100, 0);
    this.killedGroups = [Goban.sentinel]; // so that we can always do @killed_groups.last.color, etc.
    this.mergedGroups = [Goban.sentinel];
    this.garbageGroups = [];
    this.numGroups = 0;
    this.history = [];
}
module.exports = Goban;

// Prepares the goban for another game (same size, same number of players)
Goban.prototype.clear = function () {
    for (var j = 1; j <= this.gsize; j++) {
        for (var i = 1; i <= this.gsize; i++) {
            var stone = this.ban[j][i];
            if (stone.group) {
                stone.group.clear();
            }
        }
    }
    // Collect all the groups and put them into @garbage_groups
    this.killedGroups.shift(); // removes @@sentinel
    this.mergedGroups.shift(); // removes @@sentinel
    this.garbageGroups.concat(this.killedGroups);
    this.garbageGroups.concat(this.mergedGroups);
    this.killedGroups.clear();
    this.killedGroups.push(Goban.sentinel);
    this.mergedGroups.clear();
    this.mergedGroups.push(Goban.sentinel);
    this.numGroups = 0;
    return this.history.clear();
};

// Allocate a new group or recycles one from garbage list.
// For efficiency, call this one, do not call the regular Group.new method.
Goban.prototype.newGroup = function (stone, lives) {
    var group = this.garbageGroups.pop();
    if (group) {
        return group.recycle(stone, lives);
    } else {
        this.numGroups += 1;
        return new Group(this, stone, lives, this.numGroups);
    }
};

Goban.prototype.image = function () {
    return this.grid.toTextExt(false, ',', function (s) {
        return Grid.COLOR_CHARS[s.color];
    }).chop();
};

// For debugging only
Goban.prototype.debugDisplay = function () {
    console.log('Board:');
    console.log(this.grid.toText(function (s) {
        return Grid.COLOR_CHARS[s.color];
    }));
    console.log('Groups:');
    console.log(this.grid.toText(function (s) {
        if (s.group) {
            return '' + s.group.ndx;
        } else {
            return '.';
        }
    }));
    console.log('Full info on groups and stones:');
    var groups = {};
    for (var row, row_array = this.grid.yx, row_ndx = 0; row=row_array[row_ndx], row_ndx < row_array.length; row_ndx++) {
        for (var s, s_array = row, s_ndx = 0; s=s_array[s_ndx], s_ndx < s_array.length; s_ndx++) {
            if (s && s.group) {
                groups[s.group.ndx] = s.group;
            }
        }
    }
    for (var ndx = 1; ndx <= this.numGroups; ndx++) {
        if (groups[ndx]) {
            console.log(groups[ndx].debugDump());
        }
    }
};

// This display is for debugging and text-only game
Goban.prototype.consoleDisplay = function () {
    console.log(this.grid.toText(function (s) {
        return Grid.COLOR_CHARS[s.color];
    }));
};

// Basic validation only: coordinates and checks the intersection is empty
// See Stone class for evolved version of this (calling this one)
Goban.prototype.validMove = function (i, j) {
    if (i < 1 || i > this.gsize || j < 1 || j > this.gsize) {
        return false;
    }
    return this.ban[j][i].empty();
};

Goban.prototype.stoneAt = function (i, j) {
    return this.ban[j][i];
};

Goban.prototype.color = function (i, j) {
    var stone = this.ban[j][i];
    if (stone) { // works because BORDER == nil
        return stone.color;
    }
    return main.BORDER;
};

// No validity test here
Goban.prototype.empty = function (i, j) {
    return this.ban[j][i].empty();
};

Goban.prototype.moveNumber = function () {
    return this.history.length;
};

// Plays a stone and stores it in history
// Actually we simply return the existing stone and the caller will update it
Goban.prototype.playAt = function (i, j) {
    var stone = this.ban[j][i];
    if (stone.color !== main.EMPTY) {
        throw new Error('Tried to play on existing stone in ' + i + ',' + j);
    }
    this.history.push(stone);
    return stone;
};

// Removes the last stone played from the board
// Actually we simply return the existing stone and the caller will update it
Goban.prototype.undo = function () {
    return this.history.pop();
};

Goban.prototype.previousStone = function () {
    return this.history[this.history.length-1];
};

// E02: unknown method concat(...)<|endoftext|>


<commit_before>/* ========================================================================
 * Bootstrap: affix.js v3.3.1
 * http://getbootstrap.com/javascript/#affix
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // AFFIX CLASS DEFINITION
  // ======================

  var Affix = function (element, options) {
    this.options = $.extend({}, Affix.DEFAULTS, options)

    this.$target = $(this.options.target)
      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))

    this.$element     = $(element)
    this.affixed      =
    this.unpin        =
    this.pinnedOffset = null

    this.checkPosition()
  }

  Affix.VERSION  = '3.3.1'

  Affix.RESET    = 'affix affix-top affix-bottom'

  Affix.DEFAULTS = {
    offset: 0,
    target: window
  }

  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
    var scrollTop    = this.$target.scrollTop()
    var position     = this.$element.offset()
    var targetHeight = this.$target.height()

    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false

    if (this.affixed == 'bottom') {
      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
    }

    var initializing   = this.affixed == null
    var colliderTop    = initializing ? scrollTop : position.top
    var colliderHeight = initializing ? targetHeight : height

    if (offsetTop != null && colliderTop <= offsetTop) return 'top'
    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'

    return false
  }

  Affix.prototype.getPinnedOffset = function () {
    if (this.pinnedOffset) return this.pinnedOffset
    this.$element.removeClass(Affix.RESET).addClass('affix')
    var scrollTop = this.$target.scrollTop()
    var position  = this.$element.offset()
    return (this.pinnedOffset = position.top - scrollTop)
  }

  Affix.prototype.checkPositionWithEventLoop = function () {
    setTimeout($.proxy(this.checkPosition, this), 1)
  }

  Affix.prototype.checkPosition = function () {
    if (!this.$element.is(':visible')) return

    var height       = this.$element.height()
    var offset       = this.options.offset
    var offsetTop    = offset.top
    var offsetBottom = offset.bottom
    var scrollHeight = $('body').height()

    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)

    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)

    if (this.affixed != affix) {
      if (this.unpin != null) this.$element.css('top', '')

      var affixType = 'affix' + (affix ? '-' + affix : '')
      var e         = $.Event(affixType + '.bs.affix')

      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      this.affixed = affix
      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null

      this.$element
        .removeClass(Affix.RESET)
        .addClass(affixType)
        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
    }

    if (affix == 'bottom') {
      this.$element.offset({
        top: scrollHeight - height - offsetBottom
      })
    }
  }

  // AFFIX PLUGIN DEFINITION
  // =======================

  function Plugin(option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.affix')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  var old = $.fn.affix

  $.fn.affix             = Plugin
  $.fn.affix.Constructor = Affix

  // AFFIX NO CONFLICT
  // =================

  $.fn.affix.noConflict = function () {
    $.fn.affix = old
    return this
  }

  // AFFIX DATA-API
  // ==============

  $(window).on('load', function () {
    $('[data-spy="affix"]').each(function () {
      var $spy = $(this)
      var data = $spy.data()

      data.offset = data.offset || {}

      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
      if (data.offsetTop    != null) data.offset.top    = data.offsetTop

      Plugin.call($spy, data)
    })
  })

}(jQuery);
<commit_msg>Fixes affix-top class not applying<commit_after>/* ========================================================================
 * Bootstrap: affix.js v3.3.1
 * http://getbootstrap.com/javascript/#affix
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // AFFIX CLASS DEFINITION
  // ======================

  var Affix = function (element, options) {
    this.options = $.extend({}, Affix.DEFAULTS, options)

    this.$target = $(this.options.target)
      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))

    this.$element     = $(element)
    this.affixed      =
    this.unpin        =
    this.pinnedOffset = null

    this.checkPosition()
  }

  Affix.VERSION  = '3.3.1'

  Affix.RESET    = 'affix affix-top affix-bottom'

  Affix.DEFAULTS = {
    offset: 0,
    target: window
  }

  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
    var scrollTop    = this.$target.scrollTop()
    var position     = this.$element.offset()
    var targetHeight = this.$target.height()

    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false

    if (this.affixed == 'bottom') {
      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
    }

    var initializing   = this.affixed == null
    var colliderTop    = initializing ? scrollTop : position.top
    var colliderHeight = initializing ? targetHeight : height

    if (offsetTop != null && scrollTop <= offsetTop) return 'top'
    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'

    return false
  }

  Affix.prototype.getPinnedOffset = function () {
    if (this.pinnedOffset) return this.pinnedOffset
    this.$element.removeClass(Affix.RESET).addClass('affix')
    var scrollTop = this.$target.scrollTop()
    var position  = this.$element.offset()
    return (this.pinnedOffset = position.top - scrollTop)
  }

  Affix.prototype.checkPositionWithEventLoop = function () {
    setTimeout($.proxy(this.checkPosition, this), 1)
  }

  Affix.prototype.checkPosition = function () {
    if (!this.$element.is(':visible')) return

    var height       = this.$element.height()
    var offset       = this.options.offset
    var offsetTop    = offset.top
    var offsetBottom = offset.bottom
    var scrollHeight = $('body').height()

    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)

    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)

    if (this.affixed != affix) {
      if (this.unpin != null) this.$element.css('top', '')

      var affixType = 'affix' + (affix ? '-' + affix : '')
      var e         = $.Event(affixType + '.bs.affix')

      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      this.affixed = affix
      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null

      this.$element
        .removeClass(Affix.RESET)
        .addClass(affixType)
        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
    }

    if (affix == 'bottom') {
      this.$element.offset({
        top: scrollHeight - height - offsetBottom
      })
    }
  }

  // AFFIX PLUGIN DEFINITION
  // =======================

  function Plugin(option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.affix')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  var old = $.fn.affix

  $.fn.affix             = Plugin
  $.fn.affix.Constructor = Affix

  // AFFIX NO CONFLICT
  // =================

  $.fn.affix.noConflict = function () {
    $.fn.affix = old
    return this
  }

  // AFFIX DATA-API
  // ==============

  $(window).on('load', function () {
    $('[data-spy="affix"]').each(function () {
      var $spy = $(this)
      var data = $spy.data()

      data.offset = data.offset || {}

      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
      if (data.offsetTop    != null) data.offset.top    = data.offsetTop

      Plugin.call($spy, data)
    })
  })

}(jQuery);
<|endoftext|>


<commit_before>/* ========================================================================
 * Bootstrap: affix.js v3.2.0
 * http://getbootstrap.com/javascript/#affix
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // AFFIX CLASS DEFINITION
  // ======================

  var Affix = function (element, options) {
    this.options = $.extend({}, Affix.DEFAULTS, options)

    this.$target = $(this.options.target)
      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))

    this.$element     = $(element)
    this.affixed      =
    this.unpin        =
    this.pinnedOffset = null

    this.checkPosition()
  }

  Affix.VERSION  = '3.2.0'

  Affix.RESET    = 'affix affix-top affix-bottom'

  Affix.DEFAULTS = {
    offset: 0,
    target: window
  }

  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
    var scrollTop     = this.$target.scrollTop()
    var position      = this.$element.offset()
    var targetHeight  = this.$target.height()

    if (offsetTop != null && this.affixed == 'top') return scrollTop >= offsetTop ? false : 'top'
    if (this.affixed == 'bottom') {
      // Can be affixed to the top, use the unpin value
      if (offsetTop != null) {
        return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
      // Can only ever be pinned or affixed to the bottom, ignore unpin value
      } else {
        return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
      }
    } else {
      var initializing   = this.affixed == null
      var colliderTop    = initializing ? scrollTop : position.top
      var colliderHeight = initializing ? targetHeight : height

      if (offsetTop != null && colliderTop <= offsetTop) {
        return 'top'
      } else if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) {
        return 'bottom'
      } else {
        return false
      }
    }
  }

  Affix.prototype.getPinnedOffset = function () {
    if (this.pinnedOffset) return this.pinnedOffset
    this.$element.removeClass(Affix.RESET).addClass('affix')
    var scrollTop = this.$target.scrollTop()
    var position  = this.$element.offset()
    return (this.pinnedOffset = position.top - scrollTop)
  }

  Affix.prototype.checkPositionWithEventLoop = function () {
    setTimeout($.proxy(this.checkPosition, this), 1)
  }

  Affix.prototype.checkPosition = function () {
    if (!this.$element.is(':visible')) return

    var scrollHeight = $(document).height()
    var height       = this.$element.height()
    var offset       = this.options.offset
    var offsetTop    = offset.top
    var offsetBottom = offset.bottom

    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)

    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)

    if (this.affixed != affix) {
      if (this.unpin != null) this.$element.css('top', '')

      var affixType = 'affix' + (affix ? '-' + affix : '')
      var e         = $.Event(affixType + '.bs.affix')

      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      this.affixed = affix
      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null

      this.$element
        .removeClass(Affix.RESET)
        .addClass(affixType)
        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
    }

    if (affix == 'bottom') {
      this.$element.offset({
        top: scrollHeight - height - offsetBottom
      })
    }
  }

  // AFFIX PLUGIN DEFINITION
  // =======================

  function Plugin(option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.affix')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  var old = $.fn.affix

  $.fn.affix             = Plugin
  $.fn.affix.Constructor = Affix

  // AFFIX NO CONFLICT
  // =================

  $.fn.affix.noConflict = function () {
    $.fn.affix = old
    return this
  }

  // AFFIX DATA-API
  // ==============

  $(window).on('load', function () {
    $('[data-spy="affix"]').each(function () {
      var $spy = $(this)
      var data = $spy.data()

      data.offset = data.offset || {}

      if (data.offsetBottom) data.offset.bottom = data.offsetBottom
      if (data.offsetTop)    data.offset.top    = data.offsetTop

      Plugin.call($spy, data)
    })
  })

}(jQuery);
<commit_msg>Fix issue where bottom affixed element floats over the footer when the   document height is smaller than the viewport height.<commit_after>/* ========================================================================
 * Bootstrap: affix.js v3.2.0
 * http://getbootstrap.com/javascript/#affix
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // AFFIX CLASS DEFINITION
  // ======================

  var Affix = function (element, options) {
    this.options = $.extend({}, Affix.DEFAULTS, options)

    this.$target = $(this.options.target)
      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))

    this.$element     = $(element)
    this.affixed      =
    this.unpin        =
    this.pinnedOffset = null

    this.checkPosition()
  }

  Affix.VERSION  = '3.2.0'

  Affix.RESET    = 'affix affix-top affix-bottom'

  Affix.DEFAULTS = {
    offset: 0,
    target: window
  }

  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
    var scrollTop     = this.$target.scrollTop()
    var position      = this.$element.offset()
    var targetHeight  = this.$target.height()

    if (offsetTop != null && this.affixed == 'top') return scrollTop >= offsetTop ? false : 'top'
    if (this.affixed == 'bottom') {
      // Can be affixed to the top, use the unpin value
      if (offsetTop != null) {
        return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
      // Can only ever be pinned or affixed to the bottom, ignore unpin value
      } else {
        return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
      }
    } else {
      var initializing   = this.affixed == null
      var colliderTop    = initializing ? scrollTop : position.top
      var colliderHeight = initializing ? targetHeight : height

      if (offsetTop != null && colliderTop <= offsetTop) {
        return 'top'
      } else if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) {
        return 'bottom'
      } else {
        return false
      }
    }
  }

  Affix.prototype.getPinnedOffset = function () {
    if (this.pinnedOffset) return this.pinnedOffset
    this.$element.removeClass(Affix.RESET).addClass('affix')
    var scrollTop = this.$target.scrollTop()
    var position  = this.$element.offset()
    return (this.pinnedOffset = position.top - scrollTop)
  }

  Affix.prototype.checkPositionWithEventLoop = function () {
    setTimeout($.proxy(this.checkPosition, this), 1)
  }

  Affix.prototype.checkPosition = function () {
    if (!this.$element.is(':visible')) return

    var scrollHeight = $('body').height()
    var height       = this.$element.height()
    var offset       = this.options.offset
    var offsetTop    = offset.top
    var offsetBottom = offset.bottom

    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)

    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)

    if (this.affixed != affix) {
      if (this.unpin != null) this.$element.css('top', '')

      var affixType = 'affix' + (affix ? '-' + affix : '')
      var e         = $.Event(affixType + '.bs.affix')

      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      this.affixed = affix
      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null

      this.$element
        .removeClass(Affix.RESET)
        .addClass(affixType)
        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
    }

    if (affix == 'bottom') {
      this.$element.offset({
        top: scrollHeight - height - offsetBottom
      })
    }
  }

  // AFFIX PLUGIN DEFINITION
  // =======================

  function Plugin(option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.affix')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  var old = $.fn.affix

  $.fn.affix             = Plugin
  $.fn.affix.Constructor = Affix

  // AFFIX NO CONFLICT
  // =================

  $.fn.affix.noConflict = function () {
    $.fn.affix = old
    return this
  }

  // AFFIX DATA-API
  // ==============

  $(window).on('load', function () {
    $('[data-spy="affix"]').each(function () {
      var $spy = $(this)
      var data = $spy.data()

      data.offset = data.offset || {}

      if (data.offsetBottom) data.offset.bottom = data.offsetBottom
      if (data.offsetTop)    data.offset.top    = data.offsetTop

      Plugin.call($spy, data)
    })
  })

}(jQuery);
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////
//     ___                         FLORO                         ___
//    /o o\                        =====                        /  o\
//    \ º /                         *|*                         \   <
//    //|\\                By j3dlab.com - 2014                 //|\\
///////////////////////////////////////////////////////////////////////////

//
//
//                             _____
//                            |     |        /|
//                            |     |      /  |
//                            |     |    /    |
//                            |     |   |     |
//                            |     |   |     |
//                            |     |   |     |
//                            |     |   |     |
//                            |     |    \    |
//                            |     |      \  |
//                            |_____|        \|
//
//
//  CSS CONTENTS
//
//  Number. Description Block
//
//  1.Nice Scrooll
//  2.Gridalicious
//  3.Hover menu
//  4.RSS Loader
//  5.Credit Colors
//  6.Responsive menu
//  7.Browser detector
//  8.Fix Youtube z-index
//  9.Google Analytics

///////////////////////////////////////////////////////////////////////////

$(document).ready(function(){

    ///////////////////////////////////////////////////////////////////////////
    //1.Nice Scrooll
    ///////////////////////////////////////////////////////////////////////////

    var MyNSFrontColor = "#333333";
    var MyNSBackgroundColor = '#989898';

    nice = $(".desktop body").niceScroll({
        cursorcolor: MyNSFrontColor,
        cursorborder: '0',
        cursorborderradius : '0',
        zindex:'999999',
        cursoropacitymin:'1',
        cursorwidth :10,
        background: MyNSBackgroundColor
    });

    ///////////////////////////////////////////////////////////////////////////
    //2.Gridalicious
    ///////////////////////////////////////////////////////////////////////////

    var MyGridWidth = 450;
    var MyGridMargin = 30;

     $('.desktop .content[role="main"]').gridalicious({
        selector: 'article',
        width: MyGridWidth,
        gutter: MyGridMargin
    });

    ///////////////////////////////////////////////////////////////////////////
    //3.Hover menu
    ///////////////////////////////////////////////////////////////////////////

    $('#menu').mouseover(function(){
        $('.content').toggleClass('rotate');
    });
    $('#menu').mouseout(function(){
        $('.content').removeClass('rotate');
    });
    $('#menu').click(function(){
        $('.content').addClass('rotate');
    });
    $('#menu').click(function(){
        $('.content').removeClass('rotate');
    });

    ///////////////////////////////////////////////////////////////////////////
    //4.RSS Loader
    ///////////////////////////////////////////////////////////////////////////

    var MyRSSURL = ''; // http://myblog.com/rss
    var MyPoststoLoad = '6';//Number of posts to load

    /*if(MyRSSURL.length==0){
        MyRSSURL = document.location.protocol + "//" +  Vurl[2]+'/rss';
    };*/
    var Autourl = document.location.toString();
    var Vurl = Autourl.split("/");
    var url = document.location.protocol + "//" +  Vurl[2] + '/';
    var getUrl = url + 'ghost/api/v0.1/posts/'

    $.ajax({
        type: "GET",
        url: getUrl,
        //document.location.protocol + '//ajax.googleapis.com/ajax/services/feed/load?v=2.0&num='+MyPoststoLoad+'&callback=?&q=' + encodeURIComponent(MyRSSURL),
        dataType: 'json',
        error: function(){
            alert('Unable to load feed, Incorrect path or invalid feed');
        },
        success: function(xml){
            var postlist = xml.post || []; //xml.responseData.feed.entries;
            var html= '';
            var htmlselect= '';
            $.each(postlist, function(idx, data) {
                html += '
'+data.title+'
';
                htmlselect +='<option value="'+ url  + data.slug +'">'+data.title+'</option>'
            });
            $('#recentpost').append(html);
            $('#recentpostselect').append(htmlselect);
        }
    });

    ///////////////////////////////////////////////////////////////////////////
    //6.Responsive menu
    ///////////////////////////////////////////////////////////////////////////
    $("#recentpostselect").on('change', function(){
        var url = $(this).val();
        window.location.replace(url);
    });

    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    //7. Browser detector
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    var rv = -1;
    if (navigator.appName == 'Microsoft Internet Explorer'){
        var ua = navigator.userAgent;
        var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
        if (re.exec(ua) != null) rv = parseFloat( RegExp.$1 );
    }else if (navigator.appName == 'Netscape'){
        var ua = navigator.userAgent;
        var re  = new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})");
        if (re.exec(ua) != null)rv = parseFloat( RegExp.$1 );
    }if(rv>0){
        //It's IE
        $('#menu').addClass('IE');

        principalleft
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    //8. Fix Youtube z-index
    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    $('iframe[src*=youtube]').each(function( index ){
        $(this).attr('src',$(this).attr('src')+'?wmode=transparent&rel=0');
    });

    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    //9. Google Analytics
    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    var MyGoogleAnalyticsID = 'UA-48827699-1';
    var MyBlogURL = 'nixo.us';

    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', MyGoogleAnalyticsID, MyBlogURL);
    ga('send', 'pageview');

});//end document.ready

<commit_msg>fixed typo<commit_after>///////////////////////////////////////////////////////////////////////////
//     ___                         FLORO                         ___
//    /o o\                        =====                        /  o\
//    \ º /                         *|*                         \   <
//    //|\\                By j3dlab.com - 2014                 //|\\
///////////////////////////////////////////////////////////////////////////

//
//
//                             _____
//                            |     |        /|
//                            |     |      /  |
//                            |     |    /    |
//                            |     |   |     |
//                            |     |   |     |
//                            |     |   |     |
//                            |     |   |     |
//                            |     |    \    |
//                            |     |      \  |
//                            |_____|        \|
//
//
//  CSS CONTENTS
//
//  Number. Description Block
//
//  1.Nice Scrooll
//  2.Gridalicious
//  3.Hover menu
//  4.RSS Loader
//  5.Credit Colors
//  6.Responsive menu
//  7.Browser detector
//  8.Fix Youtube z-index
//  9.Google Analytics

///////////////////////////////////////////////////////////////////////////

$(document).ready(function(){

    ///////////////////////////////////////////////////////////////////////////
    //1.Nice Scrooll
    ///////////////////////////////////////////////////////////////////////////

    var MyNSFrontColor = "#333333";
    var MyNSBackgroundColor = '#989898';

    nice = $(".desktop body").niceScroll({
        cursorcolor: MyNSFrontColor,
        cursorborder: '0',
        cursorborderradius : '0',
        zindex:'999999',
        cursoropacitymin:'1',
        cursorwidth :10,
        background: MyNSBackgroundColor
    });

    ///////////////////////////////////////////////////////////////////////////
    //2.Gridalicious
    ///////////////////////////////////////////////////////////////////////////

    var MyGridWidth = 450;
    var MyGridMargin = 30;

     $('.desktop .content[role="main"]').gridalicious({
        selector: 'article',
        width: MyGridWidth,
        gutter: MyGridMargin
    });

    ///////////////////////////////////////////////////////////////////////////
    //3.Hover menu
    ///////////////////////////////////////////////////////////////////////////

    $('#menu').mouseover(function(){
        $('.content').toggleClass('rotate');
    });
    $('#menu').mouseout(function(){
        $('.content').removeClass('rotate');
    });
    $('#menu').click(function(){
        $('.content').addClass('rotate');
    });
    $('#menu').click(function(){
        $('.content').removeClass('rotate');
    });

    ///////////////////////////////////////////////////////////////////////////
    //4.RSS Loader
    ///////////////////////////////////////////////////////////////////////////

    var MyRSSURL = ''; // http://myblog.com/rss
    var MyPoststoLoad = '6';//Number of posts to load

    /*if(MyRSSURL.length==0){
        MyRSSURL = document.location.protocol + "//" +  Vurl[2]+'/rss';
    };*/
    var Autourl = document.location.toString();
    var Vurl = Autourl.split("/");
    var url = document.location.protocol + "//" +  Vurl[2] + '/';
    var getUrl = url + 'ghost/api/v0.1/posts/'

    $.ajax({
        type: "GET",
        url: getUrl,
        //document.location.protocol + '//ajax.googleapis.com/ajax/services/feed/load?v=2.0&num='+MyPoststoLoad+'&callback=?&q=' + encodeURIComponent(MyRSSURL),
        dataType: 'json',
        error: function(){
            alert('Unable to load feed, Incorrect path or invalid feed');
        },
        success: function(xml){
            var postlist = xml.posts || []; //xml.responseData.feed.entries;
            var html= '';
            var htmlselect= '';
            $.each(postlist, function(idx, data) {
                html += '
'+data.title+'
';
                htmlselect +='<option value="'+ url  + data.slug +'">'+data.title+'</option>'
            });
            $('#recentpost').append(html);
            $('#recentpostselect').append(htmlselect);
        }
    });

    ///////////////////////////////////////////////////////////////////////////
    //6.Responsive menu
    ///////////////////////////////////////////////////////////////////////////
    $("#recentpostselect").on('change', function(){
        var url = $(this).val();
        window.location.replace(url);
    });

    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    //7. Browser detector
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    var rv = -1;
    if (navigator.appName == 'Microsoft Internet Explorer'){
        var ua = navigator.userAgent;
        var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
        if (re.exec(ua) != null) rv = parseFloat( RegExp.$1 );
    }else if (navigator.appName == 'Netscape'){
        var ua = navigator.userAgent;
        var re  = new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})");
        if (re.exec(ua) != null)rv = parseFloat( RegExp.$1 );
    }if(rv>0){
        //It's IE
        $('#menu').addClass('IE');

        principalleft
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    //8. Fix Youtube z-index
    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    $('iframe[src*=youtube]').each(function( index ){
        $(this).attr('src',$(this).attr('src')+'?wmode=transparent&rel=0');
    });

    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    //9. Google Analytics
    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    var MyGoogleAnalyticsID = 'UA-48827699-1';
    var MyBlogURL = 'nixo.us';

    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', MyGoogleAnalyticsID, MyBlogURL);
    ga('send', 'pageview');

});//end document.ready

<|endoftext|>


<commit_before>const fs = require('fs')
const oneSky = require('@brainly/onesky-utils')
const sanitizeHtml = require('sanitize-html')
const sanitizeOpts = {
  allowedTags: ['b', 'strong', 'a', 'code'],
  allowedAttributes: {
    a: ['href', 'class']
  },
  textFilter(text) {
    return text.replace(/\{\{/, '{{').replace(/\}\}/, '}}')
  }
}

async function download() {
  try {
    const content = await oneSky.getMultilingualFile({
      apiKey: process.env.ONE_SKY_PUBLIC_KEY,
      secret: process.env.ONE_SKY_PRIVATE_KEY,
      projectId: '25049',
      fileName: 'en-US.json'
    })
    const json = JSON.parse(content)

    for (const [code, lang] of Object.entries(json)) {
      for (const [key, value] of Object.entries(lang.translation)) {
        lang.translation[key] = sanitizeHtml(value, sanitizeOpts)
      }
      fs.writeFileSync(
        `./locales/${code}.json`,
        JSON.stringify(lang.translation, null, 2)
      )
    }

    fs.createReadStream('./locales/en-GB.json').pipe(
      fs.createWriteStream('./locales/en-US.json')
    )
    fs.createReadStream('./locales/en-GB.json').pipe(
      fs.createWriteStream('./locales/en.json')
    )
    fs.createReadStream('./locales/zh-CN.json').pipe(
      fs.createWriteStream('./locales/cn.json')
    )
  } catch (error) {
    console.error(error)
  }
}

download()
<commit_msg>Use fs promises<commit_after>const { promises: fs } = require('fs')
const oneSky = require('@brainly/onesky-utils')

const sanitizeHtml = require('sanitize-html')
const sanitizeOpts = {
  allowedTags: ['b', 'strong', 'a', 'code'],
  allowedAttributes: {
    a: ['href', 'class']
  },
  textFilter(text) {
    return text.replace(/\{\{/, '{{').replace(/\}\}/, '}}')
  }
}

async function download() {
  try {
    const content = await oneSky.getMultilingualFile({
      apiKey: process.env.ONE_SKY_PUBLIC_KEY,
      secret: process.env.ONE_SKY_PRIVATE_KEY,
      projectId: '25049',
      fileName: 'en-US.json'
    })
    const json = JSON.parse(content)

    for (const [code, lang] of Object.entries(json)) {
      for (const [key, value] of Object.entries(lang.translation)) {
        lang.translation[key] = sanitizeHtml(value, sanitizeOpts)
      }

      await fs.writeFile(
        `./locales/${code}.json`,
        JSON.stringify(lang.translation, null, 2)
      )
    }

    // Copy files, so we have appropriate dialects
    await fs.copyFile('./locales/en-GB.json', './locales/en-US.json')
    await fs.copyFile('./locales/en-GB.json', './locales/en.json')
    await fs.copyFile('./locales/zh-CN.json', './locales/cn.json')
  } catch (error) {
    console.error(error)
  }
}

download()
<|endoftext|>


<commit_before>/**
 * IoSL-INav controllers/users/compreqs
 * SNET TU Berlin
 * using PIazza code base
 *
 * Companion requests specific controllers
 */

/* Variables and configurations. */

var config = require('./../../config');
var CompanionRequest = require('./../../models/companionrequest');
var User = require('./../../models/user');

var controller = {};

/* Controllers. */

controller.getPendingRequests = function(req, res, next) {

  CompanionRequest.find({
    to: req.user._id
  }, function(err, allPendingRequest) {

    if (err) {
      console.log("Error during looking for already companion request.");
      console.log(err);
      res.status(500).end();
      return next();
    } else {
      res.json(allPendingRequest);
      return next();
    }
  });
};

controller.createCompanionRequest = function(req, res, next) {

  var companionID = req.body.userID;

  CompanionRequest.findOne({
    $or: [{
      $and: [{
        from: req.user._id
      }, {
        to: companionID
      }]
    }, {
      $and: [{
        from: companionID
      }, {
        to: req.user._id
      }]
    }]
  }, function(err, foundReq) {

    if (err) {
      console.log("Error during looking for already existing companion request.");
      console.log(err);

      res.status(500).end();
      return next();
    }

    if (foundReq) {

      res.status(200).json({
        status: "success",
        reason: "companion request already exists",
        companionRequestID: foundReq._id
      }).end();
      return next();
    } else {
      User.findById(companionID, function(err, foundUser) {

        if (err) {
          console.log("During a companion request, the other user could not be found.");
          console.log(err);

          res.status(500).end();
          return next();
        }

        CompanionRequest.create({
          from: req.user._id,
          to: companionID,
          status: 'pending'
        }, function(err, addedCompanionRequest) {

          if (err) {
            console.log("Error during adding a new companion request.");
            console.log(err);

            res.status(500).end();
            return next();
          }

          res.json({
            status: "success",
            reason: "companion request sent",
            companionRequestID: addedCompanionRequest._id
          });
          return next();
        });
      });
    }
  });
};

controller.getCompanionRequest = function(req, res, next) {
  CompanionRequest.findById(req.companionRequestID, function(err, companionRequest) {
    if (err) {
      console.log("Error during looking for a companion request.");
      console.log(err);
      res.status(500).end();
      return next();
    } else {
      res.json(companionRequest);
      return next();
    }
  });
};

controller.updateCompanionRequest = function(req, res, next) {
  if(req.body.accept || req.body.deny){
    CompanionRequest.findById(req.companionRequestID, function(err, companionRequest) {
      if (err) {
        console.log("Error during looking for a companion request.");
        console.log(err);
        res.status(500).end();
        return next();
      } else {
        if(req.body.deny){
          companionRequest.status='denied';
          res.json({
            status: "success",
            reason: "companion request denied"
          });
        }else if(req.body.accept){
          companionRequest.status='accepted';
          /*
          req.user.groups.find({name:'All friends'},function(err,group){
            group.push(fromUser);
            return next();
          });
          */
          //TODO add users to there friendlists
          User.findById(companionRequest.from, function(err, fromUser) {
            if (err) {
              console.log("Error while locating group of companionrequest");
              console.log(err);
              res.status(500).json();
              return next();
            }
            //TODO: check if already in that group
            for(var g in fromUser.groups){
              if(fromUser.groups[g].name=='All friends'){
                if(fromUser.groups[g].members.length<=0){
                  var data=[req.user];
                  fromUser.groups[g].members=data;
                }else{
                  fromUser.groups[g].members.push(req.user);
                }
              }
            }
            for(var g in req.user.groups){
              if(req.user.groups[g].name=='All friends'){
                if(req.user.groups[g].members.length<=0){
                  var data=[fromUser];
                  req.user.groups[g].members=data;
                }else{
                  req.user.groups[g].members.push(fromUser);
                }
              }
            }
            req.user.save();
            fromUser.save();
            return next();
          });
          res.json({
            status: "success",
            reason: "companion request accepted"
          });
        }
        companionRequest.save();
        return next();
      }
    });
  }else{
    res.status(400).end();
    return next();
  }
};

controller.deleteCompanionRequest = function(req, res, next) {
  /* TODO */
  return res.status(501).end();
};

/* Export all controllers. */

module.exports = controller;
<commit_msg>implement deleteCompanionRequest<commit_after>/**
 * IoSL-INav controllers/users/compreqs
 * SNET TU Berlin
 * using PIazza code base
 *
 * Companion requests specific controllers
 */

/* Variables and configurations. */

var config = require('./../../config');
var CompanionRequest = require('./../../models/companionrequest');
var User = require('./../../models/user');

var controller = {};

/* Controllers. */

controller.getPendingRequests = function(req, res, next) {

  CompanionRequest.find({
    to: req.user._id
  }, function(err, allPendingRequest) {

    if (err) {
      console.log("Error during looking for already companion request.");
      console.log(err);
      res.status(500).end();
      return next();
    } else {
      res.json(allPendingRequest);
      return next();
    }
  });
};

controller.createCompanionRequest = function(req, res, next) {

  var companionID = req.body.userID;

  CompanionRequest.findOne({
    $or: [{
      $and: [{
        from: req.user._id
      }, {
        to: companionID
      }]
    }, {
      $and: [{
        from: companionID
      }, {
        to: req.user._id
      }]
    }]
  }, function(err, foundReq) {

    if (err) {
      console.log("Error during looking for already existing companion request.");
      console.log(err);

      res.status(500).end();
      return next();
    }

    if (foundReq) {

      res.status(200).json({
        status: "success",
        reason: "companion request already exists",
        companionRequestID: foundReq._id
      }).end();
      return next();
    } else {
      User.findById(companionID, function(err, foundUser) {

        if (err) {
          console.log("During a companion request, the other user could not be found.");
          console.log(err);

          res.status(500).end();
          return next();
        }

        CompanionRequest.create({
          from: req.user._id,
          to: companionID,
          status: 'pending'
        }, function(err, addedCompanionRequest) {

          if (err) {
            console.log("Error during adding a new companion request.");
            console.log(err);

            res.status(500).end();
            return next();
          }

          res.json({
            status: "success",
            reason: "companion request sent",
            companionRequestID: addedCompanionRequest._id
          });
          return next();
        });
      });
    }
  });
};

controller.getCompanionRequest = function(req, res, next) {
  CompanionRequest.findById(req.companionRequestID, function(err, companionRequest) {
    if (err) {
      console.log("Error during looking for a companion request.");
      console.log(err);
      res.status(500).end();
      return next();
    } else {
      res.json(companionRequest);
      return next();
    }
  });
};

controller.updateCompanionRequest = function(req, res, next) {
  if(req.body.accept || req.body.deny){
    CompanionRequest.findById(req.companionRequestID, function(err, companionRequest) {
      if (err) {
        console.log("Error during looking for a companion request.");
        console.log(err);
        res.status(500).end();
        return next();
      } else {
        if(req.body.deny){
          companionRequest.status='denied';
          res.json({
            status: "success",
            reason: "companion request denied"
          });
        }else if(req.body.accept){
          companionRequest.status='accepted';
          User.findById(companionRequest.from, function(err, fromUser) {
            if (err) {
              console.log("Error while locating group of companionrequest");
              console.log(err);
              res.status(500).json();
              return next();
            }
            //TODO: check if already in that group
            for(var g in fromUser.groups){
              if(fromUser.groups[g].name=='All friends'){
                if(fromUser.groups[g].members.length<=0){
                  var data=[req.user];
                  fromUser.groups[g].members=data;
                }else{
                  fromUser.groups[g].members.push(req.user);
                }
              }
            }
            for(var g in req.user.groups){
              if(req.user.groups[g].name=='All friends'){
                if(req.user.groups[g].members.length<=0){
                  var data=[fromUser];
                  req.user.groups[g].members=data;
                }else{
                  req.user.groups[g].members.push(fromUser);
                }
              }
            }
            req.user.save();
            fromUser.save();
            return next();
          });
          res.json({
            status: "success",
            reason: "companion request accepted"
          });
        }
        companionRequest.save();
        return next();
      }
    });
  }else{
    res.status(400).end();
    return next();
  }
};

controller.deleteCompanionRequest = function(req, res, next) {
  CompanionRequest.findByIdAndRemove(req.companionRequestID, function(err, companionRequest) {
    if (err) {
			console.log("Error while removing companion request.");
			res.status(500).end();
			return next();
		}
    res.status(200).json({
      status: "success",
      reason: "companion request deleted"
    });
    return next();
  });
};

/* Export all controllers. */

module.exports = controller;
<|endoftext|>


<commit_before>var Duration = (function () {

    var nanosecond  = 1,
        microsecond = 1000 * nanosecond,
        millisecond = 1000 * microsecond,
        second      = 1000 * millisecond,
        minute      = 60   * second,
        hour        = 60   * minute;

    var unitMap = {
        "ns" : nanosecond,
        "us" : microsecond,
        "µs" : microsecond,
        "μs" : microsecond,
        "ms" : millisecond,
        "s"  : second,
        "m"  : minute,
        "h"  : hour
    };

    var Duration = function (nanoseconds) {
        this._nanoseconds = nanoseconds;
    };

    Duration.nanosecond  = new Duration(nanosecond);
    Duration.microsecond = new Duration(microsecond);
    Duration.millisecond = new Duration(millisecond);
    Duration.second      = new Duration(second);
    Duration.minute      = new Duration(minute);
    Duration.hour        = new Duration(hour);

    Duration.prototype.nanoseconds = function () {
        return this._nanoseconds;
    };

    Duration.prototype.microseconds = function () {
        return Math.floor(this._nanoseconds / microsecond);
    };

    Duration.prototype.milliseconds = function () {
        return Math.floor(this._nanoseconds / millisecond);
    };

    Duration.prototype.seconds = function () {
        return Math.floor(this._nanoseconds / second);
    };

    Duration.prototype.minutes = function () {
        return Math.floor(this._nanoseconds / minute);
    };

    Duration.prototype.hours = function () {
        return Math.floor(this._nanoseconds / hour);
    };

    Duration.prototype.toString = function () {
      var str         = "",
          nanoseconds = Math.abs(this._nanoseconds),
          sign        = this._nanoseconds < 0 ? "-" : "";

      // no units for 0 duration
      if (nanoseconds === 0) {
        return "0";
      }

      // hours
      var hours = Math.floor(nanoseconds / hour);
      nanoseconds -= hour * hours;
      if (hours !== 0) {
        str += hours.toString() + "h";
      }

      // minutes
      var minutes = Math.floor(nanoseconds / minute);
      nanoseconds -= minute * minutes;
      if (minutes !== 0) {
        str += minutes.toString() + "m";
      }

      // seconds
      var seconds = Math.floor(nanoseconds / second);
      nanoseconds -= second * seconds;
      if (seconds !== 0) {
        str += seconds.toString() + "s";
      }

      // milliseconds
      var milliseconds = Math.floor(nanoseconds / millisecond);
      nanoseconds -= millisecond * milliseconds;
      if (milliseconds !== 0) {
        str += milliseconds.toString() + "ms";
      }

      // microseconds
      var microseconds = Math.floor(nanoseconds / microsecond);
      nanoseconds -= microsecond * microseconds;
      if (microseconds !== 0) {
        str += microseconds.toString() + "µs";
      }

      // nanoseconds
      if (nanoseconds !== 0) {
        str += nanoseconds.toString() + "ns";
      }

      return sign + str;
    };

    Duration.prototype.valueOf = function () {
      return this._nanoseconds;
    };

    Duration.parse = function (duration) {

        if (duration === "0" || duration === "+0" || duration === "-0") {
          return new Duration(0);
        }

        var regex = /([\-\+\d\.]+)([a-zµμ]+)/g,
            total = 0,
            count = 0,
            sign  = duration[0] === '-' ? -1 : 1,
            unit, value, match;

        while (match = regex.exec(duration)) {

            unit  = match[2];
            value = Math.abs(parseFloat(match[1]));
            count++;

            if (isNaN(value)) {
              throw new Error("invalid duration");
            }

            if (typeof unitMap[unit] === "undefined") {
              throw new Error("invalid unit " + unit);
            }

            total += value * unitMap[unit];
        }

        if (count === 0) {
          throw new Error("invalid duration");
        }

        return new Duration(total * sign);
    };

    return Duration;

}).call(this);

if (typeof module !== "undefined") {
   module.exports = Duration;
}
<commit_msg>don't subtract when zero<commit_after>var Duration = (function () {

    var nanosecond  = 1,
        microsecond = 1000 * nanosecond,
        millisecond = 1000 * microsecond,
        second      = 1000 * millisecond,
        minute      = 60   * second,
        hour        = 60   * minute;

    var unitMap = {
        "ns" : nanosecond,
        "us" : microsecond,
        "µs" : microsecond,
        "μs" : microsecond,
        "ms" : millisecond,
        "s"  : second,
        "m"  : minute,
        "h"  : hour
    };

    var Duration = function (nanoseconds) {
        this._nanoseconds = nanoseconds;
    };

    Duration.nanosecond  = new Duration(nanosecond);
    Duration.microsecond = new Duration(microsecond);
    Duration.millisecond = new Duration(millisecond);
    Duration.second      = new Duration(second);
    Duration.minute      = new Duration(minute);
    Duration.hour        = new Duration(hour);

    Duration.prototype.nanoseconds = function () {
        return this._nanoseconds;
    };

    Duration.prototype.microseconds = function () {
        return Math.floor(this._nanoseconds / microsecond);
    };

    Duration.prototype.milliseconds = function () {
        return Math.floor(this._nanoseconds / millisecond);
    };

    Duration.prototype.seconds = function () {
        return Math.floor(this._nanoseconds / second);
    };

    Duration.prototype.minutes = function () {
        return Math.floor(this._nanoseconds / minute);
    };

    Duration.prototype.hours = function () {
        return Math.floor(this._nanoseconds / hour);
    };

    Duration.prototype.toString = function () {
      var str         = "",
          nanoseconds = Math.abs(this._nanoseconds),
          sign        = this._nanoseconds < 0 ? "-" : "";

      // no units for 0 duration
      if (nanoseconds === 0) {
        return "0";
      }

      // hours
      var hours = Math.floor(nanoseconds / hour);
      if (hours !== 0) {
        nanoseconds -= hour * hours;
        str += hours.toString() + "h";
      }

      // minutes
      var minutes = Math.floor(nanoseconds / minute);
      if (minutes !== 0) {
        nanoseconds -= minute * minutes;
        str += minutes.toString() + "m";
      }

      // seconds
      var seconds = Math.floor(nanoseconds / second);
      if (seconds !== 0) {
        nanoseconds -= second * seconds;
        str += seconds.toString() + "s";
      }

      // milliseconds
      var milliseconds = Math.floor(nanoseconds / millisecond);
      if (milliseconds !== 0) {
        nanoseconds -= millisecond * milliseconds;
        str += milliseconds.toString() + "ms";
      }

      // microseconds
      var microseconds = Math.floor(nanoseconds / microsecond);
      if (microseconds !== 0) {
        nanoseconds -= microsecond * microseconds;
        str += microseconds.toString() + "µs";
      }

      // nanoseconds
      if (nanoseconds !== 0) {
        str += nanoseconds.toString() + "ns";
      }

      return sign + str;
    };

    Duration.prototype.valueOf = function () {
      return this._nanoseconds;
    };

    Duration.parse = function (duration) {

        if (duration === "0" || duration === "+0" || duration === "-0") {
          return new Duration(0);
        }

        var regex = /([\-\+\d\.]+)([a-zµμ]+)/g,
            total = 0,
            count = 0,
            sign  = duration[0] === '-' ? -1 : 1,
            unit, value, match;

        while (match = regex.exec(duration)) {

            unit  = match[2];
            value = Math.abs(parseFloat(match[1]));
            count++;

            if (isNaN(value)) {
              throw new Error("invalid duration");
            }

            if (typeof unitMap[unit] === "undefined") {
              throw new Error("invalid unit " + unit);
            }

            total += value * unitMap[unit];
        }

        if (count === 0) {
          throw new Error("invalid duration");
        }

        return new Duration(total * sign);
    };

    return Duration;

}).call(this);

if (typeof module !== "undefined") {
   module.exports = Duration;
}
<|endoftext|>


<commit_before>(function() {
    if ("SimileWidgets_styles" in window) {
        var head = document.getElementsByTagName("head")[0];
        var styles = SimileWidgets_styles;
        for (var i = 0; i < styles.length; i++) {
            var link = document.createElement("link");
            link.href = styles[i];
            link.rel = "stylesheet";
            link.type = "text/css";
            head.appendChild(link);
        }
    }
    if ("SimileWidgets_scripts" in window) {
        var onLoad = window.SimileWidgets_onLoad;

        var scripts = SimileWidgets_scripts;
        var i = 0;
        var next = function() {
            if (i < scripts.length) {
                var url = scripts[i++];

                window.SimileWidgets_onLoad = arguments.callee;

                var script = document.createElement("script");
                script.src = url;
                script.type = "text/javascript";
                document.body.appendChild(script);
            } else {
                onLoad();
            }
        };
        next();
    }
})();<commit_msg>file appears to be unreferenced from anywhere and probably unnecessary with require.js<commit_after><|endoftext|>


<commit_before>'use strict';

var AWS = require('aws-sdk');
var memoize = require('./lib/util/memoizeWithOptions');
var promisifyAll = require('./lib/util/promisifyAll');

function dynamoDb(options) {
  return promisifyAll(new AWS.DynamoDB(options));
}

/**
 * Returns an instance of AWS.DynamoDB which has Promise methods
 * suffixed by "Promised"
 *
 * e.g.
 * getItem : getItemPromised
 *
 * @param options
 */
module.exports = memoize(dynamoDb);
<commit_msg>dynamoDb: add support for DynamoDB.DocumentClient<commit_after>'use strict';

var AWS = require('aws-sdk');
var memoize = require('./lib/util/memoizeWithOptions');
var promisifyAll = require('./lib/util/promisifyAll');

function dynamoDb(options) {
  return promisifyAll(new AWS.DynamoDB(options));
}

function documentClient(options) {
  return promisifyAll(new AWS.DynamoDB.DocumentClient(options));
}

/**
 * Returns an instance of AWS.DynamoDB which has Promise methods
 * suffixed by "Promised"
 *
 * e.g.
 * getItem : getItemPromised
 *
 * @param options
 */
module.exports = memoize(dynamoDb);

/**
 * Returns an instance of AWS.DynamoDB.DocumentClient
 * which has Promise methods suffixed by "Promised"
 *
 * e.g.
 * get: getPromised
 *
 * @param options
 */
module.exports.documentClient = memoize(documentClient);
<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
/* eslint-disable import/prefer-default-export */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useState, useEffect } from 'react';
import PropTypes from 'prop-types';

import { StyleSheet, TextInput, View, TouchableOpacity } from 'react-native';
import { MagnifyingGlass, Cancel } from './icons';
import { APP_FONT_FAMILY, WHITE } from '../globalStyles/index';
import { debounce } from '../utilities/index';

/**
 * Simple search bar - essentially a wrapper around a text input
 * with a magnifying glass icon and clear button.
 *
 * Debounces input by the user, such that the onChangeText callback
 * is only invoked once every `debounceTimeout`.
 *
 * NOTE: This component is exported MEMOIZED. See below for propsAreEqual
 * implementation.
 *
 * @param {String} color           Color of the entire component (monochrome only).
 * @param {String} onChangeText    Callback for changing text (debounced).
 * @param {String} value           Text value of the search input.
 * @param {String} placeholder     Placeholder value.
 * @param {String} autoFocus       Autofocus this component on mounting.
 * @param {String} textInputStyle  Style of the TextInput component.
 * @param {String} viewStyle       Style of the wrapping View component.
 * @param {String} debounceTimeout Time in milliseconds to debounce the onChangeText callback.
 * @param {Func}   onFocusOrBlur   Callback for onBlur and onFocus events.
 */
export const SearchBarComponent = ({
  color,
  onChangeText,
  value,
  placeholder,
  autoFocus,
  textInputStyle,
  viewStyle,
  debounceTimeout,
  onFocusOrBlur,
  ...textInputProps
}) => {
  const [textValue, setTextValue] = useState('');

  // Set the internal text value if it differs from the passed
  // value. Keeping the search bar text input up to date with
  // user input while debouncing actual filtering of data.
  useEffect(() => {
    if (value !== textValue) setTextValue(value);
  }, [value]);

  const internalViewStyle = { ...viewStyle, borderColor: color };
  const internalTextStyle = { ...textInputStyle, color };

  // To debounce a function, need to use the same function as the
  // last call. Memoize this to keep the reference semi-stable. Keeping
  // this reference is an optimization, not a requirement so can change.
  const debouncedCallback = useCallback(
    debounce(newValue => onChangeText(newValue), debounceTimeout),
    []
  );

  // On text change, set the internal text value, and call the debounced
  // callback. Keep the users input upto date, but optimize filtering.

  const onChangeTextCallback = useCallback(newValue => {
    setTextValue(newValue);
    debouncedCallback(newValue);
  }, []);

  return (
    <View style={internalViewStyle}>
      <MagnifyingGlass color={color} />
      <TextInput
        {...textInputProps}
        style={internalTextStyle}
        value={textValue}
        underlineColorAndroid="transparent"
        placeholderTextColor={color}
        placeholder={placeholder}
        onChangeText={onChangeTextCallback}
        autoFocus={autoFocus}
        onFocus={onFocusOrBlur}
        onBlur={onFocusOrBlur}
        selectTextOnFocus
      />
      {!!textValue && (
        <TouchableOpacity onPress={() => onChangeTextCallback('')}>
          <Cancel />
        </TouchableOpacity>
      )}
    </View>
  );
};

/**
 * Only re-render this component when the value or onFocusOrBlur
 * props change.
 */
const propsAreEqual = (
  { value: prevValue, onFocusOrBlur: prevOnFocusOrBlur },
  { value: nextValue, onFocusOrBlur: nextOnFocusOrBlur }
) => {
  const valuesAreEqual = prevValue === nextValue;
  const onFocusOrBlurAreEqual = prevOnFocusOrBlur === nextOnFocusOrBlur;
  return valuesAreEqual && onFocusOrBlurAreEqual;
};

export const SearchBar = React.memo(SearchBarComponent, propsAreEqual);

const defaultStyles = StyleSheet.create({
  container: {
    borderBottomWidth: 1,
    flexDirection: 'row',
    alignItems: 'center',
    marginHorizontal: 5,
  },
  textInput: {
    height: 40,
    fontSize: 20,
    fontFamily: APP_FONT_FAMILY,
    backgroundColor: 'rgba(0, 0, 0, 0)',
    flex: 1,
  },
});

SearchBarComponent.defaultProps = {
  debounceTimeout: 250,
  textInputStyle: defaultStyles.textInput,
  viewStyle: defaultStyles.container,
  color: WHITE,
  value: '',
  placeholder: '',
  autoFocus: false,
  onFocusOrBlur: null,
};

SearchBarComponent.propTypes = {
  color: PropTypes.string,
  debounceTimeout: PropTypes.number,
  onChangeText: PropTypes.func.isRequired,
  value: PropTypes.string,
  placeholder: PropTypes.string,
  autoFocus: PropTypes.bool,
  textInputStyle: PropTypes.object,
  viewStyle: PropTypes.object,
  onFocusOrBlur: PropTypes.func,
};
<commit_msg>tiny change<commit_after>/* eslint-disable react/forbid-prop-types */
/* eslint-disable import/prefer-default-export */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useState, useEffect } from 'react';
import PropTypes from 'prop-types';

import { StyleSheet, TextInput, View, TouchableOpacity } from 'react-native';
import { MagnifyingGlass, Cancel } from './icons';
import { APP_FONT_FAMILY, SUSSOL_ORANGE } from '../globalStyles/index';
import { debounce } from '../utilities/index';

/**
 * Simple search bar - essentially a wrapper around a text input
 * with a magnifying glass icon and clear button.
 *
 * Debounces input by the user, such that the onChangeText callback
 * is only invoked once every `debounceTimeout`.
 *
 * NOTE: This component is exported MEMOIZED. See below for propsAreEqual
 * implementation.
 *
 * @param {String} color           Color of the entire component (monochrome only).
 * @param {String} onChangeText    Callback for changing text (debounced).
 * @param {String} value           Text value of the search input.
 * @param {String} placeholder     Placeholder value.
 * @param {String} autoFocus       Autofocus this component on mounting.
 * @param {String} textInputStyle  Style of the TextInput component.
 * @param {String} viewStyle       Style of the wrapping View component.
 * @param {String} debounceTimeout Time in milliseconds to debounce the onChangeText callback.
 * @param {Func}   onFocusOrBlur   Callback for onBlur and onFocus events.
 */
export const SearchBarComponent = ({
  color,
  onChangeText,
  value,
  placeholder,
  autoFocus,
  textInputStyle,
  viewStyle,
  debounceTimeout,
  onFocusOrBlur,
  ...textInputProps
}) => {
  const [textValue, setTextValue] = useState('');

  // Set the internal text value if it differs from the passed
  // value. Keeping the search bar text input up to date with
  // user input while debouncing actual filtering of data.
  useEffect(() => {
    if (value !== textValue) setTextValue(value);
  }, [value]);

  const internalViewStyle = { ...viewStyle, borderColor: color };
  const internalTextStyle = { ...textInputStyle, color };

  // To debounce a function, need to use the same function as the
  // last call. Memoize this to keep the reference semi-stable. Keeping
  // this reference is an optimization, not a requirement so can change.
  const debouncedCallback = useCallback(
    debounce(newValue => onChangeText(newValue), debounceTimeout),
    []
  );

  // On text change, set the internal text value, and call the debounced
  // callback. Keep the users input upto date, but optimize filtering.

  const onChangeTextCallback = useCallback(newValue => {
    setTextValue(newValue);
    debouncedCallback(newValue);
  }, []);

  return (
    <View style={internalViewStyle}>
      <MagnifyingGlass color={color} />
      <TextInput
        {...textInputProps}
        style={internalTextStyle}
        value={textValue}
        underlineColorAndroid="transparent"
        placeholderTextColor={color}
        placeholder={placeholder}
        onChangeText={onChangeTextCallback}
        autoFocus={autoFocus}
        onFocus={onFocusOrBlur}
        onBlur={onFocusOrBlur}
        selectTextOnFocus
      />
      {!!textValue && (
        <TouchableOpacity onPress={() => onChangeTextCallback('')}>
          <Cancel />
        </TouchableOpacity>
      )}
    </View>
  );
};

/**
 * Only re-render this component when the value or onFocusOrBlur
 * props change.
 */
const propsAreEqual = (
  { value: prevValue, onFocusOrBlur: prevOnFocusOrBlur },
  { value: nextValue, onFocusOrBlur: nextOnFocusOrBlur }
) => {
  const valuesAreEqual = prevValue === nextValue;
  const onFocusOrBlurAreEqual = prevOnFocusOrBlur === nextOnFocusOrBlur;
  return valuesAreEqual && onFocusOrBlurAreEqual;
};

export const SearchBar = React.memo(SearchBarComponent, propsAreEqual);

const defaultStyles = StyleSheet.create({
  container: {
    borderBottomWidth: 1,
    flexDirection: 'row',
    alignItems: 'center',
    marginHorizontal: 5,
  },
  textInput: {
    height: 40,
    fontSize: 20,
    fontFamily: APP_FONT_FAMILY,
    backgroundColor: 'rgba(0, 0, 0, 0)',
    flex: 1,
  },
});

SearchBarComponent.defaultProps = {
  debounceTimeout: 250,
  textInputStyle: defaultStyles.textInput,
  viewStyle: defaultStyles.container,
  color: SUSSOL_ORANGE,
  value: '',
  placeholder: '',
  autoFocus: false,
  onFocusOrBlur: null,
};

SearchBarComponent.propTypes = {
  color: PropTypes.string,
  debounceTimeout: PropTypes.number,
  onChangeText: PropTypes.func.isRequired,
  value: PropTypes.string,
  placeholder: PropTypes.string,
  autoFocus: PropTypes.bool,
  textInputStyle: PropTypes.object,
  viewStyle: PropTypes.object,
  onFocusOrBlur: PropTypes.func,
};
<|endoftext|>


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

// This script is too long and could be vastly simplified by using a mix of
// a CloudFormation template and two API calls: create elasticache replication
// group and create read replica cache cluster.

var Promise = require('es6-promise').Promise;
var AWS = require('aws-sdk');

var elasticache = new AWS.ElastiCache({
  "apiVersion": "2014-07-15",
  "region": "ap-southeast-2"
});

var randomString = function() {
  return Math.random().toString(36).substr(2, 6);
};

var suffix = randomString();
var primaryClusterId = "primary-2a-" + suffix;
var replicationGroupId = "repgroup-" + suffix;
var readReplicaClusterId = "replica-2b-" + suffix;

var primaryCacheClusterParams = {
  "CacheClusterId": primaryClusterId,
  "CacheNodeType": "cache.t2.micro",
  "Engine": "redis",
  "NumCacheNodes": "1",
  "PreferredAvailabilityZone": "ap-southeast-2a"
};

var replicationGroupParams = {
  "PrimaryClusterId": primaryClusterId,
  "ReplicationGroupDescription": "Replication Group",
  "ReplicationGroupId": replicationGroupId
};

var readReplicaClusterParams = {
  "CacheClusterId": readReplicaClusterId,
  "PreferredAvailabilityZone": "ap-southeast-2b",
  "ReplicationGroupId": replicationGroupId
};

var ec2 = new AWS.EC2({
  "apiVersion": '2014-06-15',
  "region": "ap-southeast-2"
});

var elasticacheSecurityGroupName = "elasticache-redis-access-internal";

new Promise(function(resolve, reject) {
  ec2.describeSecurityGroups({"GroupNames": [elasticacheSecurityGroupName]}, function(err, data) {
    if (err) {
      if (err.code === "InvalidGroup.NotFound") {
        console.log("Security group for ElastiCache Redis cache cluster does not exist. Creating now.");
        resolve(false);  // security group doesn't exist, let's create it
      } else {
        reject(err);
      }
    } else {
      console.log("Security group for ElastiCache Redis cache cluster already exists. Skipping creation.");
      resolve(true);  // security group exists, no need to create it
    }
  });
}).then(function(securityGroupExists) {
  if (!securityGroupExists) {
    return new Promise(function(resolve, reject) {
      ec2.describeVpcs({}, function(err, data) {
        if (err) {
          reject(err);
        } else {
          resolve(data.Vpcs[0].CidrBlock);
        }
      });
    }).then(function(cidrBlock) {
      return new Promise(function(resolve, reject) {
        var securityGroupParams = {
          "GroupName": elasticacheSecurityGroupName,
          "Description": "Enable Redis access on port 6379 from machines in VPC"
        };
        ec2.createSecurityGroup(securityGroupParams, function(err, data) {
          if (err) {
            reject(err);
          } else {
            resolve(cidrBlock);
          }
        });
      })
    }).then(function(cidrBlock) {
      return new Promise(function(resolve, reject) {
        var securityGroupIngressParams = {
          "CidrIp": cidrBlock,
          "FromPort": 6379,
          "ToPort": 6379,
          "GroupName": elasticacheSecurityGroupName,
          "IpProtocol": "tcp"
        };
        ec2.authorizeSecurityGroupIngress(securityGroupIngressParams, function(err, data) {
          if (err) {
            reject(err);
          } else {
            resolve(data);
          }
        });
      })
    }).then(function() {
      return new Promise(function(resolve, reject) {
        var securityGroupEgressParams = {
          "FromPort": -1,
          "ToPort": -1,
          "GroupName": elasticacheSecurityGroupName,
          "IpProtocol": "-1"
        };
        ec2.authorizeSecurityGroupIngress(securityGroupEgressParams, function(err, data) {
          if (err) {
            reject(err);
          } else {
            resolve(data);
          }
        });
      })
    });
  }
}).then(function() {
  return new Promise(function(resolve, reject) {
    elasticache.createCacheCluster(primaryCacheClusterParams, function(err, data) {
      if (err) {
        reject(err);
      } else {
        console.log(data);           // successful response
        resolve(data);
      }
    })
  });
}).then(function(data) {
  return new Promise(function(resolve, reject) {
    (function pollUntilAvailable() {
      setTimeout(function(){
        elasticache.describeCacheClusters({ "CacheClusterId": primaryClusterId }, function(err, data) {
          if (err) {
            reject(err);
          } else {
            var status = data.CacheClusters[0].CacheClusterStatus;
            if (status === "available") {
              console.log("Cache cluster [" + primaryClusterId +"] is [" + status + "]");
              resolve(status);
            } else {
              console.log("Cache cluster status for [" + primaryClusterId + "] is [" + status + "], waiting...");
              pollUntilAvailable();
            }
          }
        });
      }, 10 * 1000);
    })();
  });
}).then(function(status) {
  return new Promise(function(resolve, reject) {
    elasticache.createReplicationGroup(replicationGroupParams, function(err, data) {
      if (err) {
        reject(err);
      } else {
        console.log(data);
        resolve(data);
      }
    });
  });
}).then(function(data) {
  return new Promise(function(resolve, reject) {
    (function pollUntilAvailable() {
      setTimeout(function(){
        elasticache.describeReplicationGroups({ "ReplicationGroupId": replicationGroupId, "MaxRecords": 20 }, function(err, data) {
          if (err) {
            reject(err);
          } else {
            var status = data.ReplicationGroups[0].Status;
            if (status === "available") {
              console.log("Replication group [" + replicationGroupId +"] is [" + status + "]");
              resolve(status);
            } else {
              console.log("Replication group status for [" + primaryClusterId + "] is [" + status + "], waiting...");
              pollUntilAvailable();
            }
          }
        });
      }, 10 * 1000);
    })();
  });
}).then(function(status) {
  return new Promise(function(resolve, reject) {
    elasticache.createCacheCluster(readReplicaClusterParams, function(err, data) {
      if (err) {
        reject(err);
      } else {
        console.log(data);
        resolve(data);
      }
    });
  });
}).then(function(data) {
  return new Promise(function(resolve, reject) {
    (function pollUntilAvailable() {
      setTimeout(function(){
        elasticache.describeCacheClusters({ "CacheClusterId": readReplicaClusterId }, function(err, data) {
          if (err) {
            reject(err);
          } else {
            var status = data.CacheClusters[0].CacheClusterStatus;
            if (status === "available") {
              console.log("Read replica cache cluster [" + readReplicaClusterId +"] is [" + status + "]");
              resolve(status);
            } else {
              console.log("Read replica cache cluster status for [" + readReplicaClusterId + "] is [" + status + "], waiting...");
              pollUntilAvailable();
            }
          }
        });
      }, 10 * 1000);
    })();
  });
}).catch(function(err) {
  console.log(err, err.stack);
});
<commit_msg>WIP on ec stack, going to be replaced<commit_after>#!/usr/bin/env node

// This script is too long and could be vastly simplified by using a mix of
// a CloudFormation template and two API calls: create elasticache replication
// group and create read replica cache cluster.

var Promise = require('es6-promise').Promise;
var AWS = require('aws-sdk');

var creds = new AWS.SharedIniFileCredentials({
  profile: 'company'
});
AWS.config.credentials = creds;

var elasticache = new AWS.ElastiCache({
  "apiVersion": "2014-07-15",
  "region": "ap-southeast-2"
});

var randomString = function() {
  return Math.random().toString(36).substr(2, 6);
};

var suffix = randomString();
var primaryClusterId = "primary-2a-" + suffix;
var replicationGroupId = "repgroup-" + suffix;
var readReplicaClusterId = "replica-2b-" + suffix;

var ec2 = new AWS.EC2({
  "apiVersion": '2014-06-15',
  "region": "ap-southeast-2"
});

function extractPublicAndPrivateSubnets(stack) {
  var describeSubnetPromises = ['Public subnet', 'Private subnet'].
    map(function(subnetTag) {
      return new Promise(function(resolve, reject) {
        ec2.describeSubnets({
          Filters: [{
            Name: 'vpc-id',
            Values: [stack.vpc.VpcId]
          }, {
            Name: 'tag:Name',
            Values: [subnetTag]
          }]
        }, function(err, data) {
          if (err) {
            reject(err)
          } else {
            resolve(data);
          }
        });
      });
    });

  return Promise.all(describeSubnetPromises).
    then(function(subnets) {
      stack.publicSubnetId = subnets[0].Subnets[0].SubnetId;
      stack.privateSubnetId = subnets[1].Subnets[0].SubnetId;

      return stack;
    });
}

var cacheSubnetGroupName = 'cache-private-subnet-' + suffix;
var elasticacheSecurityGroupName = "elasticache-redis-access-internal-" + suffix;

new Promise(function(resolve, reject) {
  ec2.describeSecurityGroups({"GroupNames": [elasticacheSecurityGroupName]}, function(err, data) {
    if (err) {
      if (err.code === "InvalidGroup.NotFound") {
        console.log("Security group for ElastiCache Redis cache cluster does not exist. Creating now.");
        resolve(false);  // security group doesn't exist, let's create it
      } else {
        reject(err);
      }
    } else {
      console.log("Security group for ElastiCache Redis cache cluster already exists. Skipping creation.");
      resolve(true);  // security group exists, no need to create it
    }
  });
}).then(function(securityGroupExists) {
  if (!securityGroupExists) {
    return new Promise(function(resolve, reject) {
      ec2.describeVpcs({ Filters: [{ Name: 'tag-key', Values: ['online-tax'] }]}, function(err, data) {
        if (err) {
          reject(err);
        } else {
          resolve(data.Vpcs[0]);
        }
      });
    }).then(function(vpc) {
      return new Promise(function(resolve, reject) {
        var securityGroupParams = {
          "GroupName": elasticacheSecurityGroupName,
          "Description": "Enable Redis access on port 6379 from machines in VPC",
          "VpcId": vpc.VpcId
        };
        ec2.createSecurityGroup(securityGroupParams, function(err, data) {
          if (err) {
            reject(err);
          } else {
            resolve({
              vpc: vpc,
              securityGroup: data
            });
          }
        });
      })
    }).then(function(stack) {
      return new Promise(function(resolve, reject) {
        var securityGroupIngressParams = {
          "CidrIp": stack.vpc.CidrBlock,
          "FromPort": 6379,
          "ToPort": 6379,
          "GroupId": stack.securityGroup.GroupId,
          "IpProtocol": "tcp"
        };
        ec2.authorizeSecurityGroupIngress(securityGroupIngressParams, function(err, data) {
          if (err) {
            reject(err);
          } else {
            resolve(stack);
          }
        });
      })
    });
  }
}).then(extractPublicAndPrivateSubnets).
then(function(stack) {
  return new Promise(function(resolve, reject) {
    elasticache.createCacheSubnetGroup({
      CacheSubnetGroupDescription: 'Private subnet cache group',
      CacheSubnetGroupName: cacheSubnetGroupName,
      SubnetIds: [stack.privateSubnetId]
    }, function(err, data) {
      if (err) {
        reject(err);
      } else {
        resolve(stack)
      }
    })
  });
}).then(function(stack) {
  return new Promise(function(resolve, reject) {
    elasticache.createCacheCluster({
      "CacheClusterId": primaryClusterId,
      "CacheNodeType": "cache.t2.micro",
      "Engine": "redis",
      "NumCacheNodes": "1",
      "PreferredAvailabilityZone": "ap-southeast-2a",
      "SecurityGroupIds": [stack.securityGroup.GroupId],
      "CacheSubnetGroupName": cacheSubnetGroupName
    }, function(err, data) {
      if (err) {
        reject(err);
      } else {
        console.log(data);           // successful response
        resolve(data);
      }
    })
  });
}).then(function(data) {
  return new Promise(function(resolve, reject) {
    (function pollUntilAvailable() {
      setTimeout(function(){
        elasticache.describeCacheClusters({ "CacheClusterId": primaryClusterId }, function(err, data) {
          if (err) {
            reject(err);
          } else {
            var status = data.CacheClusters[0].CacheClusterStatus;
            if (status === "available") {
              console.log("Cache cluster [" + primaryClusterId +"] is [" + status + "]");
              resolve(status);
            } else {
              console.log("Cache cluster status for [" + primaryClusterId + "] is [" + status + "], waiting...");
              pollUntilAvailable();
            }
          }
        });
      }, 10 * 1000);
    })();
  });
}).then(function(status) {
  return new Promise(function(resolve, reject) {
    elasticache.createReplicationGroup({
      "PrimaryClusterId": primaryClusterId,
      "ReplicationGroupDescription": "Replication Group",
      "ReplicationGroupId": replicationGroupId
    }, function(err, data) {
      if (err) {
        reject(err);
      } else {
        console.log(data);
        resolve(data);
      }
    });
  });
}).then(function(data) {
  return new Promise(function(resolve, reject) {
    (function pollUntilAvailable() {
      setTimeout(function(){
        elasticache.describeReplicationGroups({ "ReplicationGroupId": replicationGroupId, "MaxRecords": 20 }, function(err, data) {
          if (err) {
            reject(err);
          } else {
            var status = data.ReplicationGroups[0].Status;
            if (status === "available") {
              console.log("Replication group [" + replicationGroupId +"] is [" + status + "]");
              resolve(status);
            } else {
              console.log("Replication group status for [" + primaryClusterId + "] is [" + status + "], waiting...");
              pollUntilAvailable();
            }
          }
        });
      }, 10 * 1000);
    })();
  });
}).then(function(status) {
  return new Promise(function(resolve, reject) {
    elasticache.createCacheCluster({
      "CacheClusterId": readReplicaClusterId,
      "PreferredAvailabilityZone": "ap-southeast-2b",
      "ReplicationGroupId": replicationGroupId
    }, function(err, data) {
      if (err) {
        reject(err);
      } else {
        console.log(data);
        resolve(data);
      }
    });
  });
}).then(function(data) {
  return new Promise(function(resolve, reject) {
    (function pollUntilAvailable() {
      setTimeout(function(){
        elasticache.describeCacheClusters({ "CacheClusterId": readReplicaClusterId }, function(err, data) {
          if (err) {
            reject(err);
          } else {
            var status = data.CacheClusters[0].CacheClusterStatus;
            if (status === "available") {
              console.log("Read replica cache cluster [" + readReplicaClusterId +"] is [" + status + "]");
              resolve(status);
            } else {
              console.log("Read replica cache cluster status for [" + readReplicaClusterId + "] is [" + status + "], waiting...");
              pollUntilAvailable();
            }
          }
        });
      }, 10 * 1000);
    })();
  });
}).catch(function(err) {
  console.log(err, err.stack);
});
<|endoftext|>


<commit_before>function embed360(selectors, aframeSrc) {
  aframeSrc = aframeSrc || "https://cdnjs.cloudflare.com/ajax/libs/aframe/0.5.0/aframe.min.js";
  var elementList = document.querySelectorAll(selectors);
  for (var i = 0; i < elementList.length; ++i) {
    var el = elementList[i];
    var img = el.querySelector('img');
    if (img) {
      makeIframe(el, img);
    }
  }

  function makeIframe(el, img) {
    var imgsrc = img.src;
    el.removeChild(img);
    var rotation = el.dataset.rotation || "";
    var iframe = document.createElement('iframe');
    var content = '<!DOCTYPE html>' +
      '<title>A photo in aframe in an iframe</title>' +
      '


<commit_before>'use strict';

// ---------------------------------------------------------------------------

const zb = require ('./zb.js');
const { ExchangeError } = require ('./base/errors');

// ---------------------------------------------------------------------------

module.exports = class chbtc extends zb {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'chbtc',
            'name': 'CHBTC',
            'countries': 'CN',
            'rateLimit': 1000,
            'version': 'v1',
            'has': {
                'CORS': false,
                'fetchOrder': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/28555659-f0040dc2-7109-11e7-9d99-688a438bf9f4.jpg',
                'api': {
                    'public': 'http://api.chbtc.com/data', // no https for public API
                    'private': 'https://trade.chbtc.com/api',
                },
                'www': 'https://trade.chbtc.com/api',
                'doc': 'https://www.chbtc.com/i/developer',
            },
            'markets': {
                'BTC/CNY': { 'id': 'btc_cny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY' },
                'LTC/CNY': { 'id': 'ltc_cny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY' },
                'ETH/CNY': { 'id': 'eth_cny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY' },
                'ETC/CNY': { 'id': 'etc_cny', 'symbol': 'ETC/CNY', 'base': 'ETC', 'quote': 'CNY' },
                'BTS/CNY': { 'id': 'bts_cny', 'symbol': 'BTS/CNY', 'base': 'BTS', 'quote': 'CNY' },
                // 'EOS/CNY': { 'id': 'eos_cny', 'symbol': 'EOS/CNY', 'base': 'EOS', 'quote': 'CNY' },
                'BCH/CNY': { 'id': 'bcc_cny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY' },
                'HSR/CNY': { 'id': 'hsr_cny', 'symbol': 'HSR/CNY', 'base': 'HSR', 'quote': 'CNY' },
                'QTUM/CNY': { 'id': 'qtum_cny', 'symbol': 'QTUM/CNY', 'base': 'QTUM', 'quote': 'CNY' },
            },
        });
    }

    getMarketFieldName () {
        return 'currency';
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if (api === 'private') {
            if ('code' in response)
                throw new ExchangeError (this.id + ' ' + this.json (response));
        }
        if ('result' in response) {
            if (!response['result'])
                throw new ExchangeError (this.id + ' ' + this.json (response));
        }
        return response;
    }
};
<commit_msg>more linting to chbtc #1820<commit_after>'use strict';

// ---------------------------------------------------------------------------

const zb = require ('./zb.js');
const { ExchangeError } = require ('./base/errors');

// ---------------------------------------------------------------------------

module.exports = class chbtc extends zb {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'chbtc',
            'name': 'CHBTC',
            'countries': 'CN',
            'rateLimit': 1000,
            'version': 'v1',
            'has': {
                'CORS': false,
                'fetchOrder': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/28555659-f0040dc2-7109-11e7-9d99-688a438bf9f4.jpg',
                'api': {
                    'public': 'http://api.chbtc.com/data', // no https for public API
                    'private': 'https://trade.chbtc.com/api',
                },
                'www': 'https://trade.chbtc.com/api',
                'doc': 'https://www.chbtc.com/i/developer',
            },
            'markets': {
                'BTC/CNY': { 'id': 'btc_cny', 'symbol': 'BTC/CNY', 'base': 'BTC', 'quote': 'CNY' },
                'LTC/CNY': { 'id': 'ltc_cny', 'symbol': 'LTC/CNY', 'base': 'LTC', 'quote': 'CNY' },
                'ETH/CNY': { 'id': 'eth_cny', 'symbol': 'ETH/CNY', 'base': 'ETH', 'quote': 'CNY' },
                'ETC/CNY': { 'id': 'etc_cny', 'symbol': 'ETC/CNY', 'base': 'ETC', 'quote': 'CNY' },
                'BTS/CNY': { 'id': 'bts_cny', 'symbol': 'BTS/CNY', 'base': 'BTS', 'quote': 'CNY' },
                // 'EOS/CNY': { 'id': 'eos_cny', 'symbol': 'EOS/CNY', 'base': 'EOS', 'quote': 'CNY' },
                'BCH/CNY': { 'id': 'bcc_cny', 'symbol': 'BCH/CNY', 'base': 'BCH', 'quote': 'CNY' },
                'HSR/CNY': { 'id': 'hsr_cny', 'symbol': 'HSR/CNY', 'base': 'HSR', 'quote': 'CNY' },
                'QTUM/CNY': { 'id': 'qtum_cny', 'symbol': 'QTUM/CNY', 'base': 'QTUM', 'quote': 'CNY' },
            },
        });
    }

    getMarketFieldName () {
        return 'currency';
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if (api === 'private') {
            if ('code' in response)
                throw new ExchangeError (this.id + ' ' + this.json (response));
        }
        if ('result' in response) {
            if (!response['result'])
                throw new ExchangeError (this.id + ' ' + this.json (response));
        }
        return response;
    }
};
<|endoftext|>