<commit_before>/**
 * Projection class for Baidu Spherical Mercator
 *
 * @class BaiduSphericalMercator
 */
L.Projection.BaiduSphericalMercator = {
    /**
     * Project latLng to point coordinate
     *
     * @method project
     * @param {Object} latLng coordinate for a point on earth
     * @return {Object} leafletPoint point coordinate of L.Point
     */
    project: function(latLng) {
        var projection = new BMap.MercatorProjection();
        var point = projection.lngLatToPoint(
            new BMap.Point(latLng.lng, latLng.lat)
        );
        var leafletPoint = new L.Point(point.x, point.y);
        return leafletPoint;
    },

    /**
     * unproject point coordinate to latLng
     *
     * @method unproject
     * @param {Object} bpoint baidu point coordinate
     * @return {Object} latitude and longitude
     */
    unproject: function (bpoint) {
        var projection= new BMap.MercatorProjection();
        var point = projection.pointToLngLat(
            new BMap.Pixel(bpoint.x, bpoint.y)
        );
        var latLng = new L.LatLng(point.lat, point.lng);
        return latLng;
    },

    /**
     * Don't know how it used currently.
     *
     * However, I guess this is the range of coordinate.
     * Range of pixel coordinate is gotten from
     * BMap.MercatorProjection.lngLatToPoint(180, -90) and (180, 90)
     * After getting max min value of pixel coordinate, use
     * pointToLngLat() get the max lat and Lng.
     */
    bounds: (function () {
        var MAX_X= 20037726.37;
        var MIN_Y= -11708041.66;
        var MAX_Y= 12474104.17;
        var bounds = L.bounds(
            [-MAX_X, MIN_Y], //180, -71.988531
            [MAX_X, MAX_Y]  //-180, 74.000022
        );
        return bounds;
    })()
};

/**
 * Transformation class for Baidu Transformation.
 * Basically, it contains the conversion of point coordinate and
 * pixel coordinate.
 *
 * @class BTransformation
 */
L.BTransformation = function () {
};

L.BTransformation.prototype = {
    MAXZOOM: 18,
    /**
     * Don't know how it used currently.
     */
    transform: function (point, zoom) {
        return this._transform(point.clone(), zoom);
    },

    /**
     * transform point coordinate to pixel coordinate
     *
     * @method _transform
     * @param {Object} point point coordinate
     * @param {Number} zoom zoom level of the map
     * @return {Object} point, pixel coordinate
     */
    _transform: function (point, zoom) {
        point.x = point.x >> (this.MAXZOOM - zoom);
        point.y = point.y >> (this.MAXZOOM - zoom);
        return point;
    },

    /**
     * transform pixel coordinate to point coordinate
     *
     * @method untransform
     * @param {Object} point pixel coordinate
     * @param {Number} zoom zoom level of the map
     * @return {Object} point, point coordinate
     */
    untransform: function (point, zoom) {
        point.x = point.x << (this.MAXZOOM - zoom);
        point.y = point.y << (this.MAXZOOM - zoom);
        return point;
    }
};

/**
 * Coordinate system for Baidu EPSG3857
 *
 * @class BEPSG3857
 */
L.CRS.BEPSG3857 = L.extend({}, L.CRS, {
    /**
     * transform latLng to pixel coordinate
     *
     * @method untransform
     * @param {Object} latlng latitude and longitude
     * @param {Number} zoom zoom level of the map
     * @return {Object} pixel coordinate calculated for latLng
     */
    latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
        var projectedPoint = this.projection.project(latlng);
        return this.transformation._transform(projectedPoint, zoom);
    },

    /**
     * transform pixel coordinate to latLng
     *
     * @method untransform
     * @param {Object} point pixel coordinate
     * @param {Number} zoom zoom level of the map
     * @return {Object} latitude and longitude
     */
    pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
        var untransformedPoint = this.transformation.untransform(point, zoom);
        return this.projection.unproject(untransformedPoint);
    },

    code: 'EPSG:3857',
    projection: L.Projection.BaiduSphericalMercator,

    transformation: new L.BTransformation()
});

/**
 * Tile layer for Baidu Map
 *
 * @class Baidu
 */
L.Baidu = L.TileLayer.extend({
    options: {
        subdomains: ['online1', 'online2', 'online3'],
        //TODO: decode utf8 characters in attribution
        attribution: '© 2014 Baidu - GS(2012)6003;- Data © NavInfo & CenNavi & DaoDaoTong',
    },

    /**
     * initialize the map with key and tile URL
     *
     * @method initialize
     * @param {String} key access key of baidu map
     * @param {Object} options, option of the map
     */
    initialize: function (key, options) {
        L.Util.setOptions(this, options);
        this._key = key;
        this._url = 'http://{subdomain}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=pl&udt=20140711';
    },

    /**
     * Set the corresponding position of tiles in baidu map.
     * if point.y is less or equal than 256, i.e. 35=>291, -221=>547
     * if point.y is greater than 256, i.e. 291=>35, 547=>-221
     *
     * @method _getTilePos
     * @param {Object} tilePoint tile coordinate
     * @return {Object} point left and top property of
     */
    _getTilePos: function (tilePoint) {
        var origin = this._map.getPixelOrigin();
        var tileSize = this._getTileSize();

        var point = tilePoint.multiplyBy(tileSize).subtract(origin);
        if (point.y <= 256) {
            point.y = (
                2 * Math.abs(
                    Math.floor(point.y / tileSize)
                ) + 1
            ) * tileSize + point.y;
        } else {
            point.y = point.y - (
                Math.floor(point.y / tileSize) * 2 - 1
            ) * tileSize;
        }
        return point;
    },

    /**
     * get a tile url of the map
     *
     * @method getTileUrl
     * @param {Object} coords, tile coordinate
     * @return {String} url of a tile
     */
    getTileUrl: function(coords) {
        return this._url.replace('{subdomain}', this._getSubdomain(coords))
            .replace('{x}', coords.x)
            .replace('{y}', coords.y)
            .replace('{z}', this._getZoomForUrl());
    }
});

L.map = function (id, options) {
    var map = new L.Map(id, options);

    /**
     * load new tiles when set zoom for baidu map
     * Works well: mouse scroll. zoom level <= 14 in double click
     * Works not well: zoom level > 14. Not Accurate at all.
     * TODO: figure out why not accurate. Potential: CRS differences.
     *
     * @method _setZoomAroundBaidu
     * @param {Object} latlng position of mouse clicked on the canvas
     * @param {Number} zoom zoom level
     * @param {Object} options options of the map
     * @return {Object} TODO: not sure for now. probably the map itself
     */
    var setZoomAroundBaidu = function (latlng, zoom, options) {
        var scale = this.getZoomScale(zoom);
        var viewHalf = this.getSize().divideBy(2);
        var containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng);
        var centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale);
        var newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
        var oldCenterLat = this.getCenter().lat;
        //add offset rather than minus it
        newCenter.lat = oldCenterLat - newCenter.lat + oldCenterLat;
        return this.setView(newCenter, zoom, {zoom: options});
    };

    /**
     * Override _getTopLeftPoint method. For Baidu Map, if dragging
     * down side of the map, y will increase rather than decrease.
     * vice versa.
     *
     * @method _getTopLeftPoint
     * @return {Object} point top left point
     */
    var _getTopLeftPointBaidu = function () {
        var pixel = this.getPixelOrigin();
        var pane = this._getMapPanePos();
        var point = new L.Point(pixel.x - pane.x, pixel.y + pane.y);
        return point;
    };

    /**
     * Override _update method in map. redefine bounds.
     * Pros: no blank row on the top or bottom
     * Cons: some times it might load a row that is not necessary.
     *
     * @method _updateBaidu
     */
    var _updateBaidu = function () {
        if (!this._map) { return; }
        var map = this._map,
            bounds = map.getPixelBounds(),
            zoom = map.getZoom(),
            tileSize = this._getTileSize();
        if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
            return;
        }

        boundsMax = bounds.max.divideBy(tileSize);
        boundsMax.x = Math.floor(boundsMax.x);
        boundsMax.y = Math.ceil(boundsMax.y);

        var tileBounds = L.bounds(
            bounds.min.divideBy(tileSize)._floor(),
            boundsMax
        );

        this._addTilesFromCenterOut(tileBounds);
        if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
            this._removeOtherTiles(tileBounds);
        }
    };

    //if option has baidu, use custom method
    if (options.baidu === true) {
        map._getTopLeftPoint = _getTopLeftPointBaidu;
        map.setZoomAround = setZoomAroundBaidu;
        map._update = _updateBaidu;
    }
    return map;
};

L.baiduLayer = function (key, options) {
    return new L.Baidu(key, options);
};
<commit_msg>re #10: move _update to TileLayer<commit_after>/**
 * Projection class for Baidu Spherical Mercator
 *
 * @class BaiduSphericalMercator
 */
L.Projection.BaiduSphericalMercator = {
    /**
     * Project latLng to point coordinate
     *
     * @method project
     * @param {Object} latLng coordinate for a point on earth
     * @return {Object} leafletPoint point coordinate of L.Point
     */
    project: function(latLng) {
        var projection = new BMap.MercatorProjection();
        var point = projection.lngLatToPoint(
            new BMap.Point(latLng.lng, latLng.lat)
        );
        var leafletPoint = new L.Point(point.x, point.y);
        return leafletPoint;
    },

    /**
     * unproject point coordinate to latLng
     *
     * @method unproject
     * @param {Object} bpoint baidu point coordinate
     * @return {Object} latitude and longitude
     */
    unproject: function (bpoint) {
        var projection= new BMap.MercatorProjection();
        var point = projection.pointToLngLat(
            new BMap.Pixel(bpoint.x, bpoint.y)
        );
        var latLng = new L.LatLng(point.lat, point.lng);
        return latLng;
    },

    /**
     * Don't know how it used currently.
     *
     * However, I guess this is the range of coordinate.
     * Range of pixel coordinate is gotten from
     * BMap.MercatorProjection.lngLatToPoint(180, -90) and (180, 90)
     * After getting max min value of pixel coordinate, use
     * pointToLngLat() get the max lat and Lng.
     */
    bounds: (function () {
        var MAX_X= 20037726.37;
        var MIN_Y= -11708041.66;
        var MAX_Y= 12474104.17;
        var bounds = L.bounds(
            [-MAX_X, MIN_Y], //180, -71.988531
            [MAX_X, MAX_Y]  //-180, 74.000022
        );
        return bounds;
    })()
};

/**
 * Transformation class for Baidu Transformation.
 * Basically, it contains the conversion of point coordinate and
 * pixel coordinate.
 *
 * @class BTransformation
 */
L.BTransformation = function () {
};

L.BTransformation.prototype = {
    MAXZOOM: 18,
    /**
     * Don't know how it used currently.
     */
    transform: function (point, zoom) {
        return this._transform(point.clone(), zoom);
    },

    /**
     * transform point coordinate to pixel coordinate
     *
     * @method _transform
     * @param {Object} point point coordinate
     * @param {Number} zoom zoom level of the map
     * @return {Object} point, pixel coordinate
     */
    _transform: function (point, zoom) {
        point.x = point.x >> (this.MAXZOOM - zoom);
        point.y = point.y >> (this.MAXZOOM - zoom);
        return point;
    },

    /**
     * transform pixel coordinate to point coordinate
     *
     * @method untransform
     * @param {Object} point pixel coordinate
     * @param {Number} zoom zoom level of the map
     * @return {Object} point, point coordinate
     */
    untransform: function (point, zoom) {
        point.x = point.x << (this.MAXZOOM - zoom);
        point.y = point.y << (this.MAXZOOM - zoom);
        return point;
    }
};

