<commit_before>enyo.kind({
	name: "moon.DatePicker",
	kind: "moon.ExpandableListItem",
	classes: "moon-date-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		//* Handler for _onChange_ events coming from constituent controls (day and month)
		onChange: "updateDate"
	},
	published: {
		//* Text to be displayed in the _currentValue_ control if no item is
		//* currently selected
		noneText: "",
		/**
			Current locale used for formatting. May be set after control
			creation, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en-US",
		//* Optional minimum year value
		minYear: 1900,
		//* Optional maximum year value
		maxYear: 2099,
		/**
			The current date as a standard JavaScript Date object. When a Date object
			is passed to _setValue()_, the control is updated to reflect the new
			value. _getValue()_ returns a Date object.
		*/
		value: null
	},
	components: [
		{name: "header", kind: "moon.Item", classes: "moon-date-picker-header", spotlight: true,
			onSpotlightFocus: "headerFocus", ontap: "expandContract", onSpotlightSelect: "expandContract"
		},
		{name: "drawer", kind: "enyo.Drawer", onStep: "drawerAnimationStep", components: [
			{name: "client", classes: "enyo-tool-decorator moon-date-picker-client", onSpotlightLeft:"closePicker", onSpotlightSelect: "closePicker"}
		]},
		{name: "currentValue", kind: "moon.Item", spotlight: false, classes: "moon-date-picker-current-value", ontap: "expandContract", content: ""}
	],
	create: function() {
		this.inherited(arguments);
		if (typeof ilib !== "undefined") {
			this.locale = ilib.getLocale();
		}
		this.initDefaults();
	},
	initDefaults: function() {
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({locale:this.locale});
		}

		this.value = this.value || new Date();
		this.setupPickers(this._tf ? this._tf.getDateComponents() : "mdy");
		this.noneTextChanged();
	},
	setupPickers: function(ordering) {
		var orderingArr = ordering.split("");
		var o,f,l;
		for(f = 0, l = orderingArr.length; f < l; f++) {
			o = orderingArr[f];
			switch (o) {
			case 'd':
				this.createComponent(
					{kind:"moon.IntegerScrollPicker", name:"day", classes:"moon-date-picker-day", min:1,
						max:this.monthLength(this.value.getFullYear(), this.value.getMonth()), value:this.value.getDate()});
				break;
			case 'm':
				this.createComponent({kind:"moon.IntegerScrollPicker", name:"month", classes:"moon-date-picker-month", min:1, max:12, value:this.value.getMonth()+1});
				break;
			case 'y':
				this.createComponent({kind:"moon.IntegerScrollPicker", name:"year", classes:"moon-date-picker-year", value:this.value.getFullYear(), min:this.minYear, max:this.maxYear});
				break;
			default:
				break;
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			var df = new ilib.DateFmt({
				length: 'long',
				locale: new ilib.Locale(this.locale)
			});
			return df.format(new ilib.Date.GregDate(this.value));
		} else {
			return this.getAbbrMonths()[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	updateDate: function(inSender, inEvent) {
		//* Avoid onChange events coming from itself
		if (inEvent && inEvent.originator == this) {
			return;
		}

		var day = this.$.day.getValue(),
			month = this.$.month.getValue()-1,
			year = this.$.year.getValue();

		var maxDays = this.monthLength(year, month);
		this.setValue(new Date(year, month, (day <= maxDays) ? day : maxDays));
		return true;
	},
	valueChanged: function(inOld) {
		this.$.year.setValue(this.value.getFullYear());
		this.$.month.setValue(this.value.getMonth()+1);

		if (inOld &&
			(inOld.getFullYear() != this.value.getFullYear() ||
			inOld.getMonth() != this.value.getMonth())) {
			this.$.day.setMax(this.monthLength(this.value.getFullYear(), this.value.getMonth()));
		}
		this.$.day.setValue(this.value.getDate());

		this.$.currentValue.setContent(this.parseDate());
		this.doChange({name:this.name, value:this.value});
	},
	//* If no item is selected, uses _this.noneText_ as current value.
	noneTextChanged: function() {
		if(this.value == null) {
			this.$.currentValue.setContent(this.getNoneText());
		} else {
			this.$.currentValue.setContent(this.parseDate());
		}
	},
	//* When _this.open_ changes, shows/hides _this.$.currentValue_.
	openChanged: function() {
		this.inherited(arguments);
		this.$.currentValue.setShowing(!this.$.drawer.getOpen());
		//Force the pickers to update their scroll positions (they don't update while the drawer is closed)
		if (this.$.drawer.getOpen()) {
			this.$.day.render();
			this.$.month.render();
			this.$.year.render();
		}
	},
	closePicker: function(inSender, inEvent) {
		//* If select/enter is pressed on any date picker item or the left key is pressed on the first item, close the drawer
		if (inEvent.type == "onSpotlightSelect" ||
			this.$.client.children[0].id == inEvent.originator.id) {
			this.updateDate();
			this.expandContract();
			this.noneTextChanged();
			return true;
		}
	},
	getAbbrMonths: function() {
		return ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
	},
	//* Returns number of days in a particular month/year.
	monthLength: function(inYear, inMonth) {
		return 32 - new Date(inYear, inMonth, 32).getDate();
	},
	localeChanged: function() {
		this.refresh();
	},
	refresh: function(){
		this.destroyClientControls();
		if (this._tf) {
			delete this._tf;
		}
		this.initDefaults();
		this.render();
	}
});<commit_msg>GF:1345: Fixes to DatePicker integration with ilib.  The DateFormat "components" are not ordered (per ilib docs), so we need to use the DateFormat template (which has a different format.  Don't instantiate a new DateFormat every time the picker date changes.<commit_after>enyo.kind({
	name: "moon.DatePicker",
	kind: "moon.ExpandableListItem",
	classes: "moon-date-picker",
	events: {
		/**
			Fires when the date changes.

			_inEvent.name_ contains the name of this control.

			_inEvent.value_ contains a standard JavaScript Date object representing
			the current date.
		*/
		onChange: ""
	},
	handlers: {
		//* Handler for _onChange_ events coming from constituent controls (day and month)
		onChange: "updateDate"
	},
	published: {
		//* Text to be displayed in the _currentValue_ control if no item is
		//* currently selected
		noneText: "",
		/**
			Current locale used for formatting. May be set after control
			creation, in which case the control will be updated to reflect the
			new value.
		*/
		locale: "en-US",
		//* Optional minimum year value
		minYear: 1900,
		//* Optional maximum year value
		maxYear: 2099,
		/**
			The current date as a standard JavaScript Date object. When a Date object
			is passed to _setValue()_, the control is updated to reflect the new
			value. _getValue()_ returns a Date object.
		*/
		value: null
	},
	components: [
		{name: "header", kind: "moon.Item", classes: "moon-date-picker-header", spotlight: true,
			onSpotlightFocus: "headerFocus", ontap: "expandContract", onSpotlightSelect: "expandContract"
		},
		{name: "drawer", kind: "enyo.Drawer", onStep: "drawerAnimationStep", components: [
			{name: "client", classes: "enyo-tool-decorator moon-date-picker-client", onSpotlightLeft:"closePicker", onSpotlightSelect: "closePicker"}
		]},
		{name: "currentValue", kind: "moon.Item", spotlight: false, classes: "moon-date-picker-current-value", ontap: "expandContract", content: ""}
	],
	create: function() {
		this.inherited(arguments);
		if (typeof ilib !== "undefined") {
			this.locale = ilib.getLocale();
		}
		this.initDefaults();
	},
	initDefaults: function() {
		//Attempt to use the ilib lib (assuming that it is loaded)
		if (typeof ilib !== "undefined") {
			this._tf = new ilib.DateFmt({locale:this.locale});
		}

		this.value = this.value || new Date();
		this.setupPickers(this._tf ? this._tf.getTemplate() : "mdy");
		this.noneTextChanged();
	},
	setupPickers: function(ordering) {
		var orderingArr = ordering.toLowerCase().split("");
		var doneArr = [];
		var o,f,l;
		for(f = 0, l = orderingArr.length; f < l; f++) {
			o = orderingArr[f];
			if (doneArr.indexOf(o) < 0) {
				switch (o) {
				case 'd':
					this.createComponent(
						{kind:"moon.IntegerScrollPicker", name:"day", classes:"moon-date-picker-day", min:1,
							max:this.monthLength(this.value.getFullYear(), this.value.getMonth()), value:this.value.getDate()});
					break;
				case 'm':
					this.createComponent({kind:"moon.IntegerScrollPicker", name:"month", classes:"moon-date-picker-month", min:1, max:12, value:this.value.getMonth()+1});
					break;
				case 'y':
					this.createComponent({kind:"moon.IntegerScrollPicker", name:"year", classes:"moon-date-picker-year", value:this.value.getFullYear(), min:this.minYear, max:this.maxYear});
					break;
				default:
					break;
				}
				doneArr.push(o);
			}
		}
	},
	parseDate: function() {
		if (this._tf) {
			return this._tf.format(new ilib.Date.GregDate({unixtime: this.value.getTime(), timezone:"UTC"}));
		} else {
			return this.getAbbrMonths()[this.value.getMonth()] + " " + this.value.getDate() + ", " + this.value.getFullYear();
		}
	},
	updateDate: function(inSender, inEvent) {
		//* Avoid onChange events coming from itself
		if (inEvent && inEvent.originator == this) {
			return;
		}

		var day = this.$.day.getValue(),
			month = this.$.month.getValue()-1,
			year = this.$.year.getValue();

		var maxDays = this.monthLength(year, month);
		this.setValue(new Date(year, month, (day <= maxDays) ? day : maxDays));
		return true;
	},
	valueChanged: function(inOld) {
		this.$.year.setValue(this.value.getFullYear());
		this.$.month.setValue(this.value.getMonth()+1);

		if (inOld &&
			(inOld.getFullYear() != this.value.getFullYear() ||
			inOld.getMonth() != this.value.getMonth())) {
			this.$.day.setMax(this.monthLength(this.value.getFullYear(), this.value.getMonth()));
		}
		this.$.day.setValue(this.value.getDate());

		this.$.currentValue.setContent(this.parseDate());
		if (this.value) {
			this.doChange({name:this.name, value:this.value});
		}
	},
	//* If no item is selected, uses _this.noneText_ as current value.
	noneTextChanged: function() {
		if(this.value == null) {
			this.$.currentValue.setContent(this.getNoneText());
		} else {
			this.$.currentValue.setContent(this.parseDate());
		}
	},
	//* When _this.open_ changes, shows/hides _this.$.currentValue_.
	openChanged: function() {
		this.inherited(arguments);
		this.$.currentValue.setShowing(!this.$.drawer.getOpen());
		//Force the pickers to update their scroll positions (they don't update while the drawer is closed)
		if (this.$.drawer.getOpen()) {
			this.$.day.render();
			this.$.month.render();
			this.$.year.render();
		}
	},
	closePicker: function(inSender, inEvent) {
		//* If select/enter is pressed on any date picker item or the left key is pressed on the first item, close the drawer
		if (inEvent.type == "onSpotlightSelect" ||
			this.$.client.children[0].id == inEvent.originator.id) {
			this.updateDate();
			this.expandContract();
			this.noneTextChanged();
			return true;
		}
	},
	getAbbrMonths: function() {
		return ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
	},
	//* Returns number of days in a particular month/year.
	monthLength: function(inYear, inMonth) {
		return 32 - new Date(inYear, inMonth, 32).getDate();
	},
	localeChanged: function() {
		this.refresh();
	},
	refresh: function(){
		this.destroyClientControls();
		if (this._tf) {
			delete this._tf;
		}
		this.initDefaults();
		this.render();
	}
});
<|endoftext|>


<commit_before>(function() {

// This code is only for iOS
if (!window.navigator.userAgent.match(/(iPhone|iPad|iPod)/))
	return;

var clicksEaten = [];
var CONFIG = { TOUCH_MOVE_THRESHHOLD: 10, PRESSED_CLASS: "pressed" }

function withinDistance(x1, y1, x2, y2, distance) {
	return Math.abs(x1 - x2) < distance && Math.abs(y1 - y2) < distance;
}

$(document).on('touchstart', '.button', function(e) {
	var elem = $(this);

	// Disable the webkit tap highlight, since it is no longer accurate
	elem.css('webkitTapHighlightColor', 'rgba(0,0,0,0)');

	elem.addClass(CONFIG.PRESSED_CLASS);

	var touch = e.originalEvent.touches[0];
	var location = [touch.clientX, touch.clientY];
	this.__eventLocation = location;

	this.__onTouchMove = function(e) {
		var touch = e.originalEvent.touches[0];
		if (withinDistance(touch.clientX, touch.clientY, location[0], location[1],
			CONFIG.TOUCH_MOVE_THRESHHOLD)) {
			elem.addClass(CONFIG.PRESSED_CLASS);
		} else {
			elem.removeClass(CONFIG.PRESSED_CLASS);
		}
	};

	$(document.body).on('touchmove', this.__onTouchMove);
});

$(document).on('touchcancel', '.button', function(e) {
	var elem = $(this);
	elem.removeClass(CONFIG.PRESSED_CLASS);
	$(document.body).off('touchmove', this.__onTouchMove);
});

$(document).on('touchend', '.button', function(e) {
	var elem = $(this);
	if (elem.hasClass(CONFIG.PRESSED_CLASS)) {
		elem.removeClass(CONFIG.PRESSED_CLASS);
		var location = this.__eventLocation;
		if (location) {
			var touch = e.originalEvent.changedTouches[0];
			if (!withinDistance(touch.clientX, touch.clientY, location[0], location[1], CONFIG.TOUCH_MOVE_THRESHHOLD))
				return;

			// Dispatch a fake click event
			var evt = document.createEvent("MouseEvents");
			evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
			this.dispatchEvent(evt);

			// Don't process the default for this event to avoid WebKit stealing focus from a view we might be loading
			e.preventDefault();

			clicksEaten.push(location);

		}
	}

	$(document.body).off('touchmove', this.__onTouchMove);
});

})()

<commit_msg>Remove unused global<commit_after>(function() {

// This code is only for iOS
if (!window.navigator.userAgent.match(/(iPhone|iPad|iPod)/))
	return;

var CONFIG = { TOUCH_MOVE_THRESHHOLD: 10, PRESSED_CLASS: "pressed" }

function withinDistance(x1, y1, x2, y2, distance) {
	return Math.abs(x1 - x2) < distance && Math.abs(y1 - y2) < distance;
}

$(document).on('touchstart', '.button', function(e) {
	var elem = $(this);

	// Disable the webkit tap highlight, since it is no longer accurate
	elem.css('webkitTapHighlightColor', 'rgba(0,0,0,0)');

	elem.addClass(CONFIG.PRESSED_CLASS);

	var touch = e.originalEvent.touches[0];
	var location = [touch.clientX, touch.clientY];
	this.__eventLocation = location;

	this.__onTouchMove = function(e) {
		var touch = e.originalEvent.touches[0];
		if (withinDistance(touch.clientX, touch.clientY, location[0], location[1],
			CONFIG.TOUCH_MOVE_THRESHHOLD)) {
			elem.addClass(CONFIG.PRESSED_CLASS);
		} else {
			elem.removeClass(CONFIG.PRESSED_CLASS);
		}
	};

	$(document.body).on('touchmove', this.__onTouchMove);
});

$(document).on('touchcancel', '.button', function(e) {
	var elem = $(this);
	elem.removeClass(CONFIG.PRESSED_CLASS);
	$(document.body).off('touchmove', this.__onTouchMove);
});

$(document).on('touchend', '.button', function(e) {
	var elem = $(this);
	if (elem.hasClass(CONFIG.PRESSED_CLASS)) {
		elem.removeClass(CONFIG.PRESSED_CLASS);
		var location = this.__eventLocation;
		if (location) {
			var touch = e.originalEvent.changedTouches[0];
			if (!withinDistance(touch.clientX, touch.clientY, location[0], location[1], CONFIG.TOUCH_MOVE_THRESHHOLD))
				return;

			// Dispatch a fake click event
			var evt = document.createEvent("MouseEvents");
			evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
			this.dispatchEvent(evt);

			// Don't process the default action for this event to avoid WebKit stealing focus from a
			// view we might be loading, and from dispatching a click event
			e.preventDefault();
		}
	}

	$(document.body).off('touchmove', this.__onTouchMove);
});

})()

<|endoftext|>


<commit_before>// 28: class - super in constructor
// To do: make all tests pass, leave the assert lines unchanged!

var assert = require("assert");

describe('class', () => {

  it('if you `extend` a class, use `super()` to call the parent constructor', () => {
    class A {constructor() { this.levels = 1; }}
    class B {
      constructor() {
        this.levels++;
      }
    }

    assert.equal(new B().levels, 2);
  });

  it('`super()` may also take params', () => {
    class A {constructor(startValue=1, addTo=1) { this.counter = startValue + addTo; }}
    class B extends A {
      constructor(...args) {
        super();
        this.counter++;
      }
    }

    assert.equal(new B(42, 2).counter, 45);
  });

  it('it is important where you place your `super()` call!', () => {
    class A {inc() { this.countUp = 1; }}
    class B extends A {
      inc() {
        super.inc();
        this.countUp = 2;
        return this.countUp;
      }
    }

    assert.equal(new B().inc(), 1);
  });

  it('use `super.constructor` to find out if there is a parent constructor', () => {
    class A extends null {
      constructor() {
        super();
        this.isTop = !!super.constructor;
      }
    }

    assert.equal(new A().isTop, false);
  });

});

<commit_msg>fix 28-class-super-in-constructor.js<commit_after>// 28: class - super in constructor
// To do: make all tests pass, leave the assert lines unchanged!

var assert = require("assert");

describe('class', () => {

  it('if you `extend` a class, use `super()` to call the parent constructor', () => {
    class A {constructor() { this.levels = 1; }}
    class B extends A {
      constructor() {
        super();
        this.levels++;
      }
    }

    assert.equal(new B().levels, 2);
  });

  it('`super()` may also take params', () => {
    class A {constructor(startValue=1, addTo=1) { this.counter = startValue + addTo; }}
    class B extends A {
      constructor(...args) {
        super(...args);
        this.counter++;
      }
    }

    assert.equal(new B(42, 2).counter, 45);
  });

  it('it is important where you place your `super()` call!', () => {
    class A {inc() { this.countUp = 1; }}
    class B extends A {
      inc() {
        this.countUp = 2;
        super.inc();
        return this.countUp;
      }
    }

    assert.equal(new B().inc(), 1);
  });

  it('use `super.constructor` to find out if there is a parent constructor', () => {
    class A extends null {
      constructor() {
        super();
        this.isTop = !super.constructor;
      }
    }

    assert.equal(new A().isTop, false);
  });

});

<|endoftext|>


<commit_before>var util = require('util');
var path = require('path');
var url = require('url');
var iconv = require('iconv-lite');
var request = require('./request');
var isUtf8 = require('../util/is-utf8');
var ca = require('../https/ca');
var Storage = require('../rules/storage');
var getServer = require('hagent').create(null, 40500);

var QUERY_RE = /\?.*$/;
var REQ_ID_RE = /^\d{13,15}-\d{1,5}$/;
var sessionOpts, sessionTimer, sessionPending;
var framesOpts, framesTimer, framesPending;
var reqCallbacks = {};
var resCallbacks = {};
var framesCallbacks = [];
var MAX_LENGTH = 100;
var TIMEOUT = 1000;
var pluginOpts, storage;
var noop = function() {};
/* eslint-disable no-undef */
var REQ_ID_KEY = typeof Symbol === 'undefined' ? '$reqId_' + Date.now() : Symbol();
var SESSION_KEY = typeof Symbol === 'undefined' ? '$session_' + Date.now() : Symbol();
var FRAME_KEY = typeof Symbol === 'undefined' ? '$frame_' + Date.now() : Symbol();
var REQ_KEY = typeof Symbol === 'undefined' ? '$req_' + Date.now() : Symbol();
/* eslint-enable no-undef */
var ctx;

var getValue = function(req, name) {
  const value = req.headers[name] || '';
  try {
    return value ? decodeURIComponent(value) : '';
  } catch(e) {}
  return String(value);
};
var setContenxt = function(req) {
  if (ctx) {
    ctx.request = req;
    req.ctx = ctx;
  }
  req.localStorage = storage;
  req.Storage = Storage;
  req.clientIp = getValue(req, pluginOpts.CLIENT_IP_HEADER) || '127.0.0.1';
};
var initReq = function(req, res) {
  req.on('error', noop);
  res.on('error', noop);
  var oReq = req.originalReq = {};
  var oRes = req.originalRes = {};
  setContenxt(req);
  oReq.clientIp = req.clientIp;
  req[REQ_ID_KEY] = oReq.id = getValue(req, pluginOpts.REQ_ID_HEADER);
  oReq.ruleValue = getValue(req, pluginOpts.RULE_VALUE_HEADER);
  oReq.url = oReq.fullUrl = getValue(req, pluginOpts.FULL_URL_HEADER);
  oReq.realUrl = getValue(req, pluginOpts.REAL_URL_HEADER);
  oReq.method = getValue(req, pluginOpts.METHOD_HEADER) || 'GET';
  oReq.clientPort = getValue(req, pluginOpts.CLIENT_PORT_HEAD);
  oReq.globalValue = getValue(req, pluginOpts.GLOBAL_VALUE_HEAD);
  oReq.proxyValue = getValue(req, pluginOpts.PROXY_VALUE_HEADER);
  oReq.pacValue = getValue(req, pluginOpts.PAC_VALUE_HEADER);
  oRes.serverIp = getValue(req, pluginOpts.HOST_IP_HEADER);
  oRes.statusCode = getValue(req, pluginOpts.STATUS_CODE_HEADER);
};
var toBuffer = function(base64) {
  if (base64) {
    try {
      return new Buffer(base64, 'base64');
    } catch(e) {}
  }
};
var getBuffer = function(item) {
  return toBuffer(item.base64);
};
var getText = function(item) {
  var body = toBuffer(item.base64) || '';
  if (body && !isUtf8(body)) {
    try {
      body = iconv.encode(body, 'GB18030');
    } catch(e) {}
  }
  return body + '';
};

var defineProps = function(obj) {
  if (!obj) {
    return;
  }
  if (Object.defineProperties) {
    Object.defineProperties(obj, {
      body: {
        get: function() {
          return getText(obj);
        }
      },
      buffer: {
        get: function() {
          return getBuffer(obj);
        }
      }
    });
  } else {
    obj.body = getText(obj);
    obj.buffer = getBuffer(obj);
  }
};

var execCallback = function(id, cbs, item) {
  var cbList = cbs[id];
  if (cbList && (cbs === reqCallbacks || !item || item.endTime)) {
    item = item || '';
    defineProps(item.req);
    defineProps(item.res);
    delete cbs[id];
    cbList.forEach(function(cb) {
      try {
        cb(item);
      } catch(e) {}
    });
  }
};

var retryRequestSession = function(time) {
  if (!sessionTimer) {
    sessionTimer = setTimeout(requestSessions, time || TIMEOUT);
  }
};

var requestSessions = function() {
  clearTimeout(sessionTimer);
  sessionTimer = null;
  if (sessionPending) {
    return;
  }
  var reqList = Object.keys(reqCallbacks);
  var resList = Object.keys(resCallbacks);
  if (!reqList.length && !resList.length) {
    return;
  }
  sessionPending = true;
  var _reqList = reqList.slice(0, MAX_LENGTH);
  var _resList = resList.slice(0, MAX_LENGTH);
  var query = '?reqList=' + JSON.stringify(_reqList) + '&resList=' + JSON.stringify(_resList);
  sessionOpts.path = sessionOpts.path.replace(QUERY_RE, query);
  request(sessionOpts, function(err, result) {
    sessionPending = false;
    if (err || !result) {
      return retryRequestSession();
    }
    Object.keys(result).forEach(function(id) {
      var item = result[id];
      execCallback(id, reqCallbacks, item);
      execCallback(id, resCallbacks, item);
    });
    retryRequestSession(20);
  });
};

var retryRequestFrames = function(time) {
  if (!framesTimer) {
    framesTimer = setTimeout(requestFrames, time || TIMEOUT);
  }
};
var requestFrames = function() {
  clearTimeout(framesTimer);
  framesTimer = null;
  if (framesPending) {
    return;
  }
  var cb = framesCallbacks.shift();
  if (!cb) {
    return;
  }
  var req = cb[REQ_KEY];
  console.log(req[FRAME_KEY])
  framesPending = true;
  var query = '?curReqId=' + req[REQ_ID_KEY] + '&lastFrameId=' + (req[FRAME_KEY] || '');
  framesOpts.path = framesOpts.path.replace(QUERY_RE, query);
  request(framesOpts, function(err, result) {
    framesPending = false;
    if (err || !result) {
      framesCallbacks.unshift(cb);
      return retryRequestFrames();
    }
    var frames = result.frames;
    if (Array.isArray(frames)) {
      var last = frames[frames.length - 1];
      var frameId = last && last.frameId;
      if (frameId) {
        req[FRAME_KEY] = frameId;
      }
    }
    retryRequestFrames(20);
    try {
      cb(frames || []);
    } catch(e) {}
  });
};

var getFrames = function(req, cb) {
  var reqId = req[REQ_ID_KEY];
  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {
    return;
  }
  var url = req.originalReq.url;
  if (!/^ws/.test(url)) {
    return cb('');
  }
  cb[REQ_KEY] = req;
  framesCallbacks.push(cb);
  getSession(req, function(session) {
    if (!session || session.reqError || session.resError) {
      framesCallbacks.forEach(function(_cb) {
        _cb('');
      });
      framesCallbacks = [];
      return;
    }
    requestFrames();
  });
};

var getSession = function(req, cb, isReq) {
  var reqId = req[REQ_ID_KEY];
  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {
    return;
  }
  var session = req[SESSION_KEY];
  if (session != null) {
    if (isReq) {
      return cb(session);
    }
    if (!session || session.endTime) {
      return cb(session);
    }
  }
  var cbList = isReq ? reqCallbacks[reqId] : resCallbacks[reqId];
  if (cbList) {
    if (cbList.indexOf(cb) === -1) {
      cbList.push(cb);
    }
  } else {
    cbList = [function(s) {
      req[SESSION_KEY] = s;
      cb(s);
    }];
  }
  if (isReq) {
    reqCallbacks[reqId] = cbList;
  } else {
    resCallbacks[reqId] = cbList;
  }
  retryRequestSession(20);
};

var handleStatsResponse = function(req, res) {
  initReq(req, res);
  req.getReqSession = function(cb) {
    return getSession(req, cb, true);
  };
  req.getSession = function(cb) {
    return getSession(req, cb);
  };
  req.getFrames = function(cb) {
    return getFrames(req, cb);
  };
  res.end();
};
var addSessionApi = function(req, res) {
  initReq(req, res);
  req.unsafe_getReqSession = function(cb) {
    return getSession(req, cb, true);
  };
  req.unsafe_getSession = function(cb) {
    return getSession(req, cb);
  };
  req.unsafe_getFrames = function(cb) {
    return getFrames(req, cb);
  };
};

var loadModule = function(filepath) {
  try {
    return require(filepath);
  } catch (e) {}
};

module.exports = function(options, callback) {
  options.getRootCAFile = ca.getRootCAFile;
  options.createCertificate = ca.createCertificate;
  options.Storage = Storage;
  storage = new Storage(path.join(options.config.baseDir, '.plugins', options.name));
  options.storage = options.localStorage = storage;
  var config = options.config;
  pluginOpts = options;
  var headers = { 'x-whistle-auth-key': config.authKey };
  var baseUrl = 'http://127.0.0.1:' + config.uiport + '/cgi-bin/';
  sessionOpts = url.parse(baseUrl + 'get-session?');
  sessionOpts.headers = headers;
  framesOpts = url.parse(baseUrl + 'get-frames?');
  framesOpts.headers = headers;
  delete config.authKey;
  if (options.debugMode) {
    var cacheLogs = [];
    /*eslint no-console: "off"*/
    console.log = function() {
      var msg = util.format.apply(this, arguments);
      if (cacheLogs) {
        cacheLogs.push(msg);
      } else {
        process.sendData({
          type: 'console.log',
          message: msg
        });
      }
    };
    process.on('data', function(data) {
      if (cacheLogs && data && data.type == 'console.log' && data.status == 'ready') {
        var _cacheLogs = cacheLogs;
        cacheLogs = null;
        _cacheLogs.forEach(function(msg) {
          process.sendData({
            type: 'console.log',
            message: msg
          });
        });
      }
    });
  }

  var port, statsPort, resStatsPort, uiPort, rulesPort, resRulesPort, tunnelRulesPort, tunnelPort;
  var count = 0;
  var callbackHandler = function() {
    if (--count <= 0) {
      callback(null, {
        port: port,
        statsPort: statsPort,
        resStatsPort: resStatsPort,
        uiPort: uiPort,
        rulesPort: rulesPort,
        resRulesPort: resRulesPort,
        tunnelRulesPort: tunnelRulesPort,
        tunnelPort: tunnelPort
      });
    }
  };

  try {
    require.resolve(options.value);
  } catch(e) {
    return callbackHandler();
  }

  var initServers = function(_ctx) {
    ctx = _ctx || ctx;
    var execPlugin = require(options.value);
    var startServer = execPlugin.pluginServer || execPlugin.server || execPlugin;
    if (typeof startServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startServer(server, options);
        port = _port;
        callbackHandler();
      });
    }

    var startStatsServer = execPlugin.statsServer || execPlugin.reqStatsServer;
    if (typeof startStatsServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', handleStatsResponse);
        startStatsServer(server, options);
        statsPort = _port;
        callbackHandler();
      });
    }

    var startResStatsServer = execPlugin.resStatsServer;
    if (typeof startResStatsServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', handleStatsResponse);
        startResStatsServer(server, options);
        resStatsPort = _port;
        callbackHandler();
      });
    }

    var startUIServer = execPlugin.uiServer || execPlugin.innerServer || execPlugin.internalServer;
    if (typeof startUIServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', setContenxt);
        startUIServer(server, options);
        uiPort = _port;
        callbackHandler();
      });
    }

    var startRulesServer = execPlugin.pluginRulesServer || execPlugin.rulesServer || execPlugin.reqRulesServer;
    if (typeof startRulesServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startRulesServer(server, options);
        rulesPort = _port;
        callbackHandler();
      });
    }

    var startResRulesServer = execPlugin.resRulesServer;
    if (typeof startResRulesServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', function(req, res) {
          initReq(req, res);
          req.getReqSession = function(cb) {
            return getSession(req, cb, true);
          };
          req.unsafe_getSession = function(cb) {
            return getSession(req, cb);
          };
          req.unsafe_getFrames = function(cb) {
            return getFrames(req, cb);
          };
        });
        startResRulesServer(server, options);
        resRulesPort = _port;
        callbackHandler();
      });
    }

    var startTunnelRulesServer = execPlugin.pluginRulesServer || execPlugin.tunnelRulesServer;
    if (typeof startTunnelRulesServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startTunnelRulesServer(server, options);
        tunnelRulesPort = _port;
        callbackHandler();
      });
    }

    var startTunnelServer = execPlugin.pluginServer || execPlugin.tunnelServer || execPlugin.connectServer;
    if (typeof startTunnelServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startTunnelServer(server, options);
        tunnelPort = _port;
        callbackHandler();
      });
    }

    if (!count) {
      callbackHandler();
    }
  };
  var initial = loadModule(path.join(options.value, 'initial.js'));
  if (typeof initial === 'function') {
    if (initial.length === 2) {
      ctx = initial(options, initServers);
      return ctx;
    }
    ctx = initial(options);
  }
  initServers();
};