/**
 * Coordinate system for Baidu EPSG3857
 *
 * @class BEPSG3857
 */
L.CRS.BEPSG3857 = L.extend({}, L.CRS, {
    /**
     * transform latLng to pixel coordinate
     *
     * @method untransform
     * @param {Object} latlng latitude and longitude
     * @param {Number} zoom zoom level of the map
     * @return {Object} pixel coordinate calculated for latLng
     */
    latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point
        var projectedPoint = this.projection.project(latlng);
        return this.transformation._transform(projectedPoint, zoom);
    },

    /**
     * transform pixel coordinate to latLng
     *
     * @method untransform
     * @param {Object} point pixel coordinate
     * @param {Number} zoom zoom level of the map
     * @return {Object} latitude and longitude
     */
    pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng
        var untransformedPoint = this.transformation.untransform(point, zoom);
        return this.projection.unproject(untransformedPoint);
    },

    code: 'EPSG:3857',
    projection: L.Projection.BaiduSphericalMercator,

    transformation: new L.BTransformation()
});

/**
 * Tile layer for Baidu Map
 *
 * @class Baidu
 */
L.Baidu = L.TileLayer.extend({
    options: {
        subdomains: ['online1', 'online2', 'online3'],
        //TODO: decode utf8 characters in attribution
        attribution: '© 2014 Baidu - GS(2012)6003;- Data © NavInfo & CenNavi & DaoDaoTong',
    },

    /**
     * initialize the map with key and tile URL
     *
     * @method initialize
     * @param {String} key access key of baidu map
     * @param {Object} options, option of the map
     */
    initialize: function (key, options) {
        L.Util.setOptions(this, options);
        this._key = key;
        this._url = 'http://{subdomain}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=pl&udt=20140711';
    },

    /**
     * Set the corresponding position of tiles in baidu map.
     * if point.y is less or equal than 256, i.e. 35=>291, -221=>547
     * if point.y is greater than 256, i.e. 291=>35, 547=>-221
     *
     * @method _getTilePos
     * @param {Object} tilePoint tile coordinate
     * @return {Object} point left and top property of
     */
    _getTilePos: function (tilePoint) {
        var origin = this._map.getPixelOrigin();
        var tileSize = this._getTileSize();

        var point = tilePoint.multiplyBy(tileSize).subtract(origin);
        if (point.y <= 256) {
            point.y = (
                2 * Math.abs(
                    Math.floor(point.y / tileSize)
                ) + 1
            ) * tileSize + point.y;
        } else {
            point.y = point.y - (
                Math.floor(point.y / tileSize) * 2 - 1
            ) * tileSize;
        }
        return point;
    },

    /**
     * Override _update method in map. redefine bounds.
     * Pros: no blank row on the top or bottom
     * Cons: some times it might load a row that is not necessary.
     *
     * @method _updateBaidu
     */
     _update: function () {
        if (!this._map) { return; }
        var map = this._map,
            bounds = map.getPixelBounds(),
            zoom = map.getZoom(),
            tileSize = this._getTileSize();
        if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
            return;
        }

        boundsMax = bounds.max.divideBy(tileSize);
        boundsMax.x = Math.floor(boundsMax.x);
        boundsMax.y = Math.ceil(boundsMax.y);

        var tileBounds = L.bounds(
            bounds.min.divideBy(tileSize)._floor(),
            boundsMax
        );

        this._addTilesFromCenterOut(tileBounds);
        if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
            this._removeOtherTiles(tileBounds);
        }
    },

    /**
     * get a tile url of the map
     *
     * @method getTileUrl
     * @param {Object} coords, tile coordinate
     * @return {String} url of a tile
     */
    getTileUrl: function(coords) {
        return this._url.replace('{subdomain}', this._getSubdomain(coords))
            .replace('{x}', coords.x)
            .replace('{y}', coords.y)
            .replace('{z}', this._getZoomForUrl());
    }
});

L.map = function (id, options) {
    var map = new L.Map(id, options);

    /**
     * load new tiles when set zoom for baidu map
     * Works well: mouse scroll. zoom level <= 14 in double click
     * Works not well: zoom level > 14. Not Accurate at all.
     * TODO: figure out why not accurate. Potential: CRS differences.
     *
     * @method _setZoomAroundBaidu
     * @param {Object} latlng position of mouse clicked on the canvas
     * @param {Number} zoom zoom level
     * @param {Object} options options of the map
     * @return {Object} TODO: not sure for now. probably the map itself
     */
    var setZoomAroundBaidu = function (latlng, zoom, options) {
        var scale = this.getZoomScale(zoom);
        var viewHalf = this.getSize().divideBy(2);
        var containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng);
        var centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale);
        var newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
        var oldCenterLat = this.getCenter().lat;
        //add offset rather than minus it
        newCenter.lat = oldCenterLat - newCenter.lat + oldCenterLat;
        return this.setView(newCenter, zoom, {zoom: options});
    };

    /**
     * Override _getTopLeftPoint method. For Baidu Map, if dragging
     * down side of the map, y will increase rather than decrease.
     * vice versa.
     *
     * @method _getTopLeftPoint
     * @return {Object} point top left point
     */
    var _getTopLeftPointBaidu = function () {
        var pixel = this.getPixelOrigin();
        var pane = this._getMapPanePos();
        var point = new L.Point(pixel.x - pane.x, pixel.y + pane.y);
        return point;
    };

    //if option has baidu, use custom method
    if (options.baidu === true) {
        map._getTopLeftPoint = _getTopLeftPointBaidu;
        map.setZoomAround = setZoomAroundBaidu;
    }
    return map;
};

L.baiduLayer = function (key, options) {
    return new L.Baidu(key, options);
};
<|endoftext|>


<commit_before>/**
 * pubfood
 * Copyright (c) 2015 Yieldbot, Inc. - All rights reserved.
 */

'use strict';

var util = require('../util');
var Slot = require('../model/slot');
var BidMediator = require('./bidmediator');
var BidAssembler = require('../assembler/bidassembler');
var RequestAssembler = require('../assembler/requestassembler');
var TransformOperator = require('../assembler/transformoperator');
var AuctionProvider = require('../provider/auctionprovider');
var BidProvider = require('../provider/bidprovider');
var Event = require('../event');

/**
 * @typedef {AuctionMediator} AuctionMediator [AuctionMediator]{@link pubfood#mediator.AuctionMediator}
 */

/**
 * AuctionMediator coordiates requests to Publisher Ad Servers.
 *
 * @class
 * @memberof pubfood#mediator
 */
function AuctionMediator(optionalId) {
  if (optionalId) {
    this.id = optionalId;
  }

  this.bidCount = 0;
  this.slots = [];
  this.bidProviders = {};
  this.auctionProvider = null;
  this.auctionState_ = { slots: {}, providers: {}, bids: []};
  this.bids_ = [];
  this.lateBids_ = [];
  this.inAuction = false;
  this.timeout_ = -1;
  this.trigger_ = null;
  this.bidMediator = new BidMediator(this);
  this.bidAssembler = new BidAssembler(this);
  this.requestAssembler = new RequestAssembler();
}

/**
 * Initialize the auction
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.init = function() {
  Event.on(Event.EVENT_TYPE.BID_COMPLETE, util.bind(this.checkBids_, this));
  Event.on(Event.EVENT_TYPE.BID_PUSH_NEXT, util.bind(this.pushBid_, this));
  Event.on(Event.EVENT_TYPE.AUCTION_COMPLETE, function(data) { console.log(data); });
  Event.on(Event.EVENT_TYPE.AUCTION_TRIGGER, util.bind(this.triggerAuction_, this));
  return this;
};

/**
 * Validate provider and slot dependencies.
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.validate = function(isRefresh) {
  var isValid = true;
  /* eslint-disable no-unused-vars */
  var refresh = isRefresh || false;
  /* eslint-enable no-unused-vars */

  var tst = {
    hasAuctionProvider: function () {
      return !!this.auctionProvider;
    },
    hasBidProviders: function() {
      var ret = false;
      /* eslint-disable no-unused-vars */
      for (var v in this.bidProviders) {
        ret = true;
        break;
      }
      /* eslint-enable no-unused-vars */
      if (!ret) {
        Event.publish(Event.EVENT_TYPE.WARN, {msg: 'Warn: no bid providers'}, 'validation');
      }
      return ret;
    },
    hasSlots: function() {
      return this.slots.length !== 0;
    },
    hasAllSlotsBidder: function() {
      var noBidders = [];
      for (var i = 0; i < this.slots.length; i++) {
        var slot = this.slots[i];
        if (!slot.bidProviders || !slot.bidProviders[0]) {
          noBidders.push(slot.name);
        }
      }
      if (noBidders.length > 0) {
        Event.publish(Event.EVENT_TYPE.WARN, {msg: 'Warn: no bidders - ' + noBidders.join(', ')}, 'validation');
      }
      return noBidders.length === 0;
    }
  };

  tst.hasBidProviders.warn = true;
  for (var k in tst) {
    isValid = tst[k].call(this);
    isValid = tst[k].warn ? true : isValid;
    if (!isValid) {
      Event.publish(Event.EVENT_TYPE.INVALID, {msg: 'Failed: ' + k}, 'validation');
      break;
    }
  }

  return isValid;
};

/**
 * Sets the time in which bid providers must supply bids.
 *
 * @param {number} millis - milliseconds to set the timeout
 */
AuctionMediator.prototype.timeout = function(millis) {
  this.timeout_ = millis;
};

/**
 * Gets the time in which bid providers must supply bids.
 *
 * @return {number} the timeout in milliseconds
 */
AuctionMediator.prototype.getTimeout = function() {
  return this.timeout_;
};

/**
 * Force auction provider to init.
 *
 * @param {object}  event
 * @return {AuctionMediator}
 * @private
 */
AuctionMediator.prototype.setAuctionTrigger = function(triggerFn) {
  this.trigger = triggerFn;
};

AuctionMediator.prototype.startAuction_ = function() {
  Event.publish(Event.EVENT_TYPE.BID_ASSEMBLER, 'AuctionMediator', 'assembler');
  this.bidAssembler.process(this.bids_);
  this.go_();
};

/**
 * Start the bid provider timeout.
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.startTimeout_ = function() {
  if (this.timeout_ !== -1 && this.timeout_ >= 0) {
    setTimeout(util.bind(this.startAuction_, this), this.timeout_);
  }
  return this;
};

/**
 * Force auction provider to init.
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.triggerAuction_ = function() {
  if (!this.trigger) {
    this.startTimeout_();
    return;
  }

  function triggerAuction() {
    this.startAuction_();
  }

  this.trigger(util.bind(triggerAuction, this));

  return this;
};

/**
 * Adds bid on {pubfood.PubfoodEvent.BID_PUSH_NEXT} event.
 *
s   * @param {object} data
 * @return {AuctionMediator}
 * @private
 */
AuctionMediator.prototype.pushBid_ = function(event) {
  if (!this.inAuction) {
    var bid = event.data;
    bid.type = 'slot';
    bid.provider = event.provider;
    this.bids_.push(bid);

    bid.provider_ = this.auctionState_.providers[bid.provider];
    bid.slot = this.auctionState_.slots[event.data.slot];

    this.auctionState_.bids.push(bid);
    this.auctionState_.slots[event.data.slot.name].bids.push(bid);
  } else {
    this.lateBids_.push(event.data);
  }
  return this;
};

/**
 * @todo add docs
 *
 * @private
 * @return {undefined}
 */