<commit_msg>feat: add getFrames<commit_after>var util = require('util');
var path = require('path');
var url = require('url');
var iconv = require('iconv-lite');
var request = require('./request');
var isUtf8 = require('../util/is-utf8');
var ca = require('../https/ca');
var Storage = require('../rules/storage');
var getServer = require('hagent').create(null, 40500);

var QUERY_RE = /\?.*$/;
var REQ_ID_RE = /^\d{13,15}-\d{1,5}$/;
var sessionOpts, sessionTimer, sessionPending;
var framesOpts, framesTimer, framesPending;
var reqCallbacks = {};
var resCallbacks = {};
var framesCallbacks = [];
var MAX_LENGTH = 100;
var TIMEOUT = 1000;
var pluginOpts, storage;
var noop = function() {};
/* eslint-disable no-undef */
var REQ_ID_KEY = typeof Symbol === 'undefined' ? '$reqId_' + Date.now() : Symbol();
var SESSION_KEY = typeof Symbol === 'undefined' ? '$session_' + Date.now() : Symbol();
var FRAME_KEY = typeof Symbol === 'undefined' ? '$frame_' + Date.now() : Symbol();
var REQ_KEY = typeof Symbol === 'undefined' ? '$req_' + Date.now() : Symbol();
/* eslint-enable no-undef */
var ctx;

var getValue = function(req, name) {
  const value = req.headers[name] || '';
  try {
    return value ? decodeURIComponent(value) : '';
  } catch(e) {}
  return String(value);
};
var setContenxt = function(req) {
  if (ctx) {
    ctx.request = req;
    req.ctx = ctx;
  }
  req.localStorage = storage;
  req.Storage = Storage;
  req.clientIp = getValue(req, pluginOpts.CLIENT_IP_HEADER) || '127.0.0.1';
};
var initReq = function(req, res) {
  req.on('error', noop);
  res.on('error', noop);
  var oReq = req.originalReq = {};
  var oRes = req.originalRes = {};
  setContenxt(req);
  oReq.clientIp = req.clientIp;
  req[REQ_ID_KEY] = oReq.id = getValue(req, pluginOpts.REQ_ID_HEADER);
  oReq.ruleValue = getValue(req, pluginOpts.RULE_VALUE_HEADER);
  oReq.url = oReq.fullUrl = getValue(req, pluginOpts.FULL_URL_HEADER);
  oReq.realUrl = getValue(req, pluginOpts.REAL_URL_HEADER);
  oReq.method = getValue(req, pluginOpts.METHOD_HEADER) || 'GET';
  oReq.clientPort = getValue(req, pluginOpts.CLIENT_PORT_HEAD);
  oReq.globalValue = getValue(req, pluginOpts.GLOBAL_VALUE_HEAD);
  oReq.proxyValue = getValue(req, pluginOpts.PROXY_VALUE_HEADER);
  oReq.pacValue = getValue(req, pluginOpts.PAC_VALUE_HEADER);
  oRes.serverIp = getValue(req, pluginOpts.HOST_IP_HEADER);
  oRes.statusCode = getValue(req, pluginOpts.STATUS_CODE_HEADER);
};
var toBuffer = function(base64) {
  if (base64) {
    try {
      return new Buffer(base64, 'base64');
    } catch(e) {}
  }
};
var getBuffer = function(item) {
  return toBuffer(item.base64);
};
var getText = function(item) {
  var body = toBuffer(item.base64) || '';
  if (body && !isUtf8(body)) {
    try {
      body = iconv.encode(body, 'GB18030');
    } catch(e) {}
  }
  return body + '';
};