AuctionMediator.prototype.checkBids_ = function(/*data*/) {
  this.bidCount++;
  if (this.bidCount === Object.keys(this.bidProviders).length) {
    this.startAuction_();
  }
};

/**
 * Start the auction delegate.
 *
 * @private
 * @return {undefined}
 */
AuctionMediator.prototype.go_ = function() {
  var self = this;

  if (this.inAuction) return;
  this.inAuction = true;

  var name = self.auctionProvider.name;

  Event.publish(Event.EVENT_TYPE.AUCTION_GO, name, 'auction');

  var options = this.auctionProvider.options || {};

  var targeting = this.buildTargeting_();
  this.auctionProvider.init(targeting, options, function(){
    self.auctionDone(name);
  });
};

AuctionMediator.prototype.getBidKey = function(bid) {
  var opts = this.auctionProvider.getOptions();

  return (opts.prefix ? bid.provider + '_' : '') + (opts.bidKey || 'bid');
};

AuctionMediator.prototype.mergeKeys = function(slotTargeting, bidTargeting) {
  slotTargeting = util.mergeToObject(slotTargeting, bidTargeting);
};

/**
 * Builds targeting objects for {AuctionDelegate} requests.
 * @private
 * @return {object[]} targeting objects
 */
AuctionMediator.prototype.buildTargeting_ = function() {
  var auctionTargeting = [];
  var slots = this.auctionState_.slots || [];
  for (var k in slots) {
    var s = slots[k];
    var t = { type: 'slot',
              name: s.name,
              id: s.id,
              elementId: s.elementId || '',
              sizes: s.sizes,
              bids: [],
              targeting: {}
            };
    var bids = slots[k].bids;
    for (var b in bids) {
      var bid = bids[b];
      t.bids.push({
        provider: bid.provider,
        id: bid.id,
        targeting: bid.targeting
      });
      this.mergeKeys(t.targeting, bid.targeting);
    }

    auctionTargeting.push(t);
  }
  return auctionTargeting;
};

/**
 * Notification of acution complete
 *
 * @param {string} data The auction mediator's name
 * @fires pubfood.PubfoodEvent.AUCTION_COMPLETE
 */
AuctionMediator.prototype.auctionDone = function(data) {
  Event.publish(Event.EVENT_TYPE.AUCTION_COMPLETE, data, 'auction');
};

AuctionMediator.initTargetingSlot_ = function(auctionState, slot) {
  auctionState.slots[slot.name] = auctionState.slots[slot.name] || slot;
  auctionState.slots[slot.name].bids = auctionState.slots[slot.name].bids || [];
};

AuctionMediator.initTargetingProvider_ = function(auctionState, slot, providerName) {
  auctionState.providers[providerName] = auctionState.providers[providerName] || {};
  auctionState.providers[providerName].slots = auctionState.providers[providerName].slots || {};
  auctionState.providers[providerName].slots[slot.name] = slot;
  auctionState.slots[slot.name].bidProviders[providerName].provider = auctionState.providers[providerName];

};

AuctionMediator.prototype.addTargetingSlot_ = function(slot) {
  AuctionMediator.initTargetingSlot_(this.auctionState_, slot);
  this.updateTargetingProviders_(slot);
};

AuctionMediator.prototype.updateTargetingProviders_ = function(slot) {
  var providers = slot.bidProviders || {};
  for (var k in providers) {
    var providerName = k;
    AuctionMediator.initTargetingProvider_(this.auctionState_, slot, providerName);
  }
};

/**
 * Add a [Slot]{@link pubfood#model.Slot} to [AuctionMediator]{@link pubfood#mediator.AuctionMediator} config.
 * @param {SlotConfig} slotConfig - configuration for a [Slot]{@link pubfood#model.Slot}
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.addSlot = function(slotConfig) {

  var slot = Slot.fromObject(slotConfig);
  if (slot) {
    this.slots.push(slot);

    this.addTargetingSlot_(slot);
  }
  return this;
};

/**
 * Add a [BidProvider]{@link pubfood#provider.BidProvider} configuration object.
 * @param {BidDelegate} delegateConfig - configuration for a [BidProvider]{@link pubfood#provider.BidProvider}
 * @returns {pubfood#provider.BidProvider}
 */
AuctionMediator.prototype.addBidProvider = function(delegateConfig) {

  var bidProvider = BidProvider.withDelegate(delegateConfig);
  if (bidProvider) {
    this.bidProviders[bidProvider.name] = bidProvider;

    if (!this.auctionState_.providers[bidProvider.name]) {
      this.auctionState_.providers[bidProvider.name] = {};
    }
    this.auctionState_.providers[bidProvider.name].provider = bidProvider;
  }
  return this.bidProvider;
};

/**
 * Add a [AuctionProvider]{@link pubfood#provider.AuctionProvider} configuration object.
 * @param {AuctionDelegate} delegateConfig - configuration for a [AuctionProvider]{@link pubfood#provider.AuctionProvider}
 * @returns {null|pubfood#provider.AuctionProvider}
 */
AuctionMediator.prototype.setAuctionProvider = function(delegateConfig) {
  if (this.auctionProvider) {
    Event.publish(Event.EVENT_TYPE.WARN, 'Warning: auction provider exists: ' + this.auctionProvider.name, 'auctionmediator');
  }
  var auctionProvider = AuctionProvider.withDelegate(delegateConfig);
  if(!auctionProvider){
    Event.publish(Event.EVENT_TYPE.WARN, 'Warning: invalid auction provider: ' + this.auctionProvider.name, 'auctionmediator');
  } else {
    this.auctionProvider = auctionProvider;
    this.auctionProvider.setMediator(this);
  }
  return this.auctionProvider;
};

/**
 * Adds a function to transform provider bid request parameters, before auction provider request
 *
 * @param {TransformDelegate} delegate the transformation delegate function
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.addRequestTransform = function(delegate){
  this.requestAssembler.addOperator(new TransformOperator(delegate));
  return this;
};

/**
 * Adds a function to transform provider bid results.
 *
 * @param {TransformDelegate} delegate the transformation delegate function
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.addBidTransform = function(delegate){
  this.bidAssembler.addOperator(new TransformOperator(delegate));
  return this;
};

/**
 * Load bid provider JavaScript library/tag.
 * @params {*} action
 * @returns {undefined}
 */
AuctionMediator.prototype.loadProviders = function(/*action*/) {
  var uri;

  for (var k in this.bidProviders) {
    if (this.bidProviders[k].libUri) {
      uri = this.bidProviders[k].libUri() || '';
      var sync = this.bidProviders[k].sync();
      util.loadUri(uri, sync);
    }
  }

  if (this.auctionProvider && this.auctionProvider.libUri()) {
    Event.publish(Event.EVENT_TYPE.AUCTION_LIB_LOADED, this.auctionProvider.name, 'auction');

    uri = this.auctionProvider.libUri();
    // @todo get sync going here
    util.loadUri(uri);
  }
};

/**
 * Start auction bidding.
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.start = function() {

  Event.publish(Event.EVENT_TYPE.AUCTION_GO, this.auctionProvider.name, 'auction');

  this.init();
  Event.publish(Event.EVENT_TYPE.AUCTION_TRIGGER);

  this.loadProviders();
  this.bidMediator.initBids(this.auctionState_);
  return this;
};

/**
 * Refresh bids for listed slot names.
 *
 * @param {string[]} slotNames slots to refresh
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.refresh = function(slotNames) {
  Event.publish(Event.EVENT_TYPE.AUCTION_REFRESH, this.auctionProvider.name, 'auction');

  this.bidMediator.refreshBids(slotNames);
  return this;
};

module.exports = AuctionMediator;
<commit_msg>return bidProvider<commit_after>/**
 * pubfood
 * Copyright (c) 2015 Yieldbot, Inc. - All rights reserved.
 */

'use strict';

var util = require('../util');
var Slot = require('../model/slot');
var BidMediator = require('./bidmediator');
var BidAssembler = require('../assembler/bidassembler');
var RequestAssembler = require('../assembler/requestassembler');
var TransformOperator = require('../assembler/transformoperator');
var AuctionProvider = require('../provider/auctionprovider');
var BidProvider = require('../provider/bidprovider');
var Event = require('../event');

/**
 * @typedef {AuctionMediator} AuctionMediator [AuctionMediator]{@link pubfood#mediator.AuctionMediator}
 */

/**
 * AuctionMediator coordiates requests to Publisher Ad Servers.
 *
 * @class
 * @memberof pubfood#mediator
 */
function AuctionMediator(optionalId) {
  if (optionalId) {
    this.id = optionalId;
  }

  this.bidCount = 0;
  this.slots = [];
  this.bidProviders = {};
  this.auctionProvider = null;
  this.auctionState_ = { slots: {}, providers: {}, bids: []};
  this.bids_ = [];
  this.lateBids_ = [];
  this.inAuction = false;
  this.timeout_ = -1;
  this.trigger_ = null;
  this.bidMediator = new BidMediator(this);
  this.bidAssembler = new BidAssembler(this);
  this.requestAssembler = new RequestAssembler();
}

/**
 * Initialize the auction
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.init = function() {
  Event.on(Event.EVENT_TYPE.BID_COMPLETE, util.bind(this.checkBids_, this));
  Event.on(Event.EVENT_TYPE.BID_PUSH_NEXT, util.bind(this.pushBid_, this));
  Event.on(Event.EVENT_TYPE.AUCTION_COMPLETE, function(data) { console.log(data); });
  Event.on(Event.EVENT_TYPE.AUCTION_TRIGGER, util.bind(this.triggerAuction_, this));
  return this;
};

/**
 * Validate provider and slot dependencies.
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.validate = function(isRefresh) {
  var isValid = true;
  /* eslint-disable no-unused-vars */
  var refresh = isRefresh || false;
  /* eslint-enable no-unused-vars */

  var tst = {
    hasAuctionProvider: function () {
      return !!this.auctionProvider;
    },
    hasBidProviders: function() {
      var ret = false;
      /* eslint-disable no-unused-vars */
      for (var v in this.bidProviders) {
        ret = true;
        break;
      }
      /* eslint-enable no-unused-vars */
      if (!ret) {
        Event.publish(Event.EVENT_TYPE.WARN, {msg: 'Warn: no bid providers'}, 'validation');
      }
      return ret;
    },
    hasSlots: function() {
      return this.slots.length !== 0;
    },
    hasAllSlotsBidder: function() {
      var noBidders = [];
      for (var i = 0; i < this.slots.length; i++) {
        var slot = this.slots[i];
        if (!slot.bidProviders || !slot.bidProviders[0]) {
          noBidders.push(slot.name);
        }
      }
      if (noBidders.length > 0) {
        Event.publish(Event.EVENT_TYPE.WARN, {msg: 'Warn: no bidders - ' + noBidders.join(', ')}, 'validation');
      }
      return noBidders.length === 0;
    }
  };

  tst.hasBidProviders.warn = true;
  for (var k in tst) {
    isValid = tst[k].call(this);
    isValid = tst[k].warn ? true : isValid;
    if (!isValid) {
      Event.publish(Event.EVENT_TYPE.INVALID, {msg: 'Failed: ' + k}, 'validation');
      break;
    }
  }

  return isValid;
};

/**
 * Sets the time in which bid providers must supply bids.
 *
 * @param {number} millis - milliseconds to set the timeout
 */
AuctionMediator.prototype.timeout = function(millis) {
  this.timeout_ = millis;
};

/**
 * Gets the time in which bid providers must supply bids.
 *
 * @return {number} the timeout in milliseconds
 */
AuctionMediator.prototype.getTimeout = function() {
  return this.timeout_;
};

/**
 * Force auction provider to init.
 *
 * @param {object}  event
 * @return {AuctionMediator}
 * @private
 */
AuctionMediator.prototype.setAuctionTrigger = function(triggerFn) {
  this.trigger = triggerFn;
};

AuctionMediator.prototype.startAuction_ = function() {
  Event.publish(Event.EVENT_TYPE.BID_ASSEMBLER, 'AuctionMediator', 'assembler');
  this.bidAssembler.process(this.bids_);
  this.go_();
};

/**
 * Start the bid provider timeout.
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.startTimeout_ = function() {
  if (this.timeout_ !== -1 && this.timeout_ >= 0) {
    setTimeout(util.bind(this.startAuction_, this), this.timeout_);
  }
  return this;
};

/**
 * Force auction provider to init.
 *
 * @return {AuctionMediator}
 */
AuctionMediator.prototype.triggerAuction_ = function() {
  if (!this.trigger) {
    this.startTimeout_();
    return;
  }

  function triggerAuction() {
    this.startAuction_();
  }

  this.trigger(util.bind(triggerAuction, this));

  return this;
};

/**
 * Adds bid on {pubfood.PubfoodEvent.BID_PUSH_NEXT} event.
 *
s   * @param {object} data
 * @return {AuctionMediator}
 * @private
 */
AuctionMediator.prototype.pushBid_ = function(event) {
  if (!this.inAuction) {
    var bid = event.data;
    bid.type = 'slot';
    bid.provider = event.provider;
    this.bids_.push(bid);

    bid.provider_ = this.auctionState_.providers[bid.provider];
    bid.slot = this.auctionState_.slots[event.data.slot];

    this.auctionState_.bids.push(bid);
    this.auctionState_.slots[event.data.slot.name].bids.push(bid);
  } else {
    this.lateBids_.push(event.data);
  }
  return this;
};

/**
 * @todo add docs
 *
 * @private
 * @return {undefined}
 */
AuctionMediator.prototype.checkBids_ = function(/*data*/) {
  this.bidCount++;
  if (this.bidCount === Object.keys(this.bidProviders).length) {
    this.startAuction_();
  }
};

/**
 * Start the auction delegate.
 *
 * @private
 * @return {undefined}
 */
AuctionMediator.prototype.go_ = function() {
  var self = this;

  if (this.inAuction) return;
  this.inAuction = true;

  var name = self.auctionProvider.name;

  Event.publish(Event.EVENT_TYPE.AUCTION_GO, name, 'auction');

  var options = this.auctionProvider.options || {};

  var targeting = this.buildTargeting_();
  this.auctionProvider.init(targeting, options, function(){
    self.auctionDone(name);
  });
};

AuctionMediator.prototype.getBidKey = function(bid) {
  var opts = this.auctionProvider.getOptions();

  return (opts.prefix ? bid.provider + '_' : '') + (opts.bidKey || 'bid');
};

AuctionMediator.prototype.mergeKeys = function(slotTargeting, bidTargeting) {
  slotTargeting = util.mergeToObject(slotTargeting, bidTargeting);
};

/**
 * Builds targeting objects for {AuctionDelegate} requests.
 * @private
 * @return {object[]} targeting objects
 */
AuctionMediator.prototype.buildTargeting_ = function() {
  var auctionTargeting = [];
  var slots = this.auctionState_.slots || [];
  for (var k in slots) {
    var s = slots[k];
    var t = { type: 'slot',
              name: s.name,
              id: s.id,
              elementId: s.elementId || '',
              sizes: s.sizes,
              bids: [],
              targeting: {}
            };
    var bids = slots[k].bids;
    for (var b in bids) {
      var bid = bids[b];
      t.bids.push({
        provider: bid.provider,
        id: bid.id,
        targeting: bid.targeting
      });
      this.mergeKeys(t.targeting, bid.targeting);
    }

    auctionTargeting.push(t);
  }
  return auctionTargeting;
};

/**
 * Notification of acution complete
 *
 * @param {string} data The auction mediator's name
 * @fires pubfood.PubfoodEvent.AUCTION_COMPLETE
 */
AuctionMediator.prototype.auctionDone = function(data) {
  Event.publish(Event.EVENT_TYPE.AUCTION_COMPLETE, data, 'auction');
};

AuctionMediator.initTargetingSlot_ = function(auctionState, slot) {
  auctionState.slots[slot.name] = auctionState.slots[slot.name] || slot;
  auctionState.slots[slot.name].bids = auctionState.slots[slot.name].bids || [];
};

AuctionMediator.initTargetingProvider_ = function(auctionState, slot, providerName) {
  auctionState.providers[providerName] = auctionState.providers[providerName] || {};
  auctionState.providers[providerName].slots = auctionState.providers[providerName].slots || {};
  auctionState.providers[providerName].slots[slot.name] = slot;
  auctionState.slots[slot.name].bidProviders[providerName].provider = auctionState.providers[providerName];

};

AuctionMediator.prototype.addTargetingSlot_ = function(slot) {
  AuctionMediator.initTargetingSlot_(this.auctionState_, slot);
  this.updateTargetingProviders_(slot);
};

AuctionMediator.prototype.updateTargetingProviders_ = function(slot) {
  var providers = slot.bidProviders || {};
  for (var k in providers) {
    var providerName = k;
    AuctionMediator.initTargetingProvider_(this.auctionState_, slot, providerName);
  }
};

/**
 * Add a [Slot]{@link pubfood#model.Slot} to [AuctionMediator]{@link pubfood#mediator.AuctionMediator} config.
 * @param {SlotConfig} slotConfig - configuration for a [Slot]{@link pubfood#model.Slot}
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.addSlot = function(slotConfig) {

  var slot = Slot.fromObject(slotConfig);
  if (slot) {
    this.slots.push(slot);

    this.addTargetingSlot_(slot);
  }
  return this;
};

/**
 * Add a [BidProvider]{@link pubfood#provider.BidProvider} configuration object.
 * @param {BidDelegate} delegateConfig - configuration for a [BidProvider]{@link pubfood#provider.BidProvider}
 * @returns {pubfood#provider.BidProvider}
 */
AuctionMediator.prototype.addBidProvider = function(delegateConfig) {

  var bidProvider = BidProvider.withDelegate(delegateConfig);
  if (bidProvider) {
    this.bidProviders[bidProvider.name] = bidProvider;

    if (!this.auctionState_.providers[bidProvider.name]) {
      this.auctionState_.providers[bidProvider.name] = {};
    }
    this.auctionState_.providers[bidProvider.name].provider = bidProvider;
  }
  return bidProvider;
};

/**
 * Add a [AuctionProvider]{@link pubfood#provider.AuctionProvider} configuration object.
 * @param {AuctionDelegate} delegateConfig - configuration for a [AuctionProvider]{@link pubfood#provider.AuctionProvider}
 * @returns {null|pubfood#provider.AuctionProvider}
 */
AuctionMediator.prototype.setAuctionProvider = function(delegateConfig) {
  if (this.auctionProvider) {
    Event.publish(Event.EVENT_TYPE.WARN, 'Warning: auction provider exists: ' + this.auctionProvider.name, 'auctionmediator');
  }
  var auctionProvider = AuctionProvider.withDelegate(delegateConfig);
  if(!auctionProvider){
    Event.publish(Event.EVENT_TYPE.WARN, 'Warning: invalid auction provider: ' + this.auctionProvider.name, 'auctionmediator');
  } else {
    this.auctionProvider = auctionProvider;
    this.auctionProvider.setMediator(this);
  }
  return this.auctionProvider;
};

/**
 * Adds a function to transform provider bid request parameters, before auction provider request
 *
 * @param {TransformDelegate} delegate the transformation delegate function
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.addRequestTransform = function(delegate){
  this.requestAssembler.addOperator(new TransformOperator(delegate));
  return this;
};

/**
 * Adds a function to transform provider bid results.
 *
 * @param {TransformDelegate} delegate the transformation delegate function
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.addBidTransform = function(delegate){
  this.bidAssembler.addOperator(new TransformOperator(delegate));
  return this;
};

/**
 * Load bid provider JavaScript library/tag.
 * @params {*} action
 * @returns {undefined}
 */
AuctionMediator.prototype.loadProviders = function(/*action*/) {
  var uri;

  for (var k in this.bidProviders) {
    if (this.bidProviders[k].libUri) {
      uri = this.bidProviders[k].libUri() || '';
      var sync = this.bidProviders[k].sync();
      util.loadUri(uri, sync);
    }
  }

  if (this.auctionProvider && this.auctionProvider.libUri()) {
    Event.publish(Event.EVENT_TYPE.AUCTION_LIB_LOADED, this.auctionProvider.name, 'auction');

    uri = this.auctionProvider.libUri();
    // @todo get sync going here
    util.loadUri(uri);
  }
};

/**
 * Start auction bidding.
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.start = function() {

  Event.publish(Event.EVENT_TYPE.AUCTION_GO, this.auctionProvider.name, 'auction');

  this.init();
  Event.publish(Event.EVENT_TYPE.AUCTION_TRIGGER);

  this.loadProviders();
  this.bidMediator.initBids(this.auctionState_);
  return this;
};

/**
 * Refresh bids for listed slot names.
 *
 * @param {string[]} slotNames slots to refresh
 * @returns {pubfood#mediator.AuctionMediator}
 */
AuctionMediator.prototype.refresh = function(slotNames) {
  Event.publish(Event.EVENT_TYPE.AUCTION_REFRESH, this.auctionProvider.name, 'auction');

  this.bidMediator.refreshBids(slotNames);
  return this;
};

module.exports = AuctionMediator;
<|endoftext|>