var defineProps = function(obj) {
  if (!obj) {
    return;
  }
  if (Object.defineProperties) {
    Object.defineProperties(obj, {
      body: {
        get: function() {
          return getText(obj);
        }
      },
      buffer: {
        get: function() {
          return getBuffer(obj);
        }
      }
    });
  } else {
    obj.body = getText(obj);
    obj.buffer = getBuffer(obj);
  }
};

var execCallback = function(id, cbs, item) {
  var cbList = cbs[id];
  if (cbList && (cbs === reqCallbacks || !item || item.endTime)) {
    item = item || '';
    defineProps(item.req);
    defineProps(item.res);
    delete cbs[id];
    cbList.forEach(function(cb) {
      try {
        cb(item);
      } catch(e) {}
    });
  }
};

var retryRequestSession = function(time) {
  if (!sessionTimer) {
    sessionTimer = setTimeout(requestSessions, time || TIMEOUT);
  }
};

var requestSessions = function() {
  clearTimeout(sessionTimer);
  sessionTimer = null;
  if (sessionPending) {
    return;
  }
  var reqList = Object.keys(reqCallbacks);
  var resList = Object.keys(resCallbacks);
  if (!reqList.length && !resList.length) {
    return;
  }
  sessionPending = true;
  var _reqList = reqList.slice(0, MAX_LENGTH);
  var _resList = resList.slice(0, MAX_LENGTH);
  var query = '?reqList=' + JSON.stringify(_reqList) + '&resList=' + JSON.stringify(_resList);
  sessionOpts.path = sessionOpts.path.replace(QUERY_RE, query);
  request(sessionOpts, function(err, result) {
    sessionPending = false;
    if (err || !result) {
      return retryRequestSession();
    }
    Object.keys(result).forEach(function(id) {
      var item = result[id];
      execCallback(id, reqCallbacks, item);
      execCallback(id, resCallbacks, item);
    });
    retryRequestSession(20);
  });
};

var retryRequestFrames = function(time) {
  if (!framesTimer) {
    framesTimer = setTimeout(requestFrames, time || TIMEOUT);
  }
};
var requestFrames = function() {
  clearTimeout(framesTimer);
  framesTimer = null;
  if (framesPending) {
    return;
  }
  var cb = framesCallbacks.shift();
  if (!cb) {
    return;
  }
  var req = cb[REQ_KEY];
  framesPending = true;
  var query = '?curReqId=' + req[REQ_ID_KEY] + '&lastFrameId=' + (req[FRAME_KEY] || '');
  framesOpts.path = framesOpts.path.replace(QUERY_RE, query);
  request(framesOpts, function(err, result) {
    framesPending = false;
    if (err || !result) {
      framesCallbacks.unshift(cb);
      return retryRequestFrames();
    }
    var frames = result.frames;
    if (Array.isArray(frames)) {
      var last = frames[frames.length - 1];
      var frameId = last && last.frameId;
      if (frameId) {
        req[FRAME_KEY] = frameId;
      }
      frames.forEach(defineProps);
    }
    retryRequestFrames(20);
    try {
      cb(frames || []);
    } catch(e) {}
  });
};

var getFrames = function(req, cb) {
  var reqId = req[REQ_ID_KEY];
  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {
    return;
  }
  var url = req.originalReq.url;
  if (!/^ws/.test(url)) {
    return cb('');
  }
  cb[REQ_KEY] = req;
  framesCallbacks.push(cb);
  getSession(req, function(session) {
    if (!session || session.reqError || session.resError) {
      framesCallbacks.forEach(function(_cb) {
        _cb('');
      });
      framesCallbacks = [];
      return;
    }
    requestFrames();
  });
};

var getSession = function(req, cb, isReq) {
  var reqId = req[REQ_ID_KEY];
  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {
    return;
  }
  var session = req[SESSION_KEY];
  if (session != null) {
    if (isReq) {
      return cb(session);
    }
    if (!session || session.endTime) {
      return cb(session);
    }
  }
  var cbList = isReq ? reqCallbacks[reqId] : resCallbacks[reqId];
  if (cbList) {
    if (cbList.indexOf(cb) === -1) {
      cbList.push(cb);
    }
  } else {
    cbList = [function(s) {
      req[SESSION_KEY] = s;
      cb(s);
    }];
  }
  if (isReq) {
    reqCallbacks[reqId] = cbList;
  } else {
    resCallbacks[reqId] = cbList;
  }
  retryRequestSession(20);
};

var handleStatsResponse = function(req, res) {
  initReq(req, res);
  req.getReqSession = function(cb) {
    return getSession(req, cb, true);
  };
  req.getSession = function(cb) {
    return getSession(req, cb);
  };
  req.getFrames = function(cb) {
    return getFrames(req, cb);
  };
  res.end();
};
var addSessionApi = function(req, res) {
  initReq(req, res);
  req.unsafe_getReqSession = function(cb) {
    return getSession(req, cb, true);
  };
  req.unsafe_getSession = function(cb) {
    return getSession(req, cb);
  };
  req.unsafe_getFrames = function(cb) {
    return getFrames(req, cb);
  };
};

var loadModule = function(filepath) {
  try {
    return require(filepath);
  } catch (e) {}
};

module.exports = function(options, callback) {
  options.getRootCAFile = ca.getRootCAFile;
  options.createCertificate = ca.createCertificate;
  options.Storage = Storage;
  storage = new Storage(path.join(options.config.baseDir, '.plugins', options.name));
  options.storage = options.localStorage = storage;
  var config = options.config;
  pluginOpts = options;
  var headers = { 'x-whistle-auth-key': config.authKey };
  var baseUrl = 'http://127.0.0.1:' + config.uiport + '/cgi-bin/';
  sessionOpts = url.parse(baseUrl + 'get-session?');
  sessionOpts.headers = headers;
  framesOpts = url.parse(baseUrl + 'get-frames?');
  framesOpts.headers = headers;
  delete config.authKey;
  if (options.debugMode) {
    var cacheLogs = [];
    /*eslint no-console: "off"*/
    console.log = function() {
      var msg = util.format.apply(this, arguments);
      if (cacheLogs) {
        cacheLogs.push(msg);
      } else {
        process.sendData({
          type: 'console.log',
          message: msg
        });
      }
    };
    process.on('data', function(data) {
      if (cacheLogs && data && data.type == 'console.log' && data.status == 'ready') {
        var _cacheLogs = cacheLogs;
        cacheLogs = null;
        _cacheLogs.forEach(function(msg) {
          process.sendData({
            type: 'console.log',
            message: msg
          });
        });
      }
    });
  }

  var port, statsPort, resStatsPort, uiPort, rulesPort, resRulesPort, tunnelRulesPort, tunnelPort;
  var count = 0;
  var callbackHandler = function() {
    if (--count <= 0) {
      callback(null, {
        port: port,
        statsPort: statsPort,
        resStatsPort: resStatsPort,
        uiPort: uiPort,
        rulesPort: rulesPort,
        resRulesPort: resRulesPort,
        tunnelRulesPort: tunnelRulesPort,
        tunnelPort: tunnelPort
      });
    }
  };

  try {
    require.resolve(options.value);
  } catch(e) {
    return callbackHandler();
  }

  var initServers = function(_ctx) {
    ctx = _ctx || ctx;
    var execPlugin = require(options.value);
    var startServer = execPlugin.pluginServer || execPlugin.server || execPlugin;
    if (typeof startServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startServer(server, options);
        port = _port;
        callbackHandler();
      });
    }

    var startStatsServer = execPlugin.statsServer || execPlugin.reqStatsServer;
    if (typeof startStatsServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', handleStatsResponse);
        startStatsServer(server, options);
        statsPort = _port;
        callbackHandler();
      });
    }

    var startResStatsServer = execPlugin.resStatsServer;
    if (typeof startResStatsServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', handleStatsResponse);
        startResStatsServer(server, options);
        resStatsPort = _port;
        callbackHandler();
      });
    }

    var startUIServer = execPlugin.uiServer || execPlugin.innerServer || execPlugin.internalServer;
    if (typeof startUIServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', setContenxt);
        startUIServer(server, options);
        uiPort = _port;
        callbackHandler();
      });
    }

    var startRulesServer = execPlugin.pluginRulesServer || execPlugin.rulesServer || execPlugin.reqRulesServer;
    if (typeof startRulesServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startRulesServer(server, options);
        rulesPort = _port;
        callbackHandler();
      });
    }

    var startResRulesServer = execPlugin.resRulesServer;
    if (typeof startResRulesServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', function(req, res) {
          initReq(req, res);
          req.getReqSession = function(cb) {
            return getSession(req, cb, true);
          };
          req.unsafe_getSession = function(cb) {
            return getSession(req, cb);
          };
          req.unsafe_getFrames = function(cb) {
            return getFrames(req, cb);
          };
        });
        startResRulesServer(server, options);
        resRulesPort = _port;
        callbackHandler();
      });
    }

    var startTunnelRulesServer = execPlugin.pluginRulesServer || execPlugin.tunnelRulesServer;
    if (typeof startTunnelRulesServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startTunnelRulesServer(server, options);
        tunnelRulesPort = _port;
        callbackHandler();
      });
    }

    var startTunnelServer = execPlugin.pluginServer || execPlugin.tunnelServer || execPlugin.connectServer;
    if (typeof startTunnelServer == 'function') {
      ++count;
      getServer(function(server, _port) {
        server.on('request', addSessionApi);
        startTunnelServer(server, options);
        tunnelPort = _port;
        callbackHandler();
      });
    }

    if (!count) {
      callbackHandler();
    }
  };
  var initial = loadModule(path.join(options.value, 'initial.js'));
  if (typeof initial === 'function') {
    if (initial.length === 2) {
      ctx = initial(options, initServers);
      return ctx;
    }
    ctx = initial(options);
  }
  initServers();
};