<commit_before>(function($) {
    $(document).ready(function() {

        var $form = $('#wiki-page-edit');
        var $idTagsField = $('#tagit_tags');

        // Create a hidden input type for the purposes of saving
        var hiddenTags = $('<input type="hidden" name="tags" id="hiddenTags" value="' + ($idTagsField.val() || '') +    '" />').appendTo('#page-tags');

        // Grabs text from the list items, updates hidden input so tags are properly saved
        // Requires node reading because the tag-it widget incorrectly overrides the "singleNodeField"
        function consolidateTags(isRemove) {
            return function(event, li) {
                var listItems = $('#page-tags .tagit-choice');
                var itemTexts = [];

                // Don't add list items we're going to remove
                if(!isRemove) listItems.push(li);

                // Cycling through each list item,
                listItems.each(function(i, e, a) {
                    if(!(isRemove && this == li[0])) {
                        itemTexts.push('"' + $(this).find('.tagit-label').text() + '"');
                    }
                });
                hiddenTags.val(itemTexts.join(','));

                // Check whether there are net changes in tags
                if(window.waffle && window.waffle.flag_is_active('dirtiness_tracking')) {
                    if (undefined !== originalTags && hiddenTags.val() !== originalTags) {
                        $('#page-tags').addClass('dirty').trigger('mdn:dirty');
                    } else {
                        $('#page-tags').removeClass('dirty').trigger('mdn:clean');
                    }
                }
            };
        };

        // Turn the text input into the widget
        $idTagsField.tagit({
            availableTags: mdn.wiki.tagSuggestions || [],
            singleField: true,
            allowSpaces: true,
            singleFieldNode: $idTagsField,
            onTagAdded    : consolidateTags(),
            onTagRemoved: consolidateTags(true)
        });

        // Set the new tag element text
        $('.tagit-new input').attr('placeholder', gettext('New tag...'));

        // Remove the hidden field since it wont be submitted anyways
        $idTagsField.remove();

        // Keep track of tag dirtiness
        if(window.waffle && window.waffle.flag_is_active('dirtiness_tracking')) {
            var originalTags = hiddenTags.val();
            $form.on('mdn:save-success', function() {
                originalTags = hiddenTags.val();
            });
        }
    })
})(jQuery);
<commit_msg>bug 1055078 - Make wiki-tags-edit.js pass jshint<commit_after>(function($) {
    "use strict";

    var $form = $('#wiki-page-edit');
    var $idTagsField = $('#tagit_tags');

    // Create a hidden input type for the purposes of saving
    var hiddenTags = $('<input type="hidden" name="tags" id="hiddenTags" value="' + ($idTagsField.val() || '') +    '" />').appendTo('#page-tags');

    // Grabs text from the list items, updates hidden input so tags are properly saved
    // Requires node reading because the tag-it widget incorrectly overrides the "singleNodeField"
    function consolidateTags(isRemove) {
        return function(e, li) {
            var listItems = $('#page-tags .tagit-choice');
            var itemTexts = [];

            // Don't add list items we're going to remove
            if(!isRemove) listItems.push(li);

            // Cycling through each list item,
            listItems.each(function(i, e, a) {
                if(!(isRemove && this === li[0])) {
                    itemTexts.push('"' + $(this).find('.tagit-label').text() + '"');
                }
            });
            hiddenTags.val(itemTexts.join(','));

            // Check whether there are net changes in tags
            if(window.waffle && window.waffle.flag_is_active('dirtiness_tracking')) {
                if (undefined !== originalTags && hiddenTags.val() !== originalTags) {
                    $('#page-tags').addClass('dirty').trigger('mdn:dirty');
                } else {
                    $('#page-tags').removeClass('dirty').trigger('mdn:clean');
                }
            }
        };
    }

    // Turn the text input into the widget
    $idTagsField.tagit({
        availableTags: mdn.wiki.tagSuggestions || [],
        singleField: true,
        allowSpaces: true,
        singleFieldNode: $idTagsField,
        onTagAdded    : consolidateTags(),
        onTagRemoved: consolidateTags(true)
    });

    // Set the new tag element text
    $('.tagit-new input').attr('placeholder', gettext('New tag...'));

    // Remove the hidden field since it wont be submitted anyways
    $idTagsField.remove();

    // Keep track of tag dirtiness
    if(window.waffle && window.waffle.flag_is_active('dirtiness_tracking')) {
        var originalTags = hiddenTags.val();
        $form.on('mdn:save-success', function() {
            originalTags = hiddenTags.val();
        });
    }

})(jQuery);
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qbs.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

var FileInfo = require("qbs.FileInfo");
var ModUtils = require("qbs.ModUtils"); // TODO: append/prepend functionality should go to qbs.Environment

function addNewElement(list, elem)
{
    if (!list.contains(elem))
        list.push(elem);
}

function artifactDir(artifact)
{
    if (!artifact.qbs.install)
        return FileInfo.path(artifact.filePath);
    return FileInfo.path(ModUtils.artifactInstalledFilePath(artifact));
}

function gatherPaths(product, libPaths, frameworkPaths)
{
    // Heuristic: If any rpaths are set, assume environment paths should not be set up.
    // Let's not try to be super fancy, evaluating all the rpaths, expanding $ORIGIN, relative paths
    // and whatnot.
    if (product.cpp.useRPaths && product.cpp.rpaths && product.cpp.rpaths.length > 0)
        return;

    // Gather explicitly given library paths.
    if (product.cpp.libraryPaths)
        product.cpp.libraryPaths.forEach(function(p) { addNewElement(libPaths, p); });
    if (product.cpp.frameworkPaths)
        product.cpp.frameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); });

    // Extract paths from dynamic libraries, if they are given as file paths.
    if (product.cpp.dynamicLibraries) {
        product.cpp.dynamicLibraries.forEach(function(dll) {
            if (FileInfo.isAbsolutePath(dll))
                addNewElement(libPaths, FileInfo.path(dll));
        });
    }

    // Traverse library dependencies.
    for (var i = 0; i < product.dependencies.length; ++i) {
        var dep = product.dependencies[i];
        var dllSymlinkArtifacts = dep.artifacts["bundle.symlink.executable"];
        if (dllSymlinkArtifacts) {
            var addArtifact = function(artifact) {
                addNewElement(frameworkPaths, FileInfo.path(artifactDir(artifact)));
            };
            dllSymlinkArtifacts.forEach(addArtifact); // TODO: Will also catch applications. Can we prevent that?
        } else {
            addArtifact = function(artifact) {
                addNewElement(libPaths, artifactDir(artifact));
            };
            var dllArtifacts = dep.artifacts["dynamiclibrary"];
            if (dllArtifacts)
                dllArtifacts.forEach(addArtifact);
            var loadableModuleArtifacts = dep.artifacts["loadablemodule"];
            if (loadableModuleArtifacts)
                loadableModuleArtifacts.forEach(addArtifact);
        }
        if (!dep.hasOwnProperty("present")) // Recurse if the dependency is a product. TODO: Provide non-heuristic way to decide whether dependency is a product.
            gatherPaths(dep, libPaths, frameworkPaths);
    }
}

function setupRunEnvironment(product, config)
{
    if (config.contains("ignore-lib-dependencies"))
        return;

    // TODO: Uncomment once the property is available
//    if (product.qbs.hostPlatform != product.qbs.targetPlatform)
//        return;

    var libPaths = [];
    var frameworkPaths = [];
    gatherPaths(product, libPaths, frameworkPaths);

    var runPaths = product.cpp.systemRunPaths;
    if (runPaths && runPaths.length > 0) {
        var filterFunc = function(p) { return !runPaths.contains(p); };
        libPaths = libPaths.filter(filterFunc);
        frameworkPaths = frameworkPaths.filter(filterFunc);
    }

    if (libPaths.length > 0) {
        var envVarName;
        if (product.qbs.targetOS.contains("windows"))
            envVarName = "PATH";
        else if (product.qbs.targetOS.contains("macos"))
            envVarName = "DYLD_LIBRARY_PATH";
        else
            envVarName = "LD_LIBRARY_PATH";
        var envVar = new ModUtils.EnvironmentVariable(envVarName, product.qbs.pathListSeparator,
                                                      product.qbs.hostOS.contains("windows"));
        libPaths.forEach(function(p) { envVar.prepend(p); });
        envVar.set();
    }

    if (product.qbs.targetOS.contains("macos") && frameworkPaths.length > 0) {
        envVar = new ModUtils.EnvironmentVariable("DYLD_FRAMEWORK_PATH", ':', false);
        frameworkPaths.forEach(function(p) { envVar.prepend(p); });
        envVar.set();
    }
}
<commit_msg>Uncomment a platform check now that the respective properties exist<commit_after>/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qbs.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

var FileInfo = require("qbs.FileInfo");
var ModUtils = require("qbs.ModUtils"); // TODO: append/prepend functionality should go to qbs.Environment

function addNewElement(list, elem)
{
    if (!list.contains(elem))
        list.push(elem);
}

function artifactDir(artifact)
{
    if (!artifact.qbs.install)
        return FileInfo.path(artifact.filePath);
    return FileInfo.path(ModUtils.artifactInstalledFilePath(artifact));
}

function gatherPaths(product, libPaths, frameworkPaths)
{
    // Heuristic: If any rpaths are set, assume environment paths should not be set up.
    // Let's not try to be super fancy, evaluating all the rpaths, expanding $ORIGIN, relative paths
    // and whatnot.
    if (product.cpp.useRPaths && product.cpp.rpaths && product.cpp.rpaths.length > 0)
        return;

    // Gather explicitly given library paths.
    if (product.cpp.libraryPaths)
        product.cpp.libraryPaths.forEach(function(p) { addNewElement(libPaths, p); });
    if (product.cpp.frameworkPaths)
        product.cpp.frameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); });

    // Extract paths from dynamic libraries, if they are given as file paths.
    if (product.cpp.dynamicLibraries) {
        product.cpp.dynamicLibraries.forEach(function(dll) {
            if (FileInfo.isAbsolutePath(dll))
                addNewElement(libPaths, FileInfo.path(dll));
        });
    }

    // Traverse library dependencies.
    for (var i = 0; i < product.dependencies.length; ++i) {
        var dep = product.dependencies[i];
        var dllSymlinkArtifacts = dep.artifacts["bundle.symlink.executable"];
        if (dllSymlinkArtifacts) {
            var addArtifact = function(artifact) {
                addNewElement(frameworkPaths, FileInfo.path(artifactDir(artifact)));
            };
            dllSymlinkArtifacts.forEach(addArtifact); // TODO: Will also catch applications. Can we prevent that?
        } else {
            addArtifact = function(artifact) {
                addNewElement(libPaths, artifactDir(artifact));
            };
            var dllArtifacts = dep.artifacts["dynamiclibrary"];
            if (dllArtifacts)
                dllArtifacts.forEach(addArtifact);
            var loadableModuleArtifacts = dep.artifacts["loadablemodule"];
            if (loadableModuleArtifacts)
                loadableModuleArtifacts.forEach(addArtifact);
        }
        if (!dep.hasOwnProperty("present")) // Recurse if the dependency is a product. TODO: Provide non-heuristic way to decide whether dependency is a product.
            gatherPaths(dep, libPaths, frameworkPaths);
    }
}

function setupRunEnvironment(product, config)
{
    if (config.contains("ignore-lib-dependencies"))
        return;

    if (product.qbs.hostPlatform !== product.qbs.targetPlatform)
        return;

    var libPaths = [];
    var frameworkPaths = [];
    gatherPaths(product, libPaths, frameworkPaths);

    var runPaths = product.cpp.systemRunPaths;
    if (runPaths && runPaths.length > 0) {
        var filterFunc = function(p) { return !runPaths.contains(p); };
        libPaths = libPaths.filter(filterFunc);
        frameworkPaths = frameworkPaths.filter(filterFunc);
    }

    if (libPaths.length > 0) {
        var envVarName;
        if (product.qbs.targetOS.contains("windows"))
            envVarName = "PATH";
        else if (product.qbs.targetOS.contains("macos"))
            envVarName = "DYLD_LIBRARY_PATH";
        else
            envVarName = "LD_LIBRARY_PATH";
        var envVar = new ModUtils.EnvironmentVariable(envVarName, product.qbs.pathListSeparator,
                                                      product.qbs.hostOS.contains("windows"));
        libPaths.forEach(function(p) { envVar.prepend(p); });
        envVar.set();
    }

    if (product.qbs.targetOS.contains("macos") && frameworkPaths.length > 0) {
        envVar = new ModUtils.EnvironmentVariable("DYLD_FRAMEWORK_PATH", ':', false);
        frameworkPaths.forEach(function(p) { envVar.prepend(p); });
        envVar.set();
    }
}
<|endoftext|>


<commit_before>const bluebird = require('bluebird');
const request = bluebird.promisifyAll(require('request'), { multiArgs: true });
const cheerio = require('cheerio');
const graph = require('fbgraph');
const LastFmNode = require('lastfm').LastFmNode;
const tumblr = require('tumblr.js');
const GitHub = require('github');
const Twit = require('twit');
const stripe = require('stripe')(process.env.STRIPE_SKEY);
const twilio = require('twilio')(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);
const Linkedin = require('node-linkedin')(process.env.LINKEDIN_ID, process.env.LINKEDIN_SECRET, process.env.LINKEDIN_CALLBACK_URL);
const clockwork = require('clockwork')({ key: process.env.CLOCKWORK_KEY });
const paypal = require('paypal-rest-sdk');
const lob = require('lob')(process.env.LOB_KEY);
const ig = bluebird.promisifyAll(require('instagram-node').instagram());
const foursquare = require('node-foursquare')({
  secrets: {
    clientId: process.env.FOURSQUARE_ID,
    clientSecret: process.env.FOURSQUARE_SECRET,
    redirectUrl: process.env.FOURSQUARE_REDIRECT_URL
  }
});

foursquare.Venues = bluebird.promisifyAll(foursquare.Venues);
foursquare.Users = bluebird.promisifyAll(foursquare.Users);

/**
 * GET /api
 * List of API examples.
 */
exports.getApi = (req, res) => {
  res.render('api/index', {
    title: 'API Examples'
  });
};

/**
 * GET /api/foursquare
 * Foursquare API example.
 */
exports.getFoursquare = (req, res, next) => {
  const token = req.user.tokens.find(token => token.kind === 'foursquare');
  Promise.all([
    foursquare.Venues.getTrendingAsync('40.7222756', '-74.0022724', { limit: 50 }, token.accessToken),
    foursquare.Venues.getVenueAsync('49da74aef964a5208b5e1fe3', token.accessToken),
    foursquare.Users.getCheckinsAsync('self', null, token.accessToken)
  ])
  .then(([trendingVenues, venueDetail, userCheckins]) => {
    res.render('api/foursquare', {
      title: 'Foursquare API',
      trendingVenues,
      venueDetail,
      userCheckins
    });
  })
  .catch(next);
};

/**
 * GET /api/tumblr
 * Tumblr API example.
 */
exports.getTumblr = (req, res, next) => {
  const token = req.user.tokens.find(token => token.kind === 'tumblr');
  const client = tumblr.createClient({
    consumer_key: process.env.TUMBLR_KEY,
    consumer_secret: process.env.TUMBLR_SECRET,
    token: token.accessToken,
    token_secret: token.tokenSecret
  });
  client.posts('mmosdotcom.tumblr.com', { type: 'photo' }, (err, data) => {
    if (err) { return next(err); }
    res.render('api/tumblr', {
      title: 'Tumblr API',
      blog: data.blog,
      photoset: data.posts[0].photos
    });
  });
};

/**
 * GET /api/facebook
 * Facebook API example.
 */
exports.getFacebook = (req, res, next) => {
  const token = req.user.tokens.find(token => token.kind === 'facebook');
  graph.setAccessToken(token.accessToken);
  graph.get(`${req.user.facebook}?fields=id,name,email,first_name,last_name,gender,link,locale,timezone`, (err, results) => {
    if (err) { return next(err); }
    res.render('api/facebook', {
      title: 'Facebook API',
      profile: results
    });
  });
};

/**
 * GET /api/scraping
 * Web scraping example using Cheerio library.
 */
exports.getScraping = (req, res, next) => {
  request.get('https://news.ycombinator.com/', (err, request, body) => {
    if (err) { return next(err); }
    const $ = cheerio.load(body);
    const links = [];
    $('.title a[href^="http"], a[href^="https"]').each((index, element) => {
      links.push($(element));
    });
    res.render('api/scraping', {
      title: 'Web Scraping',
      links
    });
  });
};

/**
 * GET /api/github
 * GitHub API Example.
 */
exports.getGithub = (req, res, next) => {
  const github = new GitHub();
  github.repos.get({ owner: 'sahat', repo: 'hackathon-starter' }, (err, repo) => {
    if (err) { return next(err); }
    res.render('api/github', {
      title: 'GitHub API',
      repo
    });
  });
};

/**
 * GET /api/aviary
 * Aviary image processing example.
 */
exports.getAviary = (req, res) => {
  res.render('api/aviary', {
    title: 'Aviary API'
  });
};

/**
 * GET /api/nyt
 * New York Times API example.
 */
exports.getNewYorkTimes = (req, res, next) => {
  const query = {
    'list-name': 'young-adult',
    'api-key': process.env.NYT_KEY
  };
  request.get({ url: 'http://api.nytimes.com/svc/books/v2/lists', qs: query }, (err, request, body) => {
    if (err) { return next(err); }
    if (request.statusCode === 403) {
      return next(new Error('Invalid New York Times API Key'));
    }
    const books = JSON.parse(body).results;
    res.render('api/nyt', {
      title: 'New York Times API',
      books
    });
  });
};

/**
 * GET /api/lastfm
 * Last.fm API example.
 */
exports.getLastfm = (req, res, next) => {
  const lastfm = new LastFmNode({
    api_key: process.env.LASTFM_KEY,
    secret: process.env.LASTFM_SECRET
  });
  const artistInfo = () =>
    new Promise((resolve, reject) => {
      lastfm.request('artist.getInfo', {
        artist: 'Roniit',
        handlers: {
          success: resolve,
          error: reject
        }
      });
    });
  const artistTopTracks = () =>
    new Promise((resolve, reject) => {
      lastfm.request('artist.getTopTracks', {
        artist: 'Roniit',
        handlers: {
          success: (data) => {
            resolve(data.toptracks.track.slice(0, 10));
          },
          error: reject
        }
      });
    });
  const artistTopAlbums = () =>
      new Promise((resolve, reject) => {
        lastfm.request('artist.getTopAlbums', {
          artist: 'Roniit',
          handlers: {
            success: (data) => {
              resolve(data.topalbums.album.slice(0, 3));
            },
            error: reject
          }
        });
      });
  Promise.all([
    artistInfo(),
    artistTopTracks(),
    artistTopAlbums()
  ])
  .then(([artistInfo, artistTopAlbums, artistTopTracks]) => {
    const artist = {
      name: artistInfo.artist.name,
      image: artistInfo.artist.image.slice(-1)[0]['#text'],
      tags: artistInfo.artist.tags.tag,
      bio: artistInfo.artist.bio.summary,
      stats: artistInfo.artist.stats,
      similar: artistInfo.artist.similar.artist,
      topAlbums: artistTopAlbums,
      topTracks: artistTopTracks
    };
    res.render('api/lastfm', {
      title: 'Last.fm API',
      artist
    });
  })
  .catch(next);
};

/**
 * GET /api/twitter
 * Twitter API example.
 */
exports.getTwitter = (req, res, next) => {
  const token = req.user.tokens.find(token => token.kind === 'twitter');
  const T = new Twit({
    consumer_key: process.env.TWITTER_KEY,
    consumer_secret: process.env.TWITTER_SECRET,
    access_token: token.accessToken,
    access_token_secret: token.tokenSecret
  });
  T.get('search/tweets', { q: 'nodejs since:2013-01-01', geocode: '40.71448,-74.00598,5mi', count: 10 }, (err, reply) => {
    if (err) { return next(err); }
    res.render('api/twitter', {
      title: 'Twitter API',
      tweets: reply.statuses
    });
  });
};

/**
 * POST /api/twitter
 * Post a tweet.
 */
exports.postTwitter = (req, res, next) => {
  req.assert('tweet', 'Tweet cannot be empty').notEmpty();

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/api/twitter');
  }

  const token = req.user.tokens.find(token => token.kind === 'twitter');
  const T = new Twit({
    consumer_key: process.env.TWITTER_KEY,
    consumer_secret: process.env.TWITTER_SECRET,
    access_token: token.accessToken,
    access_token_secret: token.tokenSecret
  });
  T.post('statuses/update', { status: req.body.tweet }, (err) => {
    if (err) { return next(err); }
    req.flash('success', { msg: 'Your tweet has been posted.' });
    res.redirect('/api/twitter');
  });
};

/**
 * GET /api/steam
 * Steam API example.
 */