<|endoftext|>


<commit_before>if (typeof jQuery === 'undefined') {
  throw new Error('The jQuery equal height extension requires jQuery!');
}

jQuery.fn.equalHeight = function() {

	var $ = jQuery;
	var that = this;

	var setHeights = function() {
		var elems = {};
		var cont = $(that);

		// Reset the elements heights
		cont.each(function() {
			$(this).height('');
		});

		// Create a mapping of elements and the max height for all elements at that top offset
		cont.each(function() {
			var t = $(this).offset().top;
			if (typeof elems[t] == "undefined") elems[t] = {maxHeight: 0, e: []};
			elems[t].e.push($(this));
			elems[t].maxHeight = Math.max($(this).outerHeight(), elems[t].maxHeight);
		});

		// Apply the max height to all elements in each offset class
		for (var t in elems) {
			var mh = elems[t].maxHeight;
			for (var i in elems[t].e) {
				var e = elems[t].e[i];
				var padding = e.outerHeight() - e.height();
				e.height(mh - padding);
			}
		}
	};

	setHeights();
	setTimeout(setHeights, 100); // Set heights after page elements have rendered, is there a more elegant way to do this?
	$(window).resize(setHeights);
};
<commit_msg>Set heights after images have loaded<commit_after>if (typeof jQuery === 'undefined') {
  throw new Error('The jQuery equal height extension requires jQuery!');
}

jQuery.fn.equalHeight = function() {

	var $ = jQuery;
	var that = this;

	var setHeights = function() {
		var elems = {};
		var cont = $(that);

		// Reset the elements heights
		cont.each(function() {
			$(this).height('');
		});

		// Create a mapping of elements and the max height for all elements at that top offset
		cont.each(function() {
			var t = $(this).offset().top;
			if (typeof elems[t] == "undefined") elems[t] = {maxHeight: 0, e: []};
			elems[t].e.push($(this));
			elems[t].maxHeight = Math.max($(this).outerHeight(), elems[t].maxHeight);
		});

		// Apply the max height to all elements in each offset class
		for (var t in elems) {
			var mh = elems[t].maxHeight;
			for (var i in elems[t].e) {
				var e = elems[t].e[i];
				var padding = e.outerHeight() - e.height();
				e.height(mh - padding);
			}
		}
	};

	setHeights();
	$(window).resize(setHeights);
	$(this).find('img').load(setHeights);
};
<|endoftext|>


<commit_before>
/**
 * Round-robin plugin.
 *
 * Provides a `send` method which will
 * write the `msg` to all connected peers.
 *
 * @param {Object} options
 * @api private
 */

module.exports = function(options){
  options = options || {};
  var fallback = options.fallback || function(){};

  return function(sock){

    /**
     * Bind callback to `sock`.
     */

    fallback = fallback.bind(sock);

    /**
     * Initialize counter.
     */

    var n = 0;

    /**
     * Sends `msg` to all connected peers.
     */

    sock.send = function(msg){
      var socks = this.socks
        , len = socks.length
        , sock = socks[n++ % len]
        , args = [];

      if (Array.isArray(msg)) {
        args = msg;
      } else {
        for (var i = 0; i < arguments.length; ++i) {
          args[i] = arguments[i];
        }
      }

      if (sock) {
        sock.write(this.pack(args));
      } else {
        fallback(msg);
      }
    };

  };
};<commit_msg>fix round-robin write to unwritable socket<commit_after>
/**
 * Round-robin plugin.
 *
 * Provides a `send` method which will
 * write the `msg` to all connected peers.
 *
 * @param {Object} options
 * @api private
 */

module.exports = function(options){
  options = options || {};
  var fallback = options.fallback || function(){};

  return function(sock){

    /**
     * Bind callback to `sock`.
     */

    fallback = fallback.bind(sock);

    /**
     * Initialize counter.
     */

    var n = 0;

    /**
     * Sends `msg` to all connected peers.
     */

    sock.send = function(msg){
      var socks = this.socks
        , len = socks.length
        , sock = socks[n++ % len]
        , args = [];

      if (Array.isArray(msg)) {
        args = msg;
      } else {
        for (var i = 0; i < arguments.length; ++i) {
          args[i] = arguments[i];
        }
      }

      if (sock && sock.writable) {
        sock.write(this.pack(args));
      } else {
        fallback(msg);
      }
    };

  };
};<|endoftext|>


<commit_before>console.log('Lyssna: An Amazon SNS trigger that logs the message pushed to the SNS topic.')

var _ = require('underscore')
var util = require('util')
var AwsLib = require('./lib/aws.js')
var HipchatLib = require('./lib/hipchat.js')
var NewrelicLib = require('./lib/newrelic.js')
var PagerdutyLib = require('./lib/pagerduty.js')
var SlackLib = require('./lib/slack.js')
var Promise = require('promise')
var appConfig = require('./config/app.json')