exports.getSteam = (req, res, next) => {
  const steamId = '76561197982488301';
  const params = { l: 'english', steamid: steamId, key: process.env.STEAM_KEY };
  const playerAchievements = () => {
    params.appid = '49520';
    return request.getAsync({ url: 'http://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/', qs: params, json: true })
      .then(([request, body]) => {
        if (request.statusCode === 401) {
          throw new Error('Invalid Steam API Key');
        }
        return body;
      });
  };
  const playerSummaries = () => {
    params.steamids = steamId;
    return request.getAsync({ url: 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/', qs: params, json: true })
      .then(([request, body]) => {
        if (request.statusCode === 401) {
          throw Error('Missing or Invalid Steam API Key');
        }
        return body;
      });
  };
  const ownedGames = () => {
    params.include_appinfo = 1;
    params.include_played_free_games = 1;
    return request.getAsync({ url: 'http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/', qs: params, json: true })
      .then(([request, body]) => {
        if (request.statusCode === 401) {
          throw new Error('Missing or Invalid Steam API Key');
        }
        return body;
      });
  };
  Promise.all([
    playerAchievements(),
    playerSummaries(),
    ownedGames()
  ])
  .then(([playerAchievements, playerSummaries, ownedGames]) => {
    res.render('api/steam', {
      title: 'Steam Web API',
      ownedGames: ownedGames.response.games,
      playerAchievemments: playerAchievements.playerstats,
      playerSummary: playerSummaries.response.players[0]
    });
  })
  .catch(next);
};

/**
 * GET /api/stripe
 * Stripe API example.
 */
exports.getStripe = (req, res) => {
  res.render('api/stripe', {
    title: 'Stripe API',
    publishableKey: process.env.STRIPE_PKEY
  });
};

/**
 * POST /api/stripe
 * Make a payment.
 */
exports.postStripe = (req, res) => {
  const stripeToken = req.body.stripeToken;
  const stripeEmail = req.body.stripeEmail;
  stripe.charges.create({
    amount: 395,
    currency: 'usd',
    source: stripeToken,
    description: stripeEmail
  }, (err) => {
    if (err && err.type === 'StripeCardError') {
      req.flash('errors', { msg: 'Your card has been declined.' });
      return res.redirect('/api/stripe');
    }
    req.flash('success', { msg: 'Your card has been successfully charged.' });
    res.redirect('/api/stripe');
  });
};

/**
 * GET /api/twilio
 * Twilio API example.
 */
exports.getTwilio = (req, res) => {
  res.render('api/twilio', {
    title: 'Twilio API'
  });
};

/**
 * POST /api/twilio
 * Send a text message using Twilio.
 */
exports.postTwilio = (req, res, next) => {
  req.assert('number', 'Phone number is required.').notEmpty();
  req.assert('message', 'Message cannot be blank.').notEmpty();

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/api/twilio');
  }

  const message = {
    to: req.body.number,
    from: '+13472235148',
    body: req.body.message
  };
  twilio.sendMessage(message, (err, responseData) => {
    if (err) { return next(err.message); }
    req.flash('success', { msg: `Text sent to ${responseData.to}.` });
    res.redirect('/api/twilio');
  });
};

/**
 * GET /api/clockwork
 * Clockwork SMS API example.
 */
exports.getClockwork = (req, res) => {
  res.render('api/clockwork', {
    title: 'Clockwork SMS API'
  });
};

/**
 * POST /api/clockwork
 * Send a text message using Clockwork SMS
 */
exports.postClockwork = (req, res, next) => {
  const message = {
    To: req.body.telephone,
    From: 'Hackathon',
    Content: 'Hello from the Hackathon Starter'
  };
  clockwork.sendSms(message, (err, responseData) => {
    if (err) { return next(err.errDesc); }
    req.flash('success', { msg: `Text sent to ${responseData.responses[0].to}` });
    res.redirect('/api/clockwork');
  });
};

/**
 * GET /api/linkedin
 * LinkedIn API example.
 */
exports.getLinkedin = (req, res, next) => {
  const token = req.user.tokens.find(token => token.kind === 'linkedin');
  const linkedin = Linkedin.init(token.accessToken);
  linkedin.people.me((err, $in) => {
    if (err) { return next(err); }
    res.render('api/linkedin', {
      title: 'LinkedIn API',
      profile: $in
    });
  });
};

/**
 * GET /api/instagram
 * Instagram API example.
 */
exports.getInstagram = (req, res, next) => {
  const token = req.user.tokens.find(token => token.kind === 'instagram');
  ig.use({ client_id: process.env.INSTAGRAM_ID, client_secret: process.env.INSTAGRAM_SECRET });
  ig.use({ access_token: token.accessToken });
  Promise.all([
    ig.user_searchAsync('richellemead'),
    ig.userAsync('175948269'),
    ig.media_popularAsync(),
    ig.user_self_media_recentAsync()
  ])
  .then(([searchByUsername, searchByUserId, popularImages, myRecentMedia]) => {
    res.render('api/instagram', {
      title: 'Instagram API',
      usernames: searchByUsername,
      userById: searchByUserId,
      popularImages,
      myRecentMedia
    });
  })
  .catch(next);
};

/**
 * GET /api/paypal
 * PayPal SDK example.
 */
exports.getPayPal = (req, res, next) => {
  paypal.configure({
    mode: 'sandbox',
    client_id: process.env.PAYPAL_ID,
    client_secret: process.env.PAYPAL_SECRET
  });

  const paymentDetails = {
    intent: 'sale',
    payer: {
      payment_method: 'paypal'
    },
    redirect_urls: {
      return_url: process.env.PAYPAL_RETURN_URL,
      cancel_url: process.env.PAYPAL_CANCEL_URL
    },
    transactions: [{
      description: 'Hackathon Starter',
      amount: {
        currency: 'USD',
        total: '1.99'
      }
    }]
  };

  paypal.payment.create(paymentDetails, (err, payment) => {
    if (err) { return next(err); }
    req.session.paymentId = payment.id;
    const links = payment.links;
    for (let i = 0; i < links.length; i++) {
      if (links[i].rel === 'approval_url') {
        res.render('api/paypal', {
          approvalUrl: links[i].href
        });
      }
    }
  });
};

/**
 * GET /api/paypal/success
 * PayPal SDK example.
 */
exports.getPayPalSuccess = (req, res) => {
  const paymentId = req.session.paymentId;
  const paymentDetails = { payer_id: req.query.PayerID };
  paypal.payment.execute(paymentId, paymentDetails, (err) => {
    res.render('api/paypal', {
      result: true,
      success: !err
    });
  });
};

/**
 * GET /api/paypal/cancel
 * PayPal SDK example.
 */
exports.getPayPalCancel = (req, res) => {
  req.session.paymentId = null;
  res.render('api/paypal', {
    result: true,
    canceled: true
  });
};

/**
 * GET /api/lob
 * Lob API example.
 */
exports.getLob = (req, res, next) => {
  lob.routes.list({ zip_codes: ['10007'] }, (err, routes) => {
    if (err) { return next(err); }
    res.render('api/lob', {
      title: 'Lob API',
      routes: routes.data[0].routes
    });
  });
};

/**
 * GET /api/upload
 * File Upload API example.
 */

exports.getFileUpload = (req, res) => {
  res.render('api/upload', {
    title: 'File Upload'
  });
};

exports.postFileUpload = (req, res) => {
  req.flash('success', { msg: 'File was uploaded successfully.' });
  res.redirect('/api/upload');
};

/**
 * GET /api/pinterest
 * Pinterest API example.
 */
exports.getPinterest = (req, res, next) => {
  const token = req.user.tokens.find(token => token.kind === 'pinterest');
  request.get({ url: 'https://api.pinterest.com/v1/me/boards/', qs: { access_token: token.accessToken }, json: true }, (err, request, body) => {
    if (err) { return next(err); }
    res.render('api/pinterest', {
      title: 'Pinterest API',
      boards: body.data
    });
  });
};

/**
 * POST /api/pinterest
 * Create a pin.
 */
exports.postPinterest = (req, res, next) => {
  req.assert('board', 'Board is required.').notEmpty();
  req.assert('note', 'Note cannot be blank.').notEmpty();
  req.assert('image_url', 'Image URL cannot be blank.').notEmpty();

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/api/pinterest');
  }

  const token = req.user.tokens.find(token => token.kind === 'pinterest');
  const formData = {
    board: req.body.board,
    note: req.body.note,
    link: req.body.link,
    image_url: req.body.image_url
  };

  request.post('https://api.pinterest.com/v1/pins/', { qs: { access_token: token.accessToken }, form: formData }, (err, request, body) => {
    if (err) { return next(err); }
    if (request.statusCode !== 201) {
      req.flash('errors', { msg: JSON.parse(body).message });
      return res.redirect('/api/pinterest');
    }
    req.flash('success', { msg: 'Pin created' });
    res.redirect('/api/pinterest');
  });
};

exports.getGoogleMaps = (req, res) => {
  res.render('api/google-maps', {
    title: 'Google Maps API'
  });
};
<commit_msg>removed useless apijs<commit_after><|endoftext|>


<commit_before>// 客户管理 - 目录配置
const CLIENT_DIR = {
    title: '客户管理',
    key: 'client',
    path: '/home/client',
    icon: 'user',
    childRoute: [
        {
            title: '客户资料',
            key: 'information',
            path: '/home/client/information',
            ancestor: ['client'],
            component: require('./containers/Information').default
        }, {
            title: '客户报修',
            key: 'repair',
            path: '/home/client/repair',
            ancestor: ['client'],
            childRoute: [
                {
                    title: '客户报修',
                    key: 'clientRepair',
                    path: '/home/client/repair/clientRepair',
                    ancestor: ['client', 'repair'],
                    component: require('./containers/repair/ClientRepair').default
                }, {
                    title: '客户回访',
                    key: 'returnVisit',
                    path: '/home/client/repair/returnVisit',
                    ancestor: ['client', 'repair'],
                    component: require('./containers/repair/ReturnVisit').default
                }, {
                    title: '维修费设置',
                    key: 'maintenanceFees',
                    path: '/home/client/repair/maintenanceFees',
                    ancestor: ['client', 'repair'],
                    component: require('./containers/repair/MaintenanceFees').default
                }
            ]
        }, {
            title: '投诉建议',
            key: 'complain',
            path: '/home/client/complain',
            ancestor: ['client'],
            component: require('./containers/Complain').default
        }, {
            title: '整改通知',
            key: 'notice',
            path: '/home/client/notice',
            ancestor: ['client'],
            component: require('./containers/Notice').default
        }, {
            title: '物业公告',
            key: 'propertyNotice',
            path: '/home/client/propertyNotice',
            ancestor: ['client'],
            component: require('./containers/PropertyNotice').default
        }, {
            title: '二次装修',
            key: 'secondaryDecoration',
            path: '/home/client/secondaryDecoration',
            ancestor: ['client'],
            component: require('./containers/SecondaryDecoration').default
        }, {
            title: '保证金管理',
            key: 'margin',
            path: '/home/client/margin',
            ancestor: ['client'],
            component: require('./containers/Margin').default
        }, {
            title: '门禁卡管理',
            key: 'accessCard',
            path: '/home/client/accessCard',
            ancestor: ['client'],
            childRoute: [
                {
                    title: '门禁卡管理',
                    key: 'management',
                    path: '/home/client/accessCard/management',
                    ancestor: ['client', 'accessCard'],
                    component: require('./containers/accessCard/Management').default
                }, {
                    title: '门禁卡押金',
                    key: 'deposit',
                    path: '/home/client/accessCard/deposit',
                    ancestor: ['client', 'accessCard'],
                    component: require('./containers/accessCard/Deposit').default
                }
            ]
        },
        {
            title: '房间梆定',
            key: 'roombinding',
            path: '/home/client/roombinding',
            ancestor: ['client'],
            component: require('./containers/RoomBinding').default
        },
    ]
}

// 详情页路由表
const CLIENT_DETAILS = [
    {
        title: '报修内容',
        key: 'RepairDetail',
        path: '/home/client/repair/repairDetail/:id',
        component: require('./containers/details/repair/RepairDetail').default
    }, {
        title: '维修明细',
        key: 'MaintenanceDetail',
        path: '/home/client/repair/MaintenanceDetail/:id',
        component: require('./containers/details/repair/MaintenanceDetail').default
    }, {
        title: '回访情况',
        key: 'ReturnVisitDetail',
        path: '/home/client/repair/ReturnVisitDetail/:id',
        component: require('./containers/details/repair/ReturnVisitDetail').default
    }, {
        title: '回访登记',
        key: 'ReturnDetail',
        path: '/home/client/repair/ReturnDetail/:id',
        component: require('./containers/details/repair/ReturnDetail').default
    }, {
        title: '保证金详情',
        key: 'CashDepositDetail',
        path: '/home/client/cashDepositDetail/cashDepositDetail/:id',
        component: require('./containers/details/cashDeposit/CashDepositDetail').default
    }, {
        title: '整改通知',
        key: 'CorrectionDetail',
        path: '/home/client/NoticeDetail/CorrectionDetail/:id',
        component: require('./containers/details/CorrectionDetail').default
    }
]
export { CLIENT_DIR, CLIENT_DETAILS }
<commit_msg>select 可清除<commit_after>// 客户管理 - 目录配置
const CLIENT_DIR = {
    title: '客户管理',
    key: 'client',
    path: '/home/client',
    icon: 'user',
    childRoute: [
        {
            title: '客户资料',
            key: 'information',
            path: '/home/client/information',
            ancestor: ['client'],
            component: require('./containers/Information').default
        }, {
            title: '客户报修',
            key: 'repair',
            path: '/home/client/repair',
            ancestor: ['client'],
            childRoute: [
                {
                    title: '客户报修',
                    key: 'clientRepair',
                    path: '/home/client/repair/clientRepair',
                    ancestor: ['client', 'repair'],
                    component: require('./containers/repair/ClientRepair').default
                }, {
                    title: '客户回访',
                    key: 'returnVisit',
                    path: '/home/client/repair/returnVisit',
                    ancestor: ['client', 'repair'],
                    component: require('./containers/repair/ReturnVisit').default
                }, {
                    title: '维修费设置',
                    key: 'maintenanceFees',
                    path: '/home/client/repair/maintenanceFees',
                    ancestor: ['client', 'repair'],
                    component: require('./containers/repair/MaintenanceFees').default
                }
            ]
        }, {
            title: '投诉建议',
            key: 'complain',
            path: '/home/client/complain',
            ancestor: ['client'],
            component: require('./containers/Complain').default
        }, {
            title: '整改通知',
            key: 'notice',
            path: '/home/client/notice',
            ancestor: ['client'],
            component: require('./containers/Notice').default
        }, {
            title: '物业公告',
            key: 'propertyNotice',
            path: '/home/client/propertyNotice',
            ancestor: ['client'],
            component: require('./containers/PropertyNotice').default
        }, {
            title: '二次装修',
            key: 'secondaryDecoration',
            path: '/home/client/secondaryDecoration',
            ancestor: ['client'],
            component: require('./containers/SecondaryDecoration').default
        }, {
            title: '保证金管理',
            key: 'margin',
            path: '/home/client/margin',
            ancestor: ['client'],
            component: require('./containers/Margin').default
        }, {
            title: '门禁卡管理',
            key: 'accessCard',
            path: '/home/client/accessCard',
            ancestor: ['client'],
            childRoute: [
                {
                    title: '门禁卡管理',
                    key: 'management',
                    path: '/home/client/accessCard/management',
                    ancestor: ['client', 'accessCard'],
                    component: require('./containers/accessCard/Management').default
                }, {
                    title: '门禁卡押金',
                    key: 'deposit',
                    path: '/home/client/accessCard/deposit',
                    ancestor: ['client', 'accessCard'],
                    component: require('./containers/accessCard/Deposit').default
                }
            ]
        },
        {
            title: '房间梆定',
            key: 'roombinding',
            path: '/home/client/roombinding',
            ancestor: ['client'],
            component: require('./containers/RoomBinding').default
        }
    ]
}

// 详情页路由表
const CLIENT_DETAILS = [
    {
        title: '报修内容',
        key: 'RepairDetail',
        path: '/home/client/repair/repairDetail/:id',
        component: require('./containers/details/repair/RepairDetail').default
    }, {
        title: '维修明细',
        key: 'MaintenanceDetail',
        path: '/home/client/repair/MaintenanceDetail/:id',
        component: require('./containers/details/repair/MaintenanceDetail').default
    }, {
        title: '回访情况',
        key: 'ReturnVisitDetail',
        path: '/home/client/repair/ReturnVisitDetail/:id',
        component: require('./containers/details/repair/ReturnVisitDetail').default
    }, {
        title: '回访登记',
        key: 'ReturnDetail',
        path: '/home/client/repair/ReturnDetail/:id',
        component: require('./containers/details/repair/ReturnDetail').default
    }, {
        title: '保证金详情',
        key: 'CashDepositDetail',
        path: '/home/client/cashDepositDetail/cashDepositDetail/:id',
        component: require('./containers/details/cashDeposit/CashDepositDetail').default
    }, {
        title: '整改通知',
        key: 'CorrectionDetail',
        path: '/home/client/NoticeDetail/CorrectionDetail/:id',
        component: require('./containers/details/CorrectionDetail').default
    }
]
export { CLIENT_DIR, CLIENT_DETAILS }
<|endoftext|>


<commit_before>// @flow
import * as Creators from '../../actions/teams/creators'
import * as SearchGen from '../../actions/search-gen'
import * as SearchConstants from '../../constants/search'
import {getRole, isOwner} from '../../constants/teams'
import AddPeople from '.'
import {HeaderHoc} from '../../common-adapters'
import {navigateAppend} from '../../actions/route-tree'
import {
  connect,
  compose,
  withHandlers,
  withPropsOnChange,
  withState,
  type TypedState,
} from '../../util/container'

const mapStateToProps = (state: TypedState, {routeProps}) => {
  const teamname = routeProps.get('teamname')
  return {
    isEmpty: SearchConstants.getUserInputItemIds(state, {searchKey: 'addToTeamSearch'}).length === 0,
    name: teamname,
    _yourRole: getRole(state, teamname),
  }
}

const mapDispatchToProps = (dispatch: Dispatch, {navigateUp, routeProps}) => ({
  onAddPeople: (role: string, sendNotification: boolean) => {
    dispatch(Creators.addPeopleToTeam(routeProps.get('teamname'), role, sendNotification))
    dispatch(navigateUp())
    dispatch(Creators.getTeams())
    dispatch(SearchGen.createClearSearchResults({searchKey: 'addToTeamSearch'}))
    dispatch(SearchGen.createSetUserInputItems({searchKey: 'addToTeamSearch', searchResults: []}))
  },
  onClose: () => {
    dispatch(navigateUp())
    dispatch(SearchGen.createClearSearchResults({searchKey: 'addToTeamSearch'}))
    dispatch(SearchGen.createSetUserInputItems({searchKey: 'addToTeamSearch', searchResults: []}))
  },
  onOpenRolePicker: (
    role: string,
    sendNotification: boolean,
    allowOwner: boolean,
    onComplete: (string, boolean) => void
  ) => {
    dispatch(
      navigateAppend([
        {
          props: {
            allowOwner,
            onComplete,
            selectedRole: role,
            sendNotificationChecked: sendNotification,
            showNotificationCheckbox: true,
          },
          selected: 'controlledRolePicker',
        },
      ])
    )
  },
})

export default compose(
  connect(mapStateToProps, mapDispatchToProps),
  compose(
    withState('role', 'onRoleChange', 'writer'),
    withState('sendNotification', 'setSendNotification', true),
    withPropsOnChange(['onExitSearch'], props => ({
      onCancel: () => props.onClose(),
      title: 'Add people',
    })),
    withHandlers({
      onAddPeople: ({onAddPeople, role, sendNotification}) => () =>
        role && onAddPeople(role, sendNotification),
      onOpenRolePicker: ({
        onOpenRolePicker,
        role,
        onRoleChange,
        sendNotification,
        setSendNotification,
        _yourRole,
      }) => () => {
        onOpenRolePicker(role, sendNotification, isOwner(_yourRole), (role, sendNotification) => {
          onRoleChange(role)
          setSendNotification(sendNotification)
        })
      },
    }),
    HeaderHoc
  )
)(AddPeople)
<commit_msg>Show search suggestions on team Add People page (#9853)<commit_after>// @flow
import * as Creators from '../../actions/teams/creators'
import * as SearchGen from '../../actions/search-gen'
import * as SearchConstants from '../../constants/search'
import {getRole, isOwner} from '../../constants/teams'
import AddPeople from '.'
import {HeaderHoc} from '../../common-adapters'
import {navigateAppend} from '../../actions/route-tree'
import {
  connect,
  compose,
  lifecycle,
  withHandlers,
  withPropsOnChange,
  withState,
  type TypedState,
} from '../../util/container'

const mapStateToProps = (state: TypedState, {routeProps}) => {
  const teamname = routeProps.get('teamname')
  return {
    isEmpty: SearchConstants.getUserInputItemIds(state, {searchKey: 'addToTeamSearch'}).length === 0,
    name: teamname,
    _yourRole: getRole(state, teamname),
  }
}

const mapDispatchToProps = (dispatch: Dispatch, {navigateUp, routeProps}) => ({
  _getSuggestions: () => dispatch(SearchGen.createSearchSuggestions({searchKey: 'addToTeamSearch'})),
  onAddPeople: (role: string, sendNotification: boolean) => {
    dispatch(Creators.addPeopleToTeam(routeProps.get('teamname'), role, sendNotification))
    dispatch(navigateUp())
    dispatch(Creators.getTeams())
    dispatch(SearchGen.createClearSearchResults({searchKey: 'addToTeamSearch'}))
    dispatch(SearchGen.createSetUserInputItems({searchKey: 'addToTeamSearch', searchResults: []}))
  },
  onClose: () => {
    dispatch(navigateUp())
    dispatch(SearchGen.createClearSearchResults({searchKey: 'addToTeamSearch'}))
    dispatch(SearchGen.createSetUserInputItems({searchKey: 'addToTeamSearch', searchResults: []}))
  },
  onOpenRolePicker: (
    role: string,
    sendNotification: boolean,
    allowOwner: boolean,
    onComplete: (string, boolean) => void
  ) => {
    dispatch(
      navigateAppend([
        {
          props: {
            allowOwner,
            onComplete,
            selectedRole: role,
            sendNotificationChecked: sendNotification,
            showNotificationCheckbox: true,
          },
          selected: 'controlledRolePicker',
        },
      ])
    )
  },
})

export default compose(
  connect(mapStateToProps, mapDispatchToProps),
  compose(
    withState('role', 'onRoleChange', 'writer'),
    withState('sendNotification', 'setSendNotification', true),
    withPropsOnChange(['onExitSearch'], props => ({
      onCancel: () => props.onClose(),
      title: 'Add people',
    })),
    lifecycle({
      componentWillMount: function() {
        this.props._getSuggestions()
      },
    }),
    withHandlers({
      onAddPeople: ({onAddPeople, role, sendNotification}) => () =>
        role && onAddPeople(role, sendNotification),
      onOpenRolePicker: ({
        onOpenRolePicker,
        role,
        onRoleChange,
        sendNotification,
        setSendNotification,
        _yourRole,
      }) => () => {
        onOpenRolePicker(role, sendNotification, isOwner(_yourRole), (role, sendNotification) => {
          onRoleChange(role)
          setSendNotification(sendNotification)
        })
      },
    }),
    HeaderHoc
  )
)(AddPeople)
<|endoftext|>


<commit_before>var bot = require('..');

bot.addListener('message', function (nick,to,text,message) {
  if ( text.substr(0,4) == "!say") {
    bot.notice(to, text.substr(text.indexOf(" ") + 1))
  }
  if ( text.substr(0,4) == "!src") {
    bot.notice(to, nick + " : https://github.com/zuzakistan/civilservant");
  }
})
<commit_msg>add kill<commit_after>var bot = require('..');

bot.addListener('message', function (nick,to,text,message) {
  if ( text.substr(0,4) == "!say") {
    bot.notice(to, text.substr(text.indexOf(" ") + 1))
  } else if ( text.substr(0,4) == "!src") {
    bot.notice(to, nick + " : https://github.com/zuzakistan/civilservant");
  } else if ( text.substr(0,4) == '!quit') {
	  console.log('!quit requested by ' + nick + ' in ' + to);
	  process.exit(0);
  }
})
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var mongodb = require('../mongo_connection');
var studentsCollection = mongodb.db.collection('students');
var bodyParser = require('body-parser');

/*
 * /students GET
 *
 * Returns all students.
 */
router.get('/', function(req, res) {
    studentsCollection.find().toArray()
	.then(function(students) {
	    res.json(students);
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/:id GET
 *
 * Returns the student identified by 'id'.
 */
router.get('/:id', function(req, res) {
    studentsCollection.findOne({'_id': req.params.id})
	.then(function(student) {
	    res.json(student);
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students POST
 *
 * Inserts a new student.
 */
router.post('/', bodyParser.json(), function(req, res) {
    studentsCollection.insertOne(req.body, null)
	.then(function(result) {
	    console.info('studentPOST: ' + result);
	    res.json(result);
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/many POST
 *
 * Inserts an array of new students.
 */
router.post('/many', bodyParser.json(), function(req, res) {
    studentsCollection.insertMany(req.body, null)
	.then(function(result) {
	    console.info('students/many POST: ' + result);
	    res.json(result);
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

module.exports = router;
<commit_msg>+ Multiple students search WIP<commit_after>var express = require('express');
var router = express.Router();
var mongodb = require('../mongo_connection');
var studentsCollection = mongodb.db.collection('students');
var bodyParser = require('body-parser');
var wrapResult = require('./wrap-result').wrapResult;

/*
 * /students GET
 *
 * Returns all students.
 */
router.get('/', bodyParser.json(), function(req, res) {
    var filter = null;
    if (req.body != null) {
	filter = { _id: { $in: req.body } };
    }
    console.info('students GET: ', + req);
    studentsCollection.find(filter).toArray()
	.then(function(students) {
	    res.json(wrapResult(students));
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/:id GET
 *
 * Returns the student identified by 'id'.
 */
router.get('/:id', function(req, res) {
    studentsCollection.findOne({'_id': req.params.id})
	.then(function(student) {
	    res.json(wrapResult(student));
	})
	.catch(function(err) {
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students POST
 *
 * Inserts a new student.
 */
router.post('/', bodyParser.json(), function(req, res) {
    studentsCollection.insertOne(req.body, null)
	.then(function(result) {
	    console.info('studentPOST: ' + result);
	    res.json(wrapResult(result));
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

/*
 * /students/many POST
 *
 * Inserts an array of new students.
 */
router.post('/many', bodyParser.json(), function(req, res) {
    studentsCollection.insertMany(req.body, null)
	.then(function(result) {
	    console.info('students/many POST: ' + result);
	    res.json(wrapResult(result));
	})
	.catch(function(err) {
	    console.error('students POST: ' + err);
	    res.status(500);
	    res.json(err);
	});
});

module.exports = router;
<|endoftext|>