exports.handler = function (event, context) {
  var awsHelper = new AwsLib(context)

  var getRevisionData = function (snsMessage) {
    var deploymentId = snsMessage.deploymentId
    var awsRequest = awsHelper.getDeploymentDetails(deploymentId)

    return awsRequest.then(function (deployment) {
      return deployment.deploymentInfo.revision
    }).catch(function (err) {
      context.fail(util.format('S3 Bucket error: "%s"', err))
    })
  }

  var getRevisionNumber = function (revision, deploymentId) {
    switch (revision.revisionType) {
      case 'S3':
        return parseS3Key(revision.s3Location)
      case 'GitHub':
        return revision.gitHubLocation.commitId
      default:
        console.warn(util.format('No matching revisionType "%s" for deploymentId "%s"', revision.revisionType, deploymentId))
        return
    }
  }

  var getMetaTagsForS3Key = function (revision, deploymentId) {
    switch (revision.revisionType) {
      case 'S3':
        return awsHelper.getMetaTags(revision.s3Location)
      case 'GitHub':
        console.warn(util.format('Need to implement github api to get commit information for SHA.'))
        return new Promise()
      default:
        console.warn(util.format('No matching revisionType "%s" for deploymentId "%s"', revision.revisionType, deploymentId))
        return
    }
  }

  var parseS3Key = function (s3Location) {
    var re = /(\d+\.)?(\d+\.)?(\*|\d+)/
    var str = s3Location.key
    var m
    var revision

    if ((m = re.exec(str)) !== null) {
      if (m.index === re.lastIndex) {
        re.lastIndex++
      }
      revision = revision = m[0]
      return revision
    }
    console.warn(util.format('No revision found for s3Location: "%s"', s3Location.key))
    return
  }

  _.each(event.Records, function (record) {
    if (!record) {
      context.fail(util.format('No records in event: %s', event.type))
      return
    }

    if (!record.Sns) {
      context.fail(util.format('No Sns in record for event: %s', event.type))
      return
    }

    if (!record.Sns.Message) {
      context.fail(util.format('No Message in Sns record for event: %s', event.type))
      return
    }

    console.log("Sns", JSON.parse(record.Sns))
    console.log("Sns.Message", JSON.parse(record.Sns.Message))

    var sns = record.Sns
    var snsMessage = JSON.parse(sns.Message)
    var trigger = appConfig.snsEventTriggers[snsMessage.eventTriggerName]
    var deploymentId = snsMessage.deploymentId

    if (!trigger) {
      context.fail(util.format('Unsupported sns event trigger name: %s', snsMessage.eventTriggerName))
      return
    }

    var revisionPromise = getRevisionData(snsMessage)

    _.each(trigger, function (channel, key) {
      switch (key) {
        case 'hipchat':
          var hipchatConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([hipchatConfigPromise, revisionPromise]).then(function (values) {
            var hipchatApi = new HipchatLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              hipchatApi.sendNotification(snsMessage, revision, metaTags)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        case 'newrelic':
          var newrelicConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([newrelicConfigPromise, revisionPromise]).then(function (values) {
            var newrelicApi = new NewrelicLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              newrelicApi.recordDeploymentToNewrelic(sns, metaTags.user || '', revision)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        case 'pagerduty':
          var pagerdutyConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([pagerdutyConfigPromise, revisionPromise]).then(function (values) {
            var pagerdutyLib = new PagerdutyLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              pagerdutyLib.triggerPagerDutyAlert(sns, metaTags.user || '', revision)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        case 'slack':
          var slackConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([slackConfigPromise, revisionPromise]).then(function (values) {
            var slackLib = new SlackLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              slackLib.postMessage(snsMessage, metaTags.user || '', revision)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        default:
          context.fail(util.format('Unsupported notification channel: %s', key))
      }
    })
  })
}
<commit_msg>Fixed json parse.<commit_after>console.log('Lyssna: An Amazon SNS trigger that logs the message pushed to the SNS topic.')

var _ = require('underscore')
var util = require('util')
var AwsLib = require('./lib/aws.js')
var HipchatLib = require('./lib/hipchat.js')
var NewrelicLib = require('./lib/newrelic.js')
var PagerdutyLib = require('./lib/pagerduty.js')
var SlackLib = require('./lib/slack.js')
var Promise = require('promise')
var appConfig = require('./config/app.json')

exports.handler = function (event, context) {
  var awsHelper = new AwsLib(context)

  var getRevisionData = function (snsMessage) {
    var deploymentId = snsMessage.deploymentId
    var awsRequest = awsHelper.getDeploymentDetails(deploymentId)

    return awsRequest.then(function (deployment) {
      return deployment.deploymentInfo.revision
    }).catch(function (err) {
      context.fail(util.format('S3 Bucket error: "%s"', err))
    })
  }

  var getRevisionNumber = function (revision, deploymentId) {
    switch (revision.revisionType) {
      case 'S3':
        return parseS3Key(revision.s3Location)
      case 'GitHub':
        return revision.gitHubLocation.commitId
      default:
        console.warn(util.format('No matching revisionType "%s" for deploymentId "%s"', revision.revisionType, deploymentId))
        return
    }
  }

  var getMetaTagsForS3Key = function (revision, deploymentId) {
    switch (revision.revisionType) {
      case 'S3':
        return awsHelper.getMetaTags(revision.s3Location)
      case 'GitHub':
        console.warn(util.format('Need to implement github api to get commit information for SHA.'))
        return new Promise()
      default:
        console.warn(util.format('No matching revisionType "%s" for deploymentId "%s"', revision.revisionType, deploymentId))
        return
    }
  }

  var parseS3Key = function (s3Location) {
    var re = /(\d+\.)?(\d+\.)?(\*|\d+)/
    var str = s3Location.key
    var m
    var revision

    if ((m = re.exec(str)) !== null) {
      if (m.index === re.lastIndex) {
        re.lastIndex++
      }
      revision = revision = m[0]
      return revision
    }
    console.warn(util.format('No revision found for s3Location: "%s"', s3Location.key))
    return
  }

  _.each(event.Records, function (record) {
    if (!record) {
      context.fail(util.format('No records in event: %s', event.type))
      return
    }

    if (!record.Sns) {
      context.fail(util.format('No Sns in record for event: %s', event.type))
      return
    }

    if (!record.Sns.Message) {
      context.fail(util.format('No Message in Sns record for event: %s', event.type))
      return
    }

    console.log("Sns", record.Sns)
    console.log("Sns.Message", JSON.parse(record.Sns.Message))

    var sns = record.Sns
    var snsMessage = JSON.parse(sns.Message)
    var trigger = appConfig.snsEventTriggers[snsMessage.eventTriggerName]
    var deploymentId = snsMessage.deploymentId

    if (!trigger) {
      context.fail(util.format('Unsupported sns event trigger name: %s', snsMessage.eventTriggerName))
      return
    }

    var revisionPromise = getRevisionData(snsMessage)

    _.each(trigger, function (channel, key) {
      switch (key) {
        case 'hipchat':
          var hipchatConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([hipchatConfigPromise, revisionPromise]).then(function (values) {
            var hipchatApi = new HipchatLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              hipchatApi.sendNotification(snsMessage, revision, metaTags)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        case 'newrelic':
          var newrelicConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([newrelicConfigPromise, revisionPromise]).then(function (values) {
            var newrelicApi = new NewrelicLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              newrelicApi.recordDeploymentToNewrelic(sns, metaTags.user || '', revision)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        case 'pagerduty':
          var pagerdutyConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([pagerdutyConfigPromise, revisionPromise]).then(function (values) {
            var pagerdutyLib = new PagerdutyLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              pagerdutyLib.triggerPagerDutyAlert(sns, metaTags.user || '', revision)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        case 'slack':
          var slackConfigPromise = awsHelper.getNotificationConfig(channel.s3)

          Promise.all([slackConfigPromise, revisionPromise]).then(function (values) {
            var slackLib = new SlackLib(values[0], context)
            var revision = getRevisionNumber(values[1], deploymentId)
            var metaTagsPromise = getMetaTagsForS3Key(values[1], deploymentId)

            metaTagsPromise.then(function (metaTags) {
              // Function call below can be a callback.
              slackLib.postMessage(snsMessage, metaTags.user || '', revision)
            }).catch(function (err) {
              context.fail(util.format('Error: "%s"', err))
            })
          }).catch(function (err) {
            context.fail(util.format('Error: "%s"', err))
          })
          break
        default:
          context.fail(util.format('Unsupported notification channel: %s', key))
      }
    })
  })
}
<|endoftext|>


<commit_before>(function () {

var Selection = {

  events : {
    'flotr:select' : 'zoom',
    'flotr:click' : 'reset'
  },

  zoom : function (leader, selection) {

    var data = leader.getData(),
      x1 = selection.x1,
      x2 = selection.x2,
      o;

    o = {
      xaxis : {
        min : x1,
        max : x2
      }
    };

    _.each(this.followers, function (follower) {
      follower.draw(null, o);
    }, this);
  },

  reset : function () {

    var o = {
      xaxis : {
        min : null,
        max : null
      }
    };

    _.each(this.followers, function (follower) {
      follower.draw(null, o);
    }, this);
  }
};

Humble.Vis.Action = {};
Humble.Vis.Action.Selection = Selection;

})();
<commit_msg>Added hack for resetting when functional datat type.<commit_after>(function () {

var Selection = {

  events : {
    'flotr:select' : 'zoom',
    'flotr:click' : 'reset'
  },

  zoom : function (leader, selection) {

    var data = leader.getData(),
      x1 = selection.x1,
      x2 = selection.x2,
      o;

    o = {
      xaxis : {
        min : x1,
        max : x2
      }
    };

    _.each(this.followers, function (follower) {
      follower.draw(null, o);
    }, this);
  },

  reset : function () {

    var
      leader = this.leaders[0], // Hack
      min = leader.flotr.axes.x.min,
      max = leader.flotr.axes.x.max,
      o = {
        xaxis : {
          min : min,
          max : max
        }
      };

    _.each(this.followers, function (follower) {
      follower.draw(null, o);
    }, this);
  }
};

Humble.Vis.Action = {};
Humble.Vis.Action.Selection = Selection;

})();
<|endoftext|>


<commit_before>// Copyright 2015-2017, University of Colorado Boulder

/**
 * Representation of the back of a HeaterCoolerNode.  It is independent from the front of the HeaterCoolerNode so that
 * one can easily layer objects between the heater/cooler front and back.  The back contains the elliptical hole and the
 * fire and ice images.
 *
 * @author Siddhartha Chinthapally (Actual Concepts) on 20-11-2014.
 * @author Jesse Greenberg
 */

define( require => {
  'use strict';

  // modules
  const Color = require( 'SCENERY/util/Color' );
  const Image = require( 'SCENERY/nodes/Image' );
  const LinearGradient = require( 'SCENERY/util/LinearGradient' );
  const Node = require( 'SCENERY/nodes/Node' );
  const Path = require( 'SCENERY/nodes/Path' );
  const Range = require( 'DOT/Range' );
  const NumberProperty = require( 'AXON/NumberProperty' );
  const sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  const Shape = require( 'KITE/Shape' );
  const Tandem = require( 'TANDEM/Tandem' );
  const Vector2 = require( 'DOT/Vector2' );

  //images
  const fireImage = require( 'image!SCENERY_PHET/flame.png' );
  const iceImage = require( 'image!SCENERY_PHET/ice-cube-stack.png' );

  // constants
  // Scale factor that determines the height of the heater opening.  Can be made an optional parameter if necessary.
  const OPENING_HEIGHT_SCALE = 0.1;
  const DEFAULT_HEAT_COOL_AMOUNT_PROPERTY = new NumberProperty( 0, {
    range: new Range( -1, 1 ) // +1 for max heating, -1 for max cooling
  } );

  class HeaterCoolerBack extends Node {

    /**
     * Constructor for a HeaterCoolerBack.
     *
     * @param {Object} [options] that can be passed on to the underlying node
     * @constructor
     */
    constructor( options ) {
      super();
      Tandem.indicateUninstrumentedCode();

      options = _.extend( {
        baseColor: new Color( 159, 182, 205 ), //  base color used for the stove body
        width: 120, // in screen coords, much of the rest of the size of the stove derives from this value
        snapToZero: true, // controls whether the slider will snap to the off.
        heatCoolAmountProperty: DEFAULT_HEAT_COOL_AMOUNT_PROPERTY, // Property set through interaction with slider.
        heatEnabled: true, // Can this node heat the environment?
        coolEnabled: true // Can this node cool the environment?
      }, options );

      // Dimensions for the rest of the stove, dependent on the desired stove width.
      let burnerOpeningHeight = options.width * OPENING_HEIGHT_SCALE;

      // Create the inside bowl of the burner, which is an ellipse.
      let burnerInteriorShape = new Shape()
        .ellipse( options.width / 2, burnerOpeningHeight / 4, options.width / 2, burnerOpeningHeight / 2, 0, 0, Math.PI, false );
      let burnerInterior = new Path( burnerInteriorShape, {
        stroke: 'black',
        fill: new LinearGradient( 0, 0, options.width, 0 )
          .addColorStop( 0, options.baseColor.darkerColor( 0.5 ) )
          .addColorStop( 1, options.baseColor.brighterColor( 0.5 ) )
      } );

      let fireNode = new Image( fireImage, { centerX: burnerInterior.centerX, top: burnerInterior.bottom } );
      let iceNode = new Image( iceImage, { centerX: burnerInterior.centerX, top: burnerInterior.bottom } );
      options.heatCoolAmountProperty.link( function( heat ) {

        // max heating and cooling is limited to +/- 1
        assert && assert( Math.abs( heat ) <= 1 );

        if ( heat > 0 ) {
          fireNode.setTranslation( ( burnerInterior.width - fireNode.width ) / 2, -heat * fireImage.height * 0.85 );
        }
        else if ( heat < 0 ) {
          iceNode.setTranslation( ( burnerInterior.width - iceNode.width ) / 2, heat * iceImage.height * 0.85 );
        }
        iceNode.setVisible( heat < 0 );
        fireNode.setVisible( heat > 0 );
      } );

      this.addChild( burnerInterior );
      this.addChild( fireNode );
      this.addChild( iceNode );

      this.mutate( options );
    }

    /**
     * Convenience function that returns the correct position for the front of the HeaterCoolerNode.  Specifically,
     * this returns the left center of the burner opening.
     *
     * @returns {Vector2}
     * @public
     */
    getHeaterFrontPosition() {
      return new Vector2( this.leftTop.x, this.leftTop.y + this.width * OPENING_HEIGHT_SCALE / 2 );
    }
  }

  // Shape of heater front depends on this value.
  // @static
  HeaterCoolerBack.OPENING_HEIGHT_SCALE = OPENING_HEIGHT_SCALE;

  return sceneryPhet.register( 'HeaterCoolerBack', HeaterCoolerBack );
} );

<commit_msg>Dispose function added to HeaterCoolerBack.js to unlink relevant properties.  https://github.com/phetsims/scenery-phet/issues/423.<commit_after>// Copyright 2015-2017, University of Colorado Boulder

/**
 * Representation of the back of a HeaterCoolerNode.  It is independent from the front of the HeaterCoolerNode so that
 * one can easily layer objects between the heater/cooler front and back.  The back contains the elliptical hole and the
 * fire and ice images.
 *
 * @author Siddhartha Chinthapally (Actual Concepts) on 20-11-2014.
 * @author Jesse Greenberg
 */

define( require => {
  'use strict';

  // modules
  const Color = require( 'SCENERY/util/Color' );
  const Image = require( 'SCENERY/nodes/Image' );
  const LinearGradient = require( 'SCENERY/util/LinearGradient' );
  const Node = require( 'SCENERY/nodes/Node' );
  const Path = require( 'SCENERY/nodes/Path' );
  const Range = require( 'DOT/Range' );
  const NumberProperty = require( 'AXON/NumberProperty' );
  const sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  const Shape = require( 'KITE/Shape' );
  const Tandem = require( 'TANDEM/Tandem' );
  const Vector2 = require( 'DOT/Vector2' );

  //images
  const fireImage = require( 'image!SCENERY_PHET/flame.png' );
  const iceImage = require( 'image!SCENERY_PHET/ice-cube-stack.png' );

  // constants
  // Scale factor that determines the height of the heater opening.  Can be made an optional parameter if necessary.
  const OPENING_HEIGHT_SCALE = 0.1;
  const DEFAULT_HEAT_COOL_AMOUNT_PROPERTY = new NumberProperty( 0, {
    range: new Range( -1, 1 ) // +1 for max heating, -1 for max cooling
  } );

  class HeaterCoolerBack extends Node {

    /**
     * Constructor for a HeaterCoolerBack.
     *
     * @param {Object} [options] that can be passed on to the underlying node
     * @constructor
     */
    constructor( options ) {
      super();
      Tandem.indicateUninstrumentedCode();

      options = _.extend( {
        baseColor: new Color( 159, 182, 205 ), //  base color used for the stove body
        width: 120, // in screen coords, much of the rest of the size of the stove derives from this value
        snapToZero: true, // controls whether the slider will snap to the off.
        heatCoolAmountProperty: DEFAULT_HEAT_COOL_AMOUNT_PROPERTY, // Property set through interaction with slider.
        heatEnabled: true, // Can this node heat the environment?
        coolEnabled: true // Can this node cool the environment?
      }, options );

      // Dimensions for the rest of the stove, dependent on the desired stove width.
      let burnerOpeningHeight = options.width * OPENING_HEIGHT_SCALE;

      // Create the inside bowl of the burner, which is an ellipse.
      let burnerInteriorShape = new Shape()
        .ellipse( options.width / 2, burnerOpeningHeight / 4, options.width / 2, burnerOpeningHeight / 2, 0, 0, Math.PI, false );
      let burnerInterior = new Path( burnerInteriorShape, {
        stroke: 'black',
        fill: new LinearGradient( 0, 0, options.width, 0 )
          .addColorStop( 0, options.baseColor.darkerColor( 0.5 ) )
          .addColorStop( 1, options.baseColor.brighterColor( 0.5 ) )
      } );

      let fireNode = new Image( fireImage, { centerX: burnerInterior.centerX, top: burnerInterior.bottom } );
      let iceNode = new Image( iceImage, { centerX: burnerInterior.centerX, top: burnerInterior.bottom } );
      options.heatCoolAmountProperty.link( function( heat ) {

        // max heating and cooling is limited to +/- 1
        assert && assert( Math.abs( heat ) <= 1 );

        if ( heat > 0 ) {
          fireNode.setTranslation( ( burnerInterior.width - fireNode.width ) / 2, -heat * fireImage.height * 0.85 );
        }
        else if ( heat < 0 ) {
          iceNode.setTranslation( ( burnerInterior.width - iceNode.width ) / 2, heat * iceImage.height * 0.85 );
        }
        iceNode.setVisible( heat < 0 );
        fireNode.setVisible( heat > 0 );
      } );

      this.addChild( burnerInterior );
      this.addChild( fireNode );
      this.addChild( iceNode );

      this.mutate( options );

      // @public Dispose function used for unlinking relevant properties
      this.disposeHeaterCoolerBack = function() {
        options.heatCoolAmountProperty.unlinkAll();
      }
    }

    /**
     * Convenience function that returns the correct position for the front of the HeaterCoolerNode.  Specifically,
     * this returns the left center of the burner opening.
     *
     * @returns {Vector2}
     * @public
     */
    getHeaterFrontPosition() {
      return new Vector2( this.leftTop.x, this.leftTop.y + this.width * OPENING_HEIGHT_SCALE / 2 );
    }

    /**
     * @public
     */
    dispose() {
      this.disposeHeaterCoolerBack();
    }
  }

  // Shape of heater front depends on this value.
  // @static
  HeaterCoolerBack.OPENING_HEIGHT_SCALE = OPENING_HEIGHT_SCALE;

  return sceneryPhet.register( 'HeaterCoolerBack', HeaterCoolerBack );
} );

<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Button for initiating game levels and for depicting the progress made on each level.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Imports
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ProgressIndicator = require( 'VEGAS/ProgressIndicator' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );

  /**
   * @param {Image} icon Scenery node that appears on the button, scaled to fit
   * @param {number} numStars Number of stars to show in the progress indicator at the bottom of the button
   * @param {function} fireFunction Called when the button fires
   * @param {Property<number>} scoreProperty
   * @param {number} perfectScore
   * @param {*} options
   * @constructor
   */
  function LevelStartButton( icon, numStars, fireFunction, scoreProperty, perfectScore, options ) {

    options = _.extend( {
      buttonWidth: 150,
      buttonHeight: 150,
      backgroundColor: 'rgb( 242, 255, 204 )',
      highlightedBackgroundColor: 'rgb( 250, 255, 230 )',
      shadowColor: 'black',
      cornerRadius: 10
    }, options );
    var shadowOffset = 0.026 * options.buttonWidth;

    Node.call( this ); // Call super constructor.

    // Add the drop shadow.
    this.addChild( new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
        fill: options.shadowColor,
        top: shadowOffset,
        left: shadowOffset
      }
    ) );

    // Add the button outline, which is also the parent node for everything else that is on the button.
    var buttonOutline = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
      stroke: 'black',
      lineWidth: 1,
      fill: options.backgroundColor,
      cursor: 'pointer'
    } );
    this.addChild( buttonOutline );

    // Icon, scaled to fit.
    var iconScaleFactor = Math.min( options.buttonHeight * 0.65 / icon.height, options.buttonWidth * 0.85 / icon.width );
    icon.scale( iconScaleFactor );
    icon.centerX = options.buttonWidth / 2;
    icon.centerY = options.buttonHeight * 0.4;
    icon.pickable = false;
    buttonOutline.addChild( icon );

    // Progress indicator (stars), scaled to fit
    var progressIndicatorBackground = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight * 0.2, options.cornerRadius, options.cornerRadius, {
      fill: 'white',
      stroke: 'black',
      lineWidth: 1,
      pickable: false,
      bottom: options.buttonHeight
    } );
    progressIndicatorBackground.addChild( new ProgressIndicator( numStars, options.buttonWidth / 6, scoreProperty, perfectScore, {
      centerX: progressIndicatorBackground.centerX,
      centerY: progressIndicatorBackground.height / 2,
      pickable: false
    } ) );
    buttonOutline.addChild( progressIndicatorBackground );

    //TODO This behavior was borrowed from sun.PushButton, because sun.RectanglePushButton doesn't support the pseudo-3D behavior of this button.
    // Add the listener to update the appearance and handle a click.
    var update = function( state ) {
      if ( state === 'up' ) {
        buttonOutline.fill = options.backgroundColor;
        buttonOutline.top = 0;
        buttonOutline.left = 0;
      }
      else if ( state === 'over' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
      }
      else if ( state === 'down' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
        buttonOutline.top = shadowOffset;
        buttonOutline.left = shadowOffset;
      }
    };
    buttonOutline.addInputListener( new ButtonListener( {
      up: function() { update( 'up' ); },
      over: function() { update( 'over' ); },
      down: function() { update( 'down' ); },
      out: function() { update( 'up' ); }, // 'out' state looks the same as 'up'
      fire: fireFunction
    } ) );

    this.mutate( options );
  }

  return inherit( Node, LevelStartButton );
} );
<commit_msg>Fixed a bug that was causing problems when using non-default numbers of stars on the progress indicator.<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Button for initiating game levels and for depicting the progress made on each level.
 *
 * @author John Blanco
 */
define( function( require ) {
  'use strict';

  // Imports
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ProgressIndicator = require( 'VEGAS/ProgressIndicator' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );

  /**
   * @param {Image} icon Scenery node that appears on the button, scaled to fit
   * @param {number} numStars Number of stars to show in the progress indicator at the bottom of the button
   * @param {function} fireFunction Called when the button fires
   * @param {Property<number>} scoreProperty
   * @param {number} perfectScore
   * @param {*} options
   * @constructor
   */
  function LevelStartButton( icon, numStars, fireFunction, scoreProperty, perfectScore, options ) {

    options = _.extend( {
      buttonWidth: 150,
      buttonHeight: 150,
      backgroundColor: 'rgb( 242, 255, 204 )',
      highlightedBackgroundColor: 'rgb( 250, 255, 230 )',
      shadowColor: 'black',
      cornerRadius: 10
    }, options );
    var shadowOffset = 0.026 * options.buttonWidth;

    Node.call( this ); // Call super constructor.

    // Add the drop shadow.
    this.addChild( new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
        fill: options.shadowColor,
        top: shadowOffset,
        left: shadowOffset
      }
    ) );

    // Add the button outline, which is also the parent node for everything else that is on the button.
    var buttonOutline = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight, options.cornerRadius, options.cornerRadius, {
      stroke: 'black',
      lineWidth: 1,
      fill: options.backgroundColor,
      cursor: 'pointer'
    } );
    this.addChild( buttonOutline );

    // Icon, scaled to fit.
    var iconScaleFactor = Math.min( options.buttonHeight * 0.65 / icon.height, options.buttonWidth * 0.85 / icon.width );
    icon.scale( iconScaleFactor );
    icon.centerX = options.buttonWidth / 2;
    icon.centerY = options.buttonHeight * 0.4;
    icon.pickable = false;
    buttonOutline.addChild( icon );

    // Progress indicator (stars), scaled to fit
    var progressIndicatorBackground = new Rectangle( 0, 0, options.buttonWidth, options.buttonHeight * 0.2, options.cornerRadius, options.cornerRadius, {
      fill: 'white',
      stroke: 'black',
      lineWidth: 1,
      pickable: false,
      bottom: options.buttonHeight
    } );
    progressIndicatorBackground.addChild( new ProgressIndicator( numStars, options.buttonWidth / ( numStars + 1 ), scoreProperty, perfectScore, {
      centerX: progressIndicatorBackground.centerX,
      centerY: progressIndicatorBackground.height / 2,
      pickable: false
    } ) );
    buttonOutline.addChild( progressIndicatorBackground );

    //TODO This behavior was borrowed from sun.PushButton, because sun.RectanglePushButton doesn't support the pseudo-3D behavior of this button.
    // Add the listener to update the appearance and handle a click.
    var update = function( state ) {
      if ( state === 'up' ) {
        buttonOutline.fill = options.backgroundColor;
        buttonOutline.top = 0;
        buttonOutline.left = 0;
      }
      else if ( state === 'over' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
      }
      else if ( state === 'down' ) {
        buttonOutline.fill = options.highlightedBackgroundColor;
        buttonOutline.top = shadowOffset;
        buttonOutline.left = shadowOffset;
      }
    };
    buttonOutline.addInputListener( new ButtonListener( {
      up: function() { update( 'up' ); },
      over: function() { update( 'over' ); },
      down: function() { update( 'down' ); },
      out: function() { update( 'up' ); }, // 'out' state looks the same as 'up'
      fire: fireFunction
    } ) );

    this.mutate( options );
  }

  return inherit( Node, LevelStartButton );
} );
<|endoftext|>


<commit_before>// Copyright 2002-2014, University of Colorado Boulder

/**
 * This class aims to have the same interface as ArrowNode and support the same API but to do it by transforming shapes where
 * possible instead of creating new shapes, in order to improve performance, see https://github.com/phetsims/scenery-phet/issues/28
 *
 * TODO: Add support for "collar" stroke and shrunken arrowheads.
 *
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // Imports
  var inherit = require( 'PHET_CORE/inherit' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Matrix3 = require( 'DOT/Matrix3' );
  var Shape = require( 'KITE/Shape' );
  var Vector2 = require( 'DOT/Vector2' );
  var ArrowNode = require( 'SCENERY_PHET/ArrowNode' );
  var ArrowShape = require( 'SCENERY_PHET/ArrowShape' );

  /*
   * @param {number} tailX
   * @param {number} tailY
   * @param {number} tipX
   * @param {number} tipY
   * @param {Object} options
   * @constructor
   */
  function MutableArrowNode( tailX, tailY, tipX, tipY, options ) {

    // default options
    options = _.extend( {
      headHeight: 10,
      headWidth: 10,
      tailWidth: 5,
      doubleHead: false, // true puts heads on both ends of the arrow, false puts a head at the tip
      fill: 'black',
      stroke: 'black',
      lineWidth: 1
    }, options );

    // things you're likely to mess up, add more as needed
    assert && assert( options.headWidth > options.tailWidth );

    //The tail of the Arrow is a
    Node.call( this, options );

    //Intermediate parent for setting the rotation of the node
    this.parent = new Node();

    //Rectangle for body and stripes along the sides for stroke
    this.body = new Node( {
      children: [
        new Rectangle( 0, 0, options.tailWidth, 1, {fill: options.fill} ),
        new Rectangle( -options.lineWidth / 2, 0, options.lineWidth, 1, {fill: options.stroke} ),
        new Rectangle( options.tailWidth - options.lineWidth / 2, 0, options.lineWidth, 1, {fill: options.stroke} )
      ]} );

    //Head shape.  Don't close so the stroke can be used for the angled parts, but not the flat part (the "collar").
    var headShape = new Shape().moveTo( -options.headWidth / 2, 0 ).lineTo( 0, options.headHeight ).lineTo( options.headWidth / 2, 0 );
    this.head = new Path( headShape, {fill: options.fill, stroke: options.stroke, lineWidth: options.lineWidth} );

    this.tailEdgeStroke = new Rectangle( 0, -options.lineWidth / 2, options.tailWidth, options.lineWidth, {fill: 'black'} );

    this.parent.addChild( this.head );
    this.parent.addChild( this.body );
    this.parent.addChild( this.tailEdgeStroke );

    this.addChild( this.parent );
    this.options = options;

    //Fall back to ArrowNode when arrowhead is small
    this.arrowNode = new ArrowNode( 0, 0, 0, 0, options );
    this.arrowNode.visible = false;
    this.addChild( this.arrowNode );

    this.setTailAndTip( tailX, tailY, tipX, tipY );
    this.optimized = true;
  }

  return inherit( Node, MutableArrowNode, {
    getMatrix: function( x, y, angle, sx, sy, originX, originY ) {

      //Translate to the desired location
      var matrix = Matrix3.translation( x, y );

      //Rotation and translation can happen in any order
      matrix = matrix.multiplyMatrix( Matrix3.rotation2( angle ) );

      //Update scale if not 1
      if ( originX !== 1 || originY !== 1 ) {
        matrix = matrix.multiplyMatrix( Matrix3.scaling( sx, sy ) );
      }

      //Update origin if non-zero
      if ( originX !== 0 || originY !== 0 ) {

        //Think of it as a multiplying the Vector2 to the right, so this step happens first actually.  Use it to center the registration point
        matrix = matrix.multiplyMatrix( Matrix3.translation( -originX, -originY ) );
      }

      return matrix;
    },
    setTailAndTip: function( tailX, tailY, tipX, tipY ) {
      if ( this.tailX !== tailX || this.tailY !== tailY || this.tipX !== tipX || this.tipY !== tipY ) {
        var dx = tipX - tailX;
        var dy = tipY - tailY;

        var bodyDistance = Math.sqrt( dx * dx + dy * dy ) - this.options.headHeight;

        var willBeOptimized = bodyDistance > this.options.headHeight;

        if ( willBeOptimized ) {

          if ( !this.optimized ) {
            this.body.visible = true;
            this.head.visible = true;
            this.tailEdgeStroke.visible = true;
            this.arrowNode.visible = false;
          }

          var angle = Math.atan2( dy, dx );

          var bodyTipX = bodyDistance * Math.cos( angle ) + tailX;
          var bodyTipY = bodyDistance * Math.sin( angle ) + tailY;

          //Overlap a bit so it looks like a solid piece
          var overlap = 2E-1;

          this.body.setMatrix( this.getMatrix( tailX, tailY, angle - Math.PI / 2, 1, bodyDistance + overlap, this.options.tailWidth / 2, 0 ) );
          this.head.setMatrix( this.getMatrix( bodyTipX, bodyTipY, angle - Math.PI / 2, 1, 1, 0, 0 ) );
          this.tailEdgeStroke.setMatrix( this.getMatrix( tailX, tailY, angle - Math.PI / 2, 1, 1, this.options.tailWidth / 2, 0 ) );
        }
        else {
          if ( this.optimized ) {
            this.body.visible = false;
            this.head.visible = false;
            this.tailEdgeStroke.visible = false;
            this.arrowNode.visible = true;
          }
          this.arrowNode.setShape( new ArrowShape( tailX, tailY, tipX, tipY, this.options ) );
        }

        this.optimized = willBeOptimized;
        this.tailX = tailX;
        this.tailY = tailY;
        this.tipX = tipX;
        this.tipY = tipY;
      }
    },
    setShape: function() {
      console.log( 'set shape called on MutableArrowNode.' );
    }
  } );
} );
<commit_msg>Fixed: small arrows are shown<commit_after>// Copyright 2002-2014, University of Colorado Boulder

/**
 * This class aims to have the same interface as ArrowNode and support the same API but to do it by transforming shapes where
 * possible instead of creating new shapes, in order to improve performance, see https://github.com/phetsims/scenery-phet/issues/28
 *
 * TODO: Add support for "collar" stroke and shrunken arrowheads.
 *
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // Imports
  var inherit = require( 'PHET_CORE/inherit' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Matrix3 = require( 'DOT/Matrix3' );
  var Shape = require( 'KITE/Shape' );
  var Vector2 = require( 'DOT/Vector2' );
  var ArrowNode = require( 'SCENERY_PHET/ArrowNode' );
  var ArrowShape = require( 'SCENERY_PHET/ArrowShape' );

  /*
   * @param {number} tailX
   * @param {number} tailY
   * @param {number} tipX
   * @param {number} tipY
   * @param {Object} options
   * @constructor
   */
  function MutableArrowNode( tailX, tailY, tipX, tipY, options ) {

    // default options
    options = _.extend( {
      headHeight: 10,
      headWidth: 10,
      tailWidth: 5,
      doubleHead: false, // true puts heads on both ends of the arrow, false puts a head at the tip
      fill: 'black',
      stroke: 'black',
      lineWidth: 1
    }, options );

    // things you're likely to mess up, add more as needed
    assert && assert( options.headWidth > options.tailWidth );

    //The tail of the Arrow is a
    Node.call( this, options );

    //Intermediate parent for setting the rotation of the node
    this.parent = new Node();

    //Rectangle for body and stripes along the sides for stroke
    this.body = new Node( {
      children: [
        new Rectangle( 0, 0, options.tailWidth, 1, {fill: options.fill} ),
        new Rectangle( -options.lineWidth / 2, 0, options.lineWidth, 1, {fill: options.stroke} ),
        new Rectangle( options.tailWidth - options.lineWidth / 2, 0, options.lineWidth, 1, {fill: options.stroke} )
      ]} );

    //Head shape.  Don't close so the stroke can be used for the angled parts, but not the flat part (the "collar").
    var headShape = new Shape().moveTo( -options.headWidth / 2, 0 ).lineTo( 0, options.headHeight ).lineTo( options.headWidth / 2, 0 );
    this.head = new Path( headShape, {fill: options.fill, stroke: options.stroke, lineWidth: options.lineWidth} );

    this.tailEdgeStroke = new Rectangle( 0, -options.lineWidth / 2, options.tailWidth, options.lineWidth, {fill: 'black'} );

    this.parent.addChild( this.head );
    this.parent.addChild( this.body );
    this.parent.addChild( this.tailEdgeStroke );

    this.addChild( this.parent );
    this.options = options;

    //Fall back to ArrowNode when arrowhead is small
    this.arrowNode = new ArrowNode( 0, 0, 0, 0, options );
    this.arrowNode.visible = false;
    this.addChild( this.arrowNode );

    this.optimized = true;
    this.setTailAndTip( tailX, tailY, tipX, tipY );
  }

  return inherit( Node, MutableArrowNode, {
    getMatrix: function( x, y, angle, sx, sy, originX, originY ) {

      //Translate to the desired location
      var matrix = Matrix3.translation( x, y );

      //Rotation and translation can happen in any order
      matrix = matrix.multiplyMatrix( Matrix3.rotation2( angle ) );

      //Update scale if not 1
      if ( originX !== 1 || originY !== 1 ) {
        matrix = matrix.multiplyMatrix( Matrix3.scaling( sx, sy ) );
      }

      //Update origin if non-zero
      if ( originX !== 0 || originY !== 0 ) {

        //Think of it as a multiplying the Vector2 to the right, so this step happens first actually.  Use it to center the registration point
        matrix = matrix.multiplyMatrix( Matrix3.translation( -originX, -originY ) );
      }

      return matrix;
    },
    setTailAndTip: function( tailX, tailY, tipX, tipY ) {
      if ( this.tailX !== tailX || this.tailY !== tailY || this.tipX !== tipX || this.tipY !== tipY ) {
        var dx = tipX - tailX;
        var dy = tipY - tailY;

        var bodyDistance = Math.sqrt( dx * dx + dy * dy ) - this.options.headHeight;

        var willBeOptimized = bodyDistance > this.options.headHeight;

        if ( willBeOptimized ) {

          if ( !this.optimized ) {
            this.body.visible = true;
            this.head.visible = true;
            this.tailEdgeStroke.visible = true;
            this.arrowNode.visible = false;
          }

          var angle = Math.atan2( dy, dx );

          var bodyTipX = bodyDistance * Math.cos( angle ) + tailX;
          var bodyTipY = bodyDistance * Math.sin( angle ) + tailY;

          //Overlap a bit so it looks like a solid piece
          var overlap = 2E-1;

          this.body.setMatrix( this.getMatrix( tailX, tailY, angle - Math.PI / 2, 1, bodyDistance + overlap, this.options.tailWidth / 2, 0 ) );
          this.head.setMatrix( this.getMatrix( bodyTipX, bodyTipY, angle - Math.PI / 2, 1, 1, 0, 0 ) );
          this.tailEdgeStroke.setMatrix( this.getMatrix( tailX, tailY, angle - Math.PI / 2, 1, 1, this.options.tailWidth / 2, 0 ) );
        }
        else {
          if ( this.optimized ) {
            this.body.visible = false;
            this.head.visible = false;
            this.tailEdgeStroke.visible = false;
            this.arrowNode.visible = true;
          }
          this.arrowNode.setShape( new ArrowShape( tailX, tailY, tipX, tipY, this.options ) );
        }

        this.optimized = willBeOptimized;
        this.tailX = tailX;
        this.tailY = tailY;
        this.tipX = tipX;
        this.tipY = tipY;
      }
    },
    setShape: function() {
      console.log( 'set shape called on MutableArrowNode.' );
    }
  } );
} );
<|endoftext|>