<commit_before>
(function ($) {

    // Use underscore.js html escaper
    // http://underscorejs.org/#escape
    var _escape;
    (function () {
        var entityMap = {
            escape: {
                '&': '&',
                '<': '<',
                '>': '>',
                '"': '"',
                "'": '''
            }
        };

        var escapeKeys = [];
        for (var k in entityMap.escape) {
            if (entityMap.escape.hasOwnProperty(k)) {
                escapeKeys.push(k);
            }
        }
        escapeKeys = escapeKeys.join('');

        var entityRegexes = {
            escape: new RegExp('[' + escapeKeys + ']', 'g')
        };

        _escape = function (string) {
            if (string == null) { return ''; }
            return ('' + string).replace(entityRegexes['escape'], function (match) {
                return entityMap['escape'][match];
            });
        };

    })();

    var _methods = {

        destroy: function () {
            // TODO: destroy smarttext, and return element to original state
            this.off();
        },

        value: function (val) {
            if (typeof val === 'string') {
                return _methods.setValue.call(this, val);
            }
            return _methods.getValue.call(this);
        },

        getValue: function () {
            var text, inner;
            var textEl = this.clone();
            var children = textEl.children('div');
            textEl.find('br').replaceWith('\n');
            if (children) {
                children.detach();
                text = textEl.text();
                children.each(function (indx, child) {
                    inner = $(child).text();
                    if (inner === '\n') {
                        text += inner;
                    } else {
                        text += ('\n' + inner);
                    }
                });
            } else {
                text = textEl.text();
            }
            return text;
        },

        setValue: function (val) {
            this.html(_parseLinks(val));
            _placeholderUpdate.call(this);
            return this;
        }

    };

    var _parseLinks = function (text, options) {
        if (!text) { return ''; }
        options || (options = {});
        var linkMaps = [];
        var escaped = [];
        // use String.prototype.replace because its a crossbrowser way to iterate over links
        text.replace(_linkDetectionRegex, function () {
            var url = arguments[0];
            var index = arguments[arguments.length - 2];
            // Map the positions of link and non-link text
            linkMaps.push({
                linkTag: _makeLink(url, options.linkAttributes),
                linkStart: index,
                linkEnd: index + url.length
            });
            // don't actually change the text yet
            return url;
        });
        var lastI = 0;
        // Go though each link, and escape the text between it and the last link
        for (var i = 0; i < linkMaps.length; i++) {
            escaped.push(_escape(text.slice(lastI, linkMaps[i].linkStart)));
            escaped.push(linkMaps[i].linkTag);
            lastI = linkMaps[i].linkEnd;
        };
        // Escape everything after the last link
        if (lastI) {
            escaped.push(_escape(text.slice(lastI)));
        } else {
            // or if there where no links just escape the whole string
            escaped.push(_escape(text));
        }
        escaped = escaped.join('');
        if (options.newlines) { escaped = escaped.replace(/\n/g, '
'); }
        return escaped;
    };

    var _makeLink = function (url, attrs) {
        var linkStart = "" + url + "";
        var linkAttributes = '';
        for (var a in attrs) {
            if (attrs.hasOwnProperty(a) && a !== 'href' && a !== 'src') {
                linkAttributes += (a + "='" + attrs[a] + "' ");
            }
        }
        return linkStart + linkAttributes + linkEnd;
    };

    var _linkDetectionRegex = /(([a-z]+:\/\/)?(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?=(\)|\(|\<|\>|\s|$))/gi;

    // emulate placeholder text in contenteditable HTML
    // this was inspired by https://github.com/sprucemedia/jQuery.divPlaceholder.js
    var _placeholderUpdate = function () {
        $(this).each(function () {
            if (this.textContent) {
                this.setAttribute('data-div-placeholder-content', 'true');
            }
            else {
                this.removeAttribute('data-div-placeholder-content');
            }
        });
    };

    var defaultOptions = {
        linkAttributes: {
            title: 'Click outside of link to edit',
            contenteditable: 'false'
        },
        newlines: true
    };

    var _smarttext = function ($el, options) {
        $el.html(_parseLinks(_methods['value'].call($el), options));
        // This ensures (cross-browser) that if the user clicks the link before
        // the element gets focus we follow the hyperlink as usual
        $el.find('a').one('mousedown', function () {
            // we are relying on the mousedown event firing before focus
            if ($(this).attr('contenteditable') === 'false') {
                $el.data('follow-link', true);
            }
        });
        return $el;
    };

    $.fn.smarttext = function () {
        var args = Array.prototype.slice.apply(arguments);
        var isMethod = typeof args[0] === 'string';
        if (isMethod) {
            return _methods[args[0]].apply(this, args.slice(1));
        }
        var options = $.extend(true, {}, defaultOptions, args[0]);
        return this.each(function (indx, el) {

            var $el = $(el);
            $el.attr('contenteditable', true);

            _smarttext($el, options);
            _placeholderUpdate.call($el);

            $el.on('focus', function () {
                if ($el.data('follow-link')) { return; }
                $el.find('a').attr('contenteditable', true);
            }).on('blur', function () {
                _smarttext($el, options);
                $el.find('a').attr('contenteditable', options.linkAttributes.contenteditable);
                $el.data('follow-link', false);
            });

            $el.on('change keydown keypress input', _placeholderUpdate);
        });
    };

})(jQuery);
<commit_msg>Allow specifying if element is editable<commit_after>
(function ($) {

    // Use underscore.js html escaper
    // http://underscorejs.org/#escape
    var _escape;
    (function () {
        var entityMap = {
            escape: {
                '&': '&',
                '<': '<',
                '>': '>',
                '"': '"',
                "'": '''
            }
        };

        var escapeKeys = [];
        for (var k in entityMap.escape) {
            if (entityMap.escape.hasOwnProperty(k)) {
                escapeKeys.push(k);
            }
        }
        escapeKeys = escapeKeys.join('');

        var entityRegexes = {
            escape: new RegExp('[' + escapeKeys + ']', 'g')
        };

        _escape = function (string) {
            if (string == null) { return ''; }
            return ('' + string).replace(entityRegexes['escape'], function (match) {
                return entityMap['escape'][match];
            });
        };

    })();

    var _methods = {

        destroy: function () {
            // TODO: destroy smarttext, and return element to original state
            this.off();
        },

        value: function (val) {
            if (typeof val === 'string') {
                return _methods.setValue.call(this, val);
            }
            return _methods.getValue.call(this);
        },

        getValue: function () {
            var text, inner;
            var textEl = this.clone();
            var children = textEl.children('div');
            textEl.find('br').replaceWith('\n');
            if (children) {
                children.detach();
                text = textEl.text();
                children.each(function (indx, child) {
                    inner = $(child).text();
                    if (inner === '\n') {
                        text += inner;
                    } else {
                        text += ('\n' + inner);
                    }
                });
            } else {
                text = textEl.text();
            }
            return text;
        },

        setValue: function (val) {
            this.html(_parseLinks(val));
            _placeholderUpdate.call(this);
            return this;
        }

    };

    var _parseLinks = function (text, options) {
        if (!text) { return ''; }
        options || (options = {});
        var linkMaps = [];
        var escaped = [];
        // use String.prototype.replace because its a crossbrowser way to iterate over links
        text.replace(_linkDetectionRegex, function () {
            var url = arguments[0];
            var index = arguments[arguments.length - 2];
            // Map the positions of link and non-link text
            linkMaps.push({
                linkTag: _makeLink(url, options.linkAttributes),
                linkStart: index,
                linkEnd: index + url.length
            });
            // don't actually change the text yet
            return url;
        });
        var lastI = 0;
        // Go though each link, and escape the text between it and the last link
        for (var i = 0; i < linkMaps.length; i++) {
            escaped.push(_escape(text.slice(lastI, linkMaps[i].linkStart)));
            escaped.push(linkMaps[i].linkTag);
            lastI = linkMaps[i].linkEnd;
        };
        // Escape everything after the last link
        if (lastI) {
            escaped.push(_escape(text.slice(lastI)));
        } else {
            // or if there where no links just escape the whole string
            escaped.push(_escape(text));
        }
        escaped = escaped.join('');
        if (options.newlines) { escaped = escaped.replace(/\n/g, '
'); }
        return escaped;
    };

    var _makeLink = function (url, attrs) {
        var linkStart = "" + url + "";
        var linkAttributes = '';
        for (var a in attrs) {
            if (attrs.hasOwnProperty(a) && a !== 'href' && a !== 'src') {
                linkAttributes += (a + "='" + attrs[a] + "' ");
            }
        }
        return linkStart + linkAttributes + linkEnd;
    };

    var _linkDetectionRegex = /(([a-z]+:\/\/)?(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?=(\)|\(|\<|\>|\s|$))/gi;

    // emulate placeholder text in contenteditable HTML
    // this was inspired by https://github.com/sprucemedia/jQuery.divPlaceholder.js
    var _placeholderUpdate = function () {
        $(this).each(function () {
            if (this.textContent) {
                this.setAttribute('data-div-placeholder-content', 'true');
            }
            else {
                this.removeAttribute('data-div-placeholder-content');
            }
        });
    };

    var defaultOptions = {
        linkAttributes: {
            title: 'Click outside of link to edit',
            contenteditable: 'false'
        },
        newlines: true,
        editable: true
    };

    var _smarttext = function ($el, options) {
        $el.html(_parseLinks(_methods['value'].call($el), options));
        // This ensures (cross-browser) that if the user clicks the link before
        // the element gets focus we follow the hyperlink as usual
        $el.find('a').one('mousedown', function () {
            // we are relying on the mousedown event firing before focus
            if ($(this).attr('contenteditable') === 'false') {
                $el.data('follow-link', true);
            }
        });
        return $el;
    };

    $.fn.smarttext = function () {
        var args = Array.prototype.slice.apply(arguments);
        var isMethod = typeof args[0] === 'string';
        if (isMethod) {
            return _methods[args[0]].apply(this, args.slice(1));
        }
        var options = $.extend(true, {}, defaultOptions, args[0]);
        return this.each(function (indx, el) {

            var $el = $(el);
            $el.attr('contenteditable', options.editable);

            _smarttext($el, options);
            _placeholderUpdate.call($el);

            $el.on('focus', function () {
                if ($el.data('follow-link')) { return; }
                $el.find('a').attr('contenteditable', options.editable);
            }).on('blur', function () {
                _smarttext($el, options);
                $el.find('a').attr('contenteditable', options.linkAttributes.contenteditable);
                $el.data('follow-link', false);
            });

            $el.on('change keydown keypress input', _placeholderUpdate);
        });
    };

})(jQuery);
<|endoftext|>


<commit_before>var frisby = require('frisby');
var root = "http://localhost:3000";
var gamesPath = "/games"

frisby.create('Game starts when a user POSTs a new game')
  .post(root + gamesPath)
  .expectStatus(200)
  .expectHeaderContains('content-type', 'application/json')
  // .inspectJSON()
  .expectJSON({ status: "setup" })
  .expectJSONTypes({ id: Number })
.toss();

frisby.create('Setup ends when a user updates game with PUT')
  .put(root + gamesPath + "2")
  .expectStatus(200)
  .expectHeaderContains('content-type', 'application/json')
  .expectJSON({})
// .toss();
<commit_msg>Create skipped features to flesh out API<commit_after>var frisby = require('frisby');
var root = "http://localhost:3000";
var gamesPath = "/games";
var id = 2;

frisby.create('Game starts when a user POSTs a new game')
  .post(root + gamesPath)
  .expectStatus(200)
  .expectHeaderContains('content-type', 'application/json')
  // .inspectJSON()
  .expectJSON({ status: "setup" })
  .expectJSONTypes({ id: Number })
.toss();

frisby.create('Setup ends when a user updates game with PUT')
  .put(root + gamesPath + "/" + id)
  .expectStatus(200)
  .expectHeaderContains('content-type', 'application/json')
  .expectJSON({})
// .toss();

frisby.create('Poll for game state via GET /games/:id')
  .get(root + gamesPath + "/" + id)
  .expectStatus(200)
  .expectHeaderContains('content-type', 'application/json')
  .expectJSON({})
// .toss();

frisby.create('Fire a shot via POST /games/:id/shots')
  .post(root + gamesPath + '/' + id + '/shots')
  .expectStatus(200)
  .expectHeaderContains('content-type', 'application/json')
  .expectJSON({})
// .toss();
<|endoftext|>


<commit_before>/*
	A simple, lightweight jQuery plugin for creating sortable tables.
	https://github.com/kylefox/jquery-tablesort
	Version 0.0.10
*/

(function($) {
	$.tablesort = function ($table, settings) {
		var self = this;
		this.$table = $table;
		this.$thead = this.$table.find('thead');
		this.settings = $.extend({}, $.tablesort.defaults, settings);
		this.$sortCells = this.$thead.length > 0 ? this.$thead.find('th:not(.no-sort)') : this.$table.find('th:not(.no-sort)');
		this.$sortCells.on('click.tablesort', function() {
			self.sort($(this));
		});
		this.index = null;
		this.$th = null;
		this.direction = null;
	};

	$.tablesort.prototype = {

		sort: function(th, direction) {
			var start = new Date(),
				self = this,
				table = this.$table,
				//body = table.find('tbody').length > 0 ? table.find('tbody') : table,
				rows = this.$thead.length > 0 ? table.find('tbody tr') : table.find('tr').has('td'),
				cells = table.find('tr td:nth-of-type(' + (th.index() + 1) + ')'),
				sortBy = th.data().sortBy,
				sortedMap = [];

			var unsortedValues = cells.map(function(idx, cell) {
				if (sortBy)
					return (typeof sortBy === 'function') ? sortBy($(th), $(cell), self) : sortBy;
				return ($(this).data().sortValue != null ? $(this).data().sortValue : $(this).text());
			});
			if (unsortedValues.length === 0) return;

			//click on a different column
			if (this.index !== th.index()) {
				this.direction = 'asc';
				this.index = th.index();
			}
			else if (direction !== 'asc' && direction !== 'desc')
				this.direction = this.direction === 'asc' ? 'desc' : 'asc';
			else
				this.direction = direction;

			direction = this.direction == 'asc' ? 1 : -1;

			self.$table.trigger('tablesort:start', [self]);
			self.log("Sorting by " + this.index + ' ' + this.direction);

			// Try to force a browser redraw
			self.$table.css("display");
			// Run sorting asynchronously on a timeout to force browser redraw after
			// `tablesort:start` callback. Also avoids locking up the browser too much.
			setTimeout(function() {
				self.$sortCells.removeClass(self.settings.asc + ' ' + self.settings.desc);
				for (var i = 0, length = unsortedValues.length; i < length; i++)
				{
					sortedMap.push({
						index: i,
						cell: cells[i],
						row: rows[i],
						value: unsortedValues[i]
					});
				}

				sortedMap.sort(function(a, b) {
					if (a.value > b.value) {
						return 1 * direction;
					} else if (a.value < b.value) {
						return -1 * direction;
					} else {
						return 0;
					}
				});

				$.each(sortedMap, function(i, entry) {
					table.append(entry.row);
				});

				th.addClass(self.settings[self.direction]);

				self.log('Sort finished in ' + ((new Date()).getTime() - start.getTime()) + 'ms');
				self.$table.trigger('tablesort:complete', [self]);
				//Try to force a browser redraw
				self.$table.css("display");
			}, unsortedValues.length > 2000 ? 200 : 10);
		},

		log: function(msg) {
			if(($.tablesort.DEBUG || this.settings.debug) && console && console.log) {
				console.log('[tablesort] ' + msg);
			}
		},

		destroy: function() {
			this.$sortCells.off('click.tablesort');
			this.$table.data('tablesort', null);
			return null;
		}

	};

	$.tablesort.DEBUG = false;

	$.tablesort.defaults = {
		debug: $.tablesort.DEBUG,
		asc: 'sorted ascending',
		desc: 'sorted descending'
	};

	$.fn.tablesort = function(settings) {
		var table, sortable, previous;
		return this.each(function() {
			table = $(this);
			previous = table.data('tablesort');
			if(previous) {
				previous.destroy();
			}
			table.data('tablesort', new $.tablesort(table, settings));
		});
	};

})(window.Zepto || window.jQuery);
<commit_msg>Added support for passing a 'compare' function used to sort<commit_after>/*
	A simple, lightweight jQuery plugin for creating sortable tables.
	https://github.com/kylefox/jquery-tablesort
	Version 0.0.10
*/

(function($) {
	$.tablesort = function ($table, settings) {
		var self = this;
		this.$table = $table;
		this.$thead = this.$table.find('thead');
		this.settings = $.extend({}, $.tablesort.defaults, settings);
		this.$sortCells = this.$thead.length > 0 ? this.$thead.find('th:not(.no-sort)') : this.$table.find('th:not(.no-sort)');
		this.$sortCells.on('click.tablesort', function() {
			self.sort($(this));
		});
		this.index = null;
		this.$th = null;
		this.direction = null;
	};

	$.tablesort.prototype = {

		sort: function(th, direction) {
			var start = new Date(),
				self = this,
				table = this.$table,
				//body = table.find('tbody').length > 0 ? table.find('tbody') : table,
				rows = this.$thead.length > 0 ? table.find('tbody tr') : table.find('tr').has('td'),
				cells = table.find('tr td:nth-of-type(' + (th.index() + 1) + ')'),
				sortBy = th.data().sortBy,
				sortedMap = [];

			var unsortedValues = cells.map(function(idx, cell) {
				if (sortBy)
					return (typeof sortBy === 'function') ? sortBy($(th), $(cell), self) : sortBy;
				return ($(this).data().sortValue != null ? $(this).data().sortValue : $(this).text());
			});
			if (unsortedValues.length === 0) return;

			//click on a different column
			if (this.index !== th.index()) {
				this.direction = 'asc';
				this.index = th.index();
			}
			else if (direction !== 'asc' && direction !== 'desc')
				this.direction = this.direction === 'asc' ? 'desc' : 'asc';
			else
				this.direction = direction;

			direction = this.direction == 'asc' ? 1 : -1;

			self.$table.trigger('tablesort:start', [self]);
			self.log("Sorting by " + this.index + ' ' + this.direction);

			// Try to force a browser redraw
			self.$table.css("display");
			// Run sorting asynchronously on a timeout to force browser redraw after
			// `tablesort:start` callback. Also avoids locking up the browser too much.
			setTimeout(function() {
				self.$sortCells.removeClass(self.settings.asc + ' ' + self.settings.desc);
				for (var i = 0, length = unsortedValues.length; i < length; i++)
				{
					sortedMap.push({
						index: i,
						cell: cells[i],
						row: rows[i],
						value: unsortedValues[i]
					});
				}

				sortedMap.sort(function(a, b) {
					return self.settings.compare(a.value, b.value) * direction;
				});

				$.each(sortedMap, function(i, entry) {
					table.append(entry.row);
				});

				th.addClass(self.settings[self.direction]);

				self.log('Sort finished in ' + ((new Date()).getTime() - start.getTime()) + 'ms');
				self.$table.trigger('tablesort:complete', [self]);
				//Try to force a browser redraw
				self.$table.css("display");
			}, unsortedValues.length > 2000 ? 200 : 10);
		},

		log: function(msg) {
			if(($.tablesort.DEBUG || this.settings.debug) && console && console.log) {
				console.log('[tablesort] ' + msg);
			}
		},

		destroy: function() {
			this.$sortCells.off('click.tablesort');
			this.$table.data('tablesort', null);
			return null;
		}

	};

	$.tablesort.DEBUG = false;

	$.tablesort.defaults = {
		debug: $.tablesort.DEBUG,
		asc: 'sorted ascending',
		desc: 'sorted descending',
		compare: function(a, b) {
			if (a > b) {
				return 1;
			} else if (a < b) {
				return -1;
			} else {
				return 0;
			}
		}
	};

	$.fn.tablesort = function(settings) {
		var table, sortable, previous;
		return this.each(function() {
			table = $(this);
			previous = table.data('tablesort');
			if(previous) {
				previous.destroy();
			}
			table.data('tablesort', new $.tablesort(table, settings));
		});
	};

})(window.Zepto || window.jQuery);
<|endoftext|>


<commit_before>
function makeData() {
  "use strict";
  var data = [{x: new Date("2014-04-01 00:00"), y: 4},
              {x: new Date("2014-08-29 00:00"), y: 6}]
  return data;
}

function run(svg, data, Plottable) {
  "use strict";
  var doAnimate = true;
  var xScale = new Plottable.Scales.Time();
  var xAxis = new Plottable.Axes.Time(xScale, "bottom");

  var extent = function(){ return [data[0], data[1]]; };
  xScale.addPaddingExceptionsProvider(extent);

  var yScale = new Plottable.Scales.Linear();
  var yAxis = new Plottable.Axes.Numeric(yScale, "left");

  var dataset = new Plottable.Dataset(data);
  var lineRenderer = new Plottable.Plots.Line()
              .addDataset(dataset)
              .x(function(d) { return d.x; }, xScale)
              .y(function(d) { return d.y; }, yScale)
              .attr("opacity", 0.75)
              .animate(doAnimate);

  var lineChart = new Plottable.Components.Table([[yAxis, lineRenderer],
                                                 [null,  xAxis]]);
  lineChart.renderTo(svg);

  var cb = function(x, y){
    var d = dataset.data();
    dataset.data(d);
  };

  new Plottable.Interactions.Click().onClick(cb).attachTo(lineRenderer);
}
<commit_msg>jshint and fixing my test<commit_after>
function makeData() {
  "use strict";
  var data = [{x: new Date("2014-04-01 00:24"), y: 4},
              {x: new Date("2014-08-29 00:24"), y: 6}];
  return data;
}

function run(svg, data, Plottable) {
  "use strict";
  var doAnimate = true;
  var xScale = new Plottable.Scales.Time();
  var xAxis = new Plottable.Axes.Time(xScale, "bottom");

  var extent = function(){ return [new Date("2014-04-01 00:24"), new Date("2014-08-29 00:24")]; };
  xScale.addPaddingExceptionsProvider(extent);

  var yScale = new Plottable.Scales.Linear();
  var yAxis = new Plottable.Axes.Numeric(yScale, "left");

  var dataset = new Plottable.Dataset(data);
  var lineRenderer = new Plottable.Plots.Line()
              .addDataset(dataset)
              .x(function(d) { return d.x; }, xScale)
              .y(function(d) { return d.y; }, yScale)
              .attr("opacity", 0.75)
              .animate(doAnimate);

  var lineChart = new Plottable.Components.Table([[yAxis, lineRenderer],
                                                 [null,  xAxis]]);
  lineChart.renderTo(svg);

  var cb = function(x, y){
    var d = dataset.data();
    dataset.data(d);
  };

  new Plottable.Interactions.Click().onClick(cb).attachTo(lineRenderer);
}
<|endoftext|>


<commit_before>(function( $ ) {
	var requests = {};
	var zipValid = {
		us: /[0-9]{5}(-[0-9]{4})?/
	};

	$.ziptastic = function(country, zip, callback){
		// If only zip and callback are given default to US
		if (arguments.length == 2 && typeof arguments[1] == 'function') {
			callback = arguments[1];
			zip = arguments[0];
			country = 'US';
		}

		country = country.toUpperCase();
		// Only make unique requests
		if(!requests[country]) {
			requests[country] = {};
		}
		if(!requests[country][zip]) {
			requests[country][zip] = $.getJSON('http://zip.getziptastic.com/v2/' + country + '/' + zip);
		}

		// Bind to the finished request
		requests[country][zip].done(function(data) {
			if (typeof callback == 'function') {
				callback(data.country, data.state, data.state_short, data.city, zip);
			}
		});

		// Allow for binding to the deferred object
		return requests[country][zip];
	};

	$.fn.ziptastic = function( options ) {
		return this.each(function() {
			var ele = $(this);

			ele.on('keyup', function() {
				var zip = ele.val();

				// TODO Non-US zip codes?
				if(zipValid.us.test(zip)) {
					$.ziptastic(zip, function(country, state, state_short, city) {
						// Trigger the updated information
						ele.trigger('zipChange', [country, state, state_short, city, zip]);
					});
				}
			});
		});
	};
})( jQuery );
<commit_msg>Add protcol matching when making AJAX request.<commit_after>(function( $ ) {
	var requests = {};
	var zipValid = {
		us: /[0-9]{5}(-[0-9]{4})?/
	};

	$.ziptastic = function(country, zip, callback){
		// If only zip and callback are given default to US
		if (arguments.length == 2 && typeof arguments[1] == 'function') {
			callback = arguments[1];
			zip = arguments[0];
			country = 'US';
		}

		country = country.toUpperCase();
		// Only make unique requests
		if(!requests[country]) {
			requests[country] = {};
		}
		if(!requests[country][zip]) {
			requests[country][zip] = $.getJSON(location.protocol + '//zip.getziptastic.com/v2/' + country + '/' + zip);
		}

		// Bind to the finished request
		requests[country][zip].done(function(data) {
			if (typeof callback == 'function') {
				callback(data.country, data.state, data.state_short, data.city, zip);
			}
		});

		// Allow for binding to the deferred object
		return requests[country][zip];
	};

	$.fn.ziptastic = function( options ) {
		return this.each(function() {
			var ele = $(this);

			ele.on('keyup', function() {
				var zip = ele.val();

				// TODO Non-US zip codes?
				if(zipValid.us.test(zip)) {
					$.ziptastic(zip, function(country, state, state_short, city) {
						// Trigger the updated information
						ele.trigger('zipChange', [country, state, state_short, city, zip]);
					});
				}
			});
		});
	};
})( jQuery );
<|endoftext|>


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

/**
 * Control for changing a Property of type {number}.
 * Consists of a labeled value, slider and arrow buttons.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowButton = require( 'SUN/buttons/ArrowButton' );
  var Color = require( 'SCENERY/util/Color' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var HSlider = require( 'SUN/HSlider' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var NumberDisplay = require( 'SCENERY_PHET/NumberDisplay' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var Tandem = require( 'TANDEM/Tandem' );
  var TandemText = require( 'TANDEM/scenery/nodes/TandemText' );
  var Text = require( 'SCENERY/nodes/Text' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Util = require( 'DOT/Util' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  // phet-io modules
  var TNumberControl = require( 'ifphetio!PHET_IO/types/scenery-phet/TNumberControl' );

  /**
   * @param {string} title
   * @param {Property.<number>} numberProperty
   * @param {Range} numberRange
   * @param {Object} [options]
   * @constructor
   */
  function NumberControl( title, numberProperty, numberRange, options ) {

    options = _.extend( {

      startCallback: function() {}, // called when interaction begins
      endCallback: function() {}, // called when interaction ends
      enabledProperty: new Property( true ), // {Property.<boolean>} is this control enabled?
      disabledOpacity: 0.5, // {number} opacity used to make the control look disabled

      // title
      titleFont: new PhetFont( 12 ),
      titleMaxWidth: null, // {null|string} maxWidth to use for title, to constrain width for i18n

      // value
      valueFont: new PhetFont( 12 ),
      valueMaxWidth: null, // {null|string} maxWidth to use for value display, to constrain width for i18n
      decimalPlaces: 0,

      // {string} Pattern used to format the value. Must contain '{0}'.
      // If you want units or other verbiage, add them to the pattern, e.g. '{0} L'
      valuePattern: '{0}',

      // arrow buttons
      delta: 1,

      // slider
      majorTicks: [], // array of objects with these fields: { value: {number}, label: {Node} }
      minorTickSpacing: 0, // zero indicates no minor ticks
      trackSize: new Dimension2( 180, 3 ),
      thumbSize: new Dimension2( 17, 34 ),
      majorTickLength: 20,
      minorTickStroke: 'rgba( 0, 0, 0, 0.3 )',
      thumbFillEnabled: 'green',

      // A {function} that handles layout of subcomponents.
      // It has signature function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton )
      // and returns a Node. If you want to customize the layout, use one of the predefined creators
      // (see CREATE_LAYOUT_FUNCTION_*) or create your own function.
      layoutFunction: NumberControl.createLayoutFunction1(),

      // phet-io
      tandem: null // {Tandem|null}

    }, options );

    // highlight color for thumb defaults to a brighter version of the thumb color
    options.thumbFillHighlighted = options.thumbFillHighlighted || Color.toColor( options.thumbFillEnabled ).brighterColor();

    // constrain the slider value to the provided range and the same delta as the arrow buttons
    options.constrainValue = options.constrainValue || function( value ) {
        var newValue = Util.roundSymmetric( value / options.delta ) * options.delta;
        return numberRange.constrainValue( newValue );
      };

    // validate options
    assert && assert( options.disabledOpacity > 0 && options.disabledOpacity < 1, 'invalid disabledOpacity: ' + options.disabledOpacity );
    Tandem.validateOptions( options ); // The tandem is required when brand==='phet-io'

    var thisNode = this;

    var delta = options.delta; // to improve readability

    var titleNode = new TandemText( title, {
      font: options.titleFont,
      maxWidth: options.titleMaxWidth,
      tandem: options.tandem && options.tandem.createTandem( 'titleNode' )
    } );

    var numberDisplay = new NumberDisplay( numberProperty, numberRange, {
      valuePattern: options.valuePattern,
      font: options.valueFont,
      decimalPlaces: options.decimalPlaces,
      maxWidth: options.valueMaxWidth,
      tandem: options.tandem && options.tandem.createTandem( 'numberDisplay' )
    } );

    var arrowButtonOptions = {
      delta: options.delta,
      startCallback: options.startCallback,
      endCallback: options.endCallback,
      scale: 0.85
    };

    var leftArrowButton = new ArrowButton( 'left', function() {
      var value = numberProperty.get() - delta;
      value = Util.roundSymmetric( value / delta ) * delta; // constrain to delta
      value = Math.max( value, numberRange.min ); // constrain to range
      numberProperty.set( value );
    }, _.extend( {
      tandem: options.tandem && options.tandem.createTandem( 'leftArrowButton' )
    }, arrowButtonOptions ) );

    var rightArrowButton = new ArrowButton( 'right', function() {
      var value = numberProperty.get() + delta;
      value = Util.roundSymmetric( value / delta ) * delta; // constrain to delta
      value = Math.min( value, numberRange.max ); // constrain to range
      numberProperty.set( value );
    }, _.extend( {
      tandem: options.tandem && options.tandem.createTandem( 'rightArrowButton' )
    }, arrowButtonOptions ) );

    var arrowEnabledListener = function( value ) {
      leftArrowButton.enabled = ( value > numberRange.min );
      rightArrowButton.enabled = ( value < numberRange.max );
    };
    numberProperty.link( arrowEnabledListener );

    //TODO pass only options that are relevant to HSlider, see https://github.com/phetsims/scenery-phet/issues/255
    var slider = new HSlider( numberProperty, numberRange, _.extend( {
      startDrag: options.startCallback,
      endDrag: options.endCallback,
    }, options, {

      // This uses a 3-arg extend so that the tandem is overriden properly
      tandem: options.tandem && options.tandem.createTandem( 'slider' )
    } ) );

    // major ticks
    var majorTicks = options.majorTicks;
    for ( var i = 0; i < majorTicks.length; i++ ) {
      slider.addMajorTick( majorTicks[ i ].value, majorTicks[ i ].label );
    }

    // minor ticks, exclude values where we already have major ticks
    if ( options.minorTickSpacing > 0 ) {
      for ( var minorTickValue = numberRange.min; minorTickValue <= numberRange.max; ) {
        if ( !_.find( majorTicks, function( majorTick ) { return majorTick.value === minorTickValue; } ) ) {
          slider.addMinorTick( minorTickValue );
        }
        minorTickValue += options.minorTickSpacing;
      }
    }

    options.children = [
      options.layoutFunction( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton )
    ];
    Node.call( this, options );

    // enabled/disable this control
    this.enabledProperty = options.enabledProperty; // @public
    var enabledObserver = function( enabled ) {
      thisNode.pickable = enabled;
      thisNode.opacity = enabled ? 1.0 : options.disabledOpacity;
      //TODO if !enabled, cancel any interaction that is in progress, see scenery#218
    };
    this.enabledProperty.link( enabledObserver );

    // @private
    this.disposeNumberControl = function() {
      numberDisplay.dispose();
      numberProperty.unlink( arrowEnabledListener );
      thisNode.enabledProperty.unlink( enabledObserver );
      slider.dispose();
      options.tandem && options.tandem.removeInstance( thisNode );
    };

    options.tandem && options.tandem.addInstance( this, TNumberControl );
  }

  sceneryPhet.register( 'NumberControl', NumberControl );

  return inherit( VBox, NumberControl, {

    // @public
    dispose: function() {
      this.disposeNumberControl();
      VBox.prototype.dispose.call( this );
    },

    // @public
    setEnabled: function( enabled ) { this.enabledProperty.set( enabled ); },
    set enabled( value ) { this.setEnabled( value ); },

    // @public
    getEnabled: function() { return this.enabledProperty.get(); },
    get enabled() { return this.getEnabled(); }

  }, {

    /**
     * Creates a NumberControl with default tick marks for min and max values.
     * @param {string} label
     * @param {Property.<number>} property
     * @param {Range} range
     * @param {Object} [options]
     * @returns {NumberControl}
     * @static
     * @public
     */
    withMinMaxTicks: function( label, property, range, options ) {

      options = _.extend( {
        tickLabelFont: new PhetFont( 12 )
      }, options );

      options.majorTicks = [
        { value: range.min, label: new Text( range.min, { font: options.tickLabelFont } ) },
        { value: range.max, label: new Text( range.max, { font: options.tickLabelFont } ) }
      ];

      return new NumberControl( label, property, range, options );
    },

    /**
     * Creates one of the pre-defined layout functions that can be used for options.layoutFunction.
     * Arranges subcomponents like this:
     *
     *  title number
     *  < ------|------ >
     *
     * @param {Object} [options]
     * @returns {function}
     * @public
     * @static
     */
    createLayoutFunction1: function( options ) {

      options = _.extend( {
        align: 'center', // {string} horizontal alignment of rows, 'left'|'right'|'center'
        titleXSpacing: 5, // {number} horizontal spacing between title and number
        arrowButtonsXSpacing: 15, // {number} horizontal spacing between arrow buttons and slider
        ySpacing: 5 // {number} vertical spacing between rows
      }, options );

      return function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton ) {
        return new VBox( {
          align: options.align,
          spacing: options.ySpacing,
          children: [
            new HBox( {
              spacing: options.titleXSpacing,
              children: [ titleNode, numberDisplay ]
            } ),
            new HBox( {
              spacing: options.arrowButtonsXSpacing,
              resize: false, // prevent slider from causing a resize when thumb is at min or max
              children: [ leftArrowButton, slider, rightArrowButton ]
            } )
          ]
        } );
      };
    },

    /**
     * Creates one of the pre-defined layout functions that can be used for options.layoutFunction.
     * Arranges subcomponents like this:
     *
     *  title < number >
     *  ------|------
     *
     * @param {Object} [options]
     * @returns {function}
     * @public
     * @static
     */
    createLayoutFunction2: function( options ) {

      options = _.extend( {
        align: 'center', // {string} horizontal alignment of rows, 'left'|'right'|'center'
        xSpacing: 5, // {number} horizontal spacing in top row
        ySpacing: 5 // {number} vertical spacing between rows
      }, options );

      return function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton ) {
        return new VBox( {
          align: options.align,
          spacing: options.ySpacing,
          resize: false, // prevent slider from causing a resize when thumb is at min or max
          children: [
            new HBox( {
              spacing: options.xSpacing,
              children: [ titleNode, leftArrowButton, numberDisplay, rightArrowButton ]
            } ),
            slider
          ]
        } );
      };
    },

    /**
     * Creates one of the pre-defined layout functions that can be used for options.layoutFunction.
     * Arranges subcomponents like this:
     *
     *  title
     *  < number >
     *  -------|-------
     *
     * @param {Object} [options]
     * @returns {function}
     * @public
     * @static
     */
    createLayoutFunction3: function( options ) {

      options = _.extend( {
        alignTitle: 'center', // {string} horizontal alignment of title, relative to slider, 'left'|'right'|'center'
        alignNumber: 'center', // {string} horizontal alignment of number display, relative to slider, 'left'|'right'|'center'
        xSpacing: 5, // {number} horizontal spacing between arrow buttons and slider
        ySpacing: 5 // {number} vertical spacing between rows
      }, options );

      return function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton ) {
        return new VBox( {
          spacing: options.ySpacing,
          resize: false, // prevent slider from causing a resize when thumb is at min or max
          align: options.alignTitle,
          children: [
            titleNode,
            new VBox( {
              spacing: options.ySpacing,
              resize: false, // prevent slider from causing a resize when thumb is at min or max
              align: options.alignNumber,
              children: [
                new HBox( {
                  spacing: options.xSpacing,
                  children: [ leftArrowButton, numberDisplay, rightArrowButton ]
                } ),
                slider
              ]
            } )
          ]
        } );
      };
    }
  } );
} );
<commit_msg>organize modules<commit_after>// Copyright 2015, University of Colorado Boulder

/**
 * Control for changing a Property of type {number}.
 * Consists of a labeled value, slider and arrow buttons.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowButton = require( 'SUN/buttons/ArrowButton' );
  var Color = require( 'SCENERY/util/Color' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var HSlider = require( 'SUN/HSlider' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var NumberDisplay = require( 'SCENERY_PHET/NumberDisplay' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Tandem = require( 'TANDEM/Tandem' );
  var TandemText = require( 'TANDEM/scenery/nodes/TandemText' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  // phet-io modules
  var TNumberControl = require( 'ifphetio!PHET_IO/types/scenery-phet/TNumberControl' );

  /**
   * @param {string} title
   * @param {Property.<number>} numberProperty
   * @param {Range} numberRange
   * @param {Object} [options]
   * @constructor
   */
  function NumberControl( title, numberProperty, numberRange, options ) {

    options = _.extend( {

      startCallback: function() {}, // called when interaction begins
      endCallback: function() {}, // called when interaction ends
      enabledProperty: new Property( true ), // {Property.<boolean>} is this control enabled?
      disabledOpacity: 0.5, // {number} opacity used to make the control look disabled

      // title
      titleFont: new PhetFont( 12 ),
      titleMaxWidth: null, // {null|string} maxWidth to use for title, to constrain width for i18n

      // value
      valueFont: new PhetFont( 12 ),
      valueMaxWidth: null, // {null|string} maxWidth to use for value display, to constrain width for i18n
      decimalPlaces: 0,

      // {string} Pattern used to format the value. Must contain '{0}'.
      // If you want units or other verbiage, add them to the pattern, e.g. '{0} L'
      valuePattern: '{0}',

      // arrow buttons
      delta: 1,

      // slider
      majorTicks: [], // array of objects with these fields: { value: {number}, label: {Node} }
      minorTickSpacing: 0, // zero indicates no minor ticks
      trackSize: new Dimension2( 180, 3 ),
      thumbSize: new Dimension2( 17, 34 ),
      majorTickLength: 20,
      minorTickStroke: 'rgba( 0, 0, 0, 0.3 )',
      thumbFillEnabled: 'green',

      // A {function} that handles layout of subcomponents.
      // It has signature function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton )
      // and returns a Node. If you want to customize the layout, use one of the predefined creators
      // (see CREATE_LAYOUT_FUNCTION_*) or create your own function.
      layoutFunction: NumberControl.createLayoutFunction1(),

      // phet-io
      tandem: null // {Tandem|null}

    }, options );

    // highlight color for thumb defaults to a brighter version of the thumb color
    options.thumbFillHighlighted = options.thumbFillHighlighted || Color.toColor( options.thumbFillEnabled ).brighterColor();

    // constrain the slider value to the provided range and the same delta as the arrow buttons
    options.constrainValue = options.constrainValue || function( value ) {
        var newValue = Util.roundSymmetric( value / options.delta ) * options.delta;
        return numberRange.constrainValue( newValue );
      };

    // validate options
    assert && assert( options.disabledOpacity > 0 && options.disabledOpacity < 1, 'invalid disabledOpacity: ' + options.disabledOpacity );
    Tandem.validateOptions( options ); // The tandem is required when brand==='phet-io'

    var thisNode = this;

    var delta = options.delta; // to improve readability

    var titleNode = new TandemText( title, {
      font: options.titleFont,
      maxWidth: options.titleMaxWidth,
      tandem: options.tandem && options.tandem.createTandem( 'titleNode' )
    } );

    var numberDisplay = new NumberDisplay( numberProperty, numberRange, {
      valuePattern: options.valuePattern,
      font: options.valueFont,
      decimalPlaces: options.decimalPlaces,
      maxWidth: options.valueMaxWidth,
      tandem: options.tandem && options.tandem.createTandem( 'numberDisplay' )
    } );

    var arrowButtonOptions = {
      delta: options.delta,
      startCallback: options.startCallback,
      endCallback: options.endCallback,
      scale: 0.85
    };

    var leftArrowButton = new ArrowButton( 'left', function() {
      var value = numberProperty.get() - delta;
      value = Util.roundSymmetric( value / delta ) * delta; // constrain to delta
      value = Math.max( value, numberRange.min ); // constrain to range
      numberProperty.set( value );
    }, _.extend( {
      tandem: options.tandem && options.tandem.createTandem( 'leftArrowButton' )
    }, arrowButtonOptions ) );

    var rightArrowButton = new ArrowButton( 'right', function() {
      var value = numberProperty.get() + delta;
      value = Util.roundSymmetric( value / delta ) * delta; // constrain to delta
      value = Math.min( value, numberRange.max ); // constrain to range
      numberProperty.set( value );
    }, _.extend( {
      tandem: options.tandem && options.tandem.createTandem( 'rightArrowButton' )
    }, arrowButtonOptions ) );

    var arrowEnabledListener = function( value ) {
      leftArrowButton.enabled = ( value > numberRange.min );
      rightArrowButton.enabled = ( value < numberRange.max );
    };
    numberProperty.link( arrowEnabledListener );

    //TODO pass only options that are relevant to HSlider, see https://github.com/phetsims/scenery-phet/issues/255
    var slider = new HSlider( numberProperty, numberRange, _.extend( {
      startDrag: options.startCallback,
      endDrag: options.endCallback,
    }, options, {

      // This uses a 3-arg extend so that the tandem is overriden properly
      tandem: options.tandem && options.tandem.createTandem( 'slider' )
    } ) );

    // major ticks
    var majorTicks = options.majorTicks;
    for ( var i = 0; i < majorTicks.length; i++ ) {
      slider.addMajorTick( majorTicks[ i ].value, majorTicks[ i ].label );
    }

    // minor ticks, exclude values where we already have major ticks
    if ( options.minorTickSpacing > 0 ) {
      for ( var minorTickValue = numberRange.min; minorTickValue <= numberRange.max; ) {
        if ( !_.find( majorTicks, function( majorTick ) { return majorTick.value === minorTickValue; } ) ) {
          slider.addMinorTick( minorTickValue );
        }
        minorTickValue += options.minorTickSpacing;
      }
    }

    options.children = [
      options.layoutFunction( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton )
    ];
    Node.call( this, options );

    // enabled/disable this control
    this.enabledProperty = options.enabledProperty; // @public
    var enabledObserver = function( enabled ) {
      thisNode.pickable = enabled;
      thisNode.opacity = enabled ? 1.0 : options.disabledOpacity;
      //TODO if !enabled, cancel any interaction that is in progress, see scenery#218
    };
    this.enabledProperty.link( enabledObserver );

    // @private
    this.disposeNumberControl = function() {
      numberDisplay.dispose();
      numberProperty.unlink( arrowEnabledListener );
      thisNode.enabledProperty.unlink( enabledObserver );
      slider.dispose();
      options.tandem && options.tandem.removeInstance( thisNode );
    };

    options.tandem && options.tandem.addInstance( this, TNumberControl );
  }

  sceneryPhet.register( 'NumberControl', NumberControl );

  return inherit( VBox, NumberControl, {

    // @public
    dispose: function() {
      this.disposeNumberControl();
      VBox.prototype.dispose.call( this );
    },

    // @public
    setEnabled: function( enabled ) { this.enabledProperty.set( enabled ); },
    set enabled( value ) { this.setEnabled( value ); },

    // @public
    getEnabled: function() { return this.enabledProperty.get(); },
    get enabled() { return this.getEnabled(); }

  }, {

    /**
     * Creates a NumberControl with default tick marks for min and max values.
     * @param {string} label
     * @param {Property.<number>} property
     * @param {Range} range
     * @param {Object} [options]
     * @returns {NumberControl}
     * @static
     * @public
     */
    withMinMaxTicks: function( label, property, range, options ) {

      options = _.extend( {
        tickLabelFont: new PhetFont( 12 )
      }, options );

      options.majorTicks = [
        { value: range.min, label: new Text( range.min, { font: options.tickLabelFont } ) },
        { value: range.max, label: new Text( range.max, { font: options.tickLabelFont } ) }
      ];

      return new NumberControl( label, property, range, options );
    },

    /**
     * Creates one of the pre-defined layout functions that can be used for options.layoutFunction.
     * Arranges subcomponents like this:
     *
     *  title number
     *  < ------|------ >
     *
     * @param {Object} [options]
     * @returns {function}
     * @public
     * @static
     */
    createLayoutFunction1: function( options ) {

      options = _.extend( {
        align: 'center', // {string} horizontal alignment of rows, 'left'|'right'|'center'
        titleXSpacing: 5, // {number} horizontal spacing between title and number
        arrowButtonsXSpacing: 15, // {number} horizontal spacing between arrow buttons and slider
        ySpacing: 5 // {number} vertical spacing between rows
      }, options );

      return function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton ) {
        return new VBox( {
          align: options.align,
          spacing: options.ySpacing,
          children: [
            new HBox( {
              spacing: options.titleXSpacing,
              children: [ titleNode, numberDisplay ]
            } ),
            new HBox( {
              spacing: options.arrowButtonsXSpacing,
              resize: false, // prevent slider from causing a resize when thumb is at min or max
              children: [ leftArrowButton, slider, rightArrowButton ]
            } )
          ]
        } );
      };
    },

    /**
     * Creates one of the pre-defined layout functions that can be used for options.layoutFunction.
     * Arranges subcomponents like this:
     *
     *  title < number >
     *  ------|------
     *
     * @param {Object} [options]
     * @returns {function}
     * @public
     * @static
     */
    createLayoutFunction2: function( options ) {

      options = _.extend( {
        align: 'center', // {string} horizontal alignment of rows, 'left'|'right'|'center'
        xSpacing: 5, // {number} horizontal spacing in top row
        ySpacing: 5 // {number} vertical spacing between rows
      }, options );

      return function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton ) {
        return new VBox( {
          align: options.align,
          spacing: options.ySpacing,
          resize: false, // prevent slider from causing a resize when thumb is at min or max
          children: [
            new HBox( {
              spacing: options.xSpacing,
              children: [ titleNode, leftArrowButton, numberDisplay, rightArrowButton ]
            } ),
            slider
          ]
        } );
      };
    },

    /**
     * Creates one of the pre-defined layout functions that can be used for options.layoutFunction.
     * Arranges subcomponents like this:
     *
     *  title
     *  < number >
     *  -------|-------
     *
     * @param {Object} [options]
     * @returns {function}
     * @public
     * @static
     */
    createLayoutFunction3: function( options ) {

      options = _.extend( {
        alignTitle: 'center', // {string} horizontal alignment of title, relative to slider, 'left'|'right'|'center'
        alignNumber: 'center', // {string} horizontal alignment of number display, relative to slider, 'left'|'right'|'center'
        xSpacing: 5, // {number} horizontal spacing between arrow buttons and slider
        ySpacing: 5 // {number} vertical spacing between rows
      }, options );

      return function( titleNode, numberDisplay, slider, leftArrowButton, rightArrowButton ) {
        return new VBox( {
          spacing: options.ySpacing,
          resize: false, // prevent slider from causing a resize when thumb is at min or max
          align: options.alignTitle,
          children: [
            titleNode,
            new VBox( {
              spacing: options.ySpacing,
              resize: false, // prevent slider from causing a resize when thumb is at min or max
              align: options.alignNumber,
              children: [
                new HBox( {
                  spacing: options.xSpacing,
                  children: [ leftArrowButton, numberDisplay, rightArrowButton ]
                } ),
                slider
              ]
            } )
          ]
        } );
      };
    }
  } );
} );
<|endoftext|>


<commit_before>/**
 * Convert unit from one unit type to another.
 * ======================================================================== */

var plugin = function(style){

  /**
   * Convert any CSS <length> or <percentage> value to any another.
   *
   * Used: Compass `convert-length` function with some changes
   */
  var to_unit = function(length, to, from_context, to_context) {
    // Context values must be in px so we can determine a conversion ratio for
    // relative units.
    var base = 16,
        DPI = 72,
        ratio = (3 / 5),
        Unit = style.nodes.Unit;

    if(typeof from_context == 'undefined') {
      from_context = base;
    } else {
      if(from_context.type != 'px') {
        console.error("Paremeter from_context must resolve to a value in pixel units.")
      }

      from_context = from_context.val;
    }

    if(typeof to_context == 'undefined') {
      to_context = from_context;
    } else {
      if(to_context.type != 'px') {
        console.error("Parameter to_context must resolve to a value in pixel units.")
      }

      to_context = to_context.val;
    }

    from = length.type;
    length = length.val;
    to = to.name ? to.name : to.val;

    // Optimize for cases where `from` and `to` units are accidentally the same.
    if(from == to) {
      return new Unit(length, from);
    }

    if(from == '' || typeof from == 'undefined') {
      return new Unit(length, to);
    }

    if(from != 'px') {
      // Convert relative units using the from-context parameter.
      if     (from == 'em') {
        length = length * from_context;
      } else if(from == 'rem') {
        length = length * base;
      } else if(from == '%') {
        length = length * from_context;
      } else if(from == 'ex') {
        length = length * from_context / 2;
      } else if(from == 'ch') {
        length = length * from_context * ratio;
      } else if(from == 'in') {
        length = length * DPI;
      } else if(from == 'mm') {
        length = length / 2.54 * DPI / 10;
      } else if(from == 'cm') {
        length = length / 2.54 * DPI;
      } else if(from == 'pt') {
        length = length * 96 / 72;
      } else if(from == 'pc') {
        length = length * from_context;
      } else {
        // Certain units can't be converted.
        if(from == 'vw' || from == 'vh' || from == 'vmin') {
          console.warn("From: '" + from + "' units can't be reliably converted. " +
              "Returning original value.");
          return new Unit(length, from);
        } else {
          console.warn("From: '" + from + "' is an unknown length unit. " +
              "Returning original value.")
          return new Unit(length, from);
        }
      }
    }

    if(to != 'px') {
      if       (to == 'em') {       // Relative units
        length = length / to_context;
      } else if(to == 'rem') {
        length = length / base;
      } else if(to == '%') {
        length = length / to_context * 100;
      } else if(to == 'ex') {
        length = length / to_context * 2;
      } else if(to == 'ch') {
          length = length / to_context / ratio;
      } else if(to == 'in') {       // Absolute units
        length = length / DPI;
      } else if(to == 'mm') {
        length = length * 2.54 / DPI * 10;
      } else if(to == 'cm') {
        length = length * 2.54 / DPI;
      } else if(to == 'pt') {
        length = length * 72 / 96;
      } else if(to == 'pc') {
        length = length / to_context;
      } else if(to == 'vw' || to == 'vh' || to == 'vmin') {
        console.warn("To: '" + to + "' units can't be reliably converted. " +
            "Returning original value.");
        return new Unit(length, from);
      } else {
        console.warn("To: '" + to + "' is an unknown length unit. " +
            "Returning original value.");
        return new Unit(length, from);
      }
    }

    return new Unit(length, to);
  };

  style.define('to_unit', to_unit);

};

module.exports = {
  plugin: plugin,
}<commit_msg>Base DPI value for unit conversion fix.<commit_after>/**
 * Convert unit from one unit type to another.
 * ======================================================================== */

var plugin = function(style){

  /**
   * Convert any CSS <length> or <percentage> value to any another.
   *
   * Used: Compass `convert-length` function with some changes
   */
  var to_unit = function(length, to, from_context, to_context) {
    // Context values must be in px so we can determine a conversion ratio for
    // relative units.
    var base = 16,
        DPI = 96,
        ratio = (3 / 5),
        Unit = style.nodes.Unit;

    if(typeof from_context == 'undefined') {
      from_context = base;
    } else {
      if(from_context.type != 'px') {
        console.error("Paremeter from_context must resolve to a value in pixel units.")
      }

      from_context = from_context.val;
    }

    if(typeof to_context == 'undefined') {
      to_context = from_context;
    } else {
      if(to_context.type != 'px') {
        console.error("Parameter to_context must resolve to a value in pixel units.")
      }

      to_context = to_context.val;
    }

    from = length.type;
    length = length.val;
    to = to.name ? to.name : to.val;

    // Optimize for cases where `from` and `to` units are accidentally the same.
    if(from == to) {
      return new Unit(length, from);
    }

    if(from == '' || typeof from == 'undefined') {
      return new Unit(length, to);
    }

    if(from != 'px') {
      // Convert relative units using the from-context parameter.
      if     (from == 'em') {
        length = length * from_context;
      } else if(from == 'rem') {
        length = length * base;
      } else if(from == '%') {
        length = length * from_context;
      } else if(from == 'ex') {
        length = length * from_context / 2;
      } else if(from == 'ch') {
        length = length * from_context * ratio;
      } else if(from == 'in') {
        length = length * DPI;
      } else if(from == 'mm') {
        length = length / 2.54 * DPI / 10;
      } else if(from == 'cm') {
        length = length / 2.54 * DPI;
      } else if(from == 'pt') {
        length = length * 96 / 72;
      } else if(from == 'pc') {
        length = length * from_context;
      } else {
        // Certain units can't be converted.
        if(from == 'vw' || from == 'vh' || from == 'vmin') {
          console.warn("From: '" + from + "' units can't be reliably converted. " +
              "Returning original value.");
          return new Unit(length, from);
        } else {
          console.warn("From: '" + from + "' is an unknown length unit. " +
              "Returning original value.")
          return new Unit(length, from);
        }
      }
    }

    if(to != 'px') {
      if       (to == 'em') {       // Relative units
        length = length / to_context;
      } else if(to == 'rem') {
        length = length / base;
      } else if(to == '%') {
        length = length / to_context * 100;
      } else if(to == 'ex') {
        length = length / to_context * 2;
      } else if(to == 'ch') {
          length = length / to_context / ratio;
      } else if(to == 'in') {       // Absolute units
        length = length / DPI;
      } else if(to == 'mm') {
        length = length * 2.54 / DPI * 10;
      } else if(to == 'cm') {
        length = length * 2.54 / DPI;
      } else if(to == 'pt') {
        length = length * 72 / 96;
      } else if(to == 'pc') {
        length = length / to_context;
      } else if(to == 'vw' || to == 'vh' || to == 'vmin') {
        console.warn("To: '" + to + "' units can't be reliably converted. " +
            "Returning original value.");
        return new Unit(length, from);
      } else {
        console.warn("To: '" + to + "' is an unknown length unit. " +
            "Returning original value.");
        return new Unit(length, from);
      }
    }

    return new Unit(length, to);
  };

  style.define('to_unit', to_unit);

};

module.exports = {
  plugin: plugin,
}<|endoftext|>


<commit_before>
"use strict";

var
    _       = require('underscore'),
    moment  = require('moment'),
    Promise = require("bluebird"),
    LeaveDay = require('../leave_day');

module.exports = function(sequelize, DataTypes) {
    var Leave = sequelize.define("Leave", {
        // TODO add validators!
        'status' : {
            type      : DataTypes.INTEGER,
            allowNull : false
        },
        employee_comment : {
            type      : DataTypes.STRING,
            allowNull : true,
        },
        approver_comment : {
            type      : DataTypes.STRING,
            allowNull : true,
        },
        decided_at : {
            type      : DataTypes.DATE,
            allowNull : true,
        },

        date_start : {
            type         : DataTypes.DATE,
            allowNull    : false,
            defaultValue : sequelize.NOW,
        },
        day_part_start : {
            type         : DataTypes.INTEGER,
            allowNull    : false,
            defaultValue : 1, // VPP TODO replace with constant value
        },
        date_end : {
            type         : DataTypes.DATE,
            allowNull    : false,
            defaultValue : sequelize.NOW,
        },
        day_part_end : {
            type         : DataTypes.INTEGER,
            allowNull    : false,
            defaultValue : 1, // VPP TODO replace with constant value
        },
    }, {

        indexes : [
          {
            fields : ['userId'],
          },
          {
            fields : ['leaveTypeId'],
          },
          {
            fields : ['approverid'],
          },
        ],
        classMethods : {
            associate : function( models ){
                Leave.belongsTo(models.User, { as : 'user',foreignKey     : 'userId' });
                Leave.belongsTo(models.User, { as : 'approver',foreignKey : 'approverId' });
                Leave.belongsTo(models.LeaveType, { as : 'leave_type' } );
            },

            status_new : function(){
                return 1;
            },

            status_approved : function(){
                return 2;
            },

            status_rejected : function() {
              return 3;
            },

            status_pended_revoke : function(){
              return 4;
            },

            leave_day_part_all : function(){
                return 1;
            },

            leave_day_part_morning : function(){
                return 2;
            },

            leave_day_part_afternoon : function(){
                return 3;
            },

            /*
             * Create new leave for provided parameters.
             * Returns promise that is resolved with newly created leave row
             * */
            create_new_leave : function(args){

              // Make sure all required data is provided
              _.each(
                  ['for_employee','of_type','with_parameters'],
                  function(property){
                      if (! _.has(args, property)) {
                          throw new Error('No mandatory '+property+' was provided');
                      }
                  }
              );

              var employee      = args.for_employee,
              leave_type        = args.of_type,
              valide_attributes = args.with_parameters;

              // Make sure that booking to be created is not going to ovelap with
              // any existing bookings
              return Promise.try(function(){
                  return employee.validate_overlapping(valide_attributes);
              })
              .then(function(){
                  return employee.promise_superviser();
              })

              .then(function(superviser){

                  var start_date = moment(valide_attributes.from_date),
                  end_date       = moment(valide_attributes.to_date);

                  // Check that start date is not bigger then end one
                  if ( start_date.toDate() > end_date.toDate() ) {
                      throw new Error('Start date is later than end date');
                  }

                  // Following statement creates in memory only leave object
                  // it is not in database until .save() method is called
                  var leave_to_create = sequelize.models.Leave.build({
                    userId           : employee.id,
                    leaveTypeId      : leave_type.id,
                    status           : Leave.status_new(),
                    approverId       : superviser.id,
                    employee_comment : valide_attributes.reason,

                    date_start     : start_date.format('YYYY-MM-DD'),
                    date_end       : end_date.format('YYYY-MM-DD'),
                    day_part_start : valide_attributes.from_date_part,
                    day_part_end   : valide_attributes.to_date_part,
                  });

                  return employee.validate_leave_fits_into_remaining_allowance({
                    year       : start_date,
                    leave_type : leave_type,
                    leave      : leave_to_create,
                  })
                  .then(function(){
                    return leave_to_create.save();
                  });

              });
            }, // End of create_new_leave
        }, // End of class methods

        instanceMethods : {

get_days : function() {

  var self   = this,
  start_date = moment(this.date_start),
  end_date   = moment(this.date_end),
  days       = [ start_date ];

  if (self.hasOwnProperty('_days')) {
    return self._days;
  }

  if ( ! start_date.isSame( end_date, 'day') ){

      var days_in_between = end_date.diff( start_date, 'days' ) - 1;

      for (var i=1; i<=days_in_between; i++) {
          days.push( start_date.clone().add(i, 'days') );
      }

      days.push( end_date );
  }

  days = _.map(
      days,
      function(day){
          return new LeaveDay({
            sequelize : sequelize,
            date     : day.format('YYYY-MM-DD'),
            day_part : day.isSame(start_date, 'day')
              ? self.day_part_start
              : day.isSame(end_date, 'day')
              ? self.day_part_end
              : Leave.leave_day_part_all(),
          });
      }
  );

  return self._days = days;
},

fit_with_leave_request : function(leave_request) {

    // If start and end dates are the same, check if one of them fit
    // into fist or last leave_days.
    if (
        leave_request.is_within_one_day() && (
            leave_request.does_fit_with_leave_day( _.last(this.get_days()) )
            ||
            leave_request.does_fit_with_leave_day( _.first(this.get_days()) )
        )
      ) {
        return true;
    }

    // If start and end dates are different, check if start date
    // fits into end leave_day or end date fits int start leave_date.
    if (
        (! leave_request.is_within_one_day()) && (
            leave_request.does_fit_with_leave_day_at_start(
                 _.last(this.get_days())
            )
            ||
            leave_request.does_fit_with_leave_day_at_end(
                 _.first(this.get_days())
            )
        )
    ) {
        return true;
    }

    return false;
}, // End of fit_with_leave_request

is_new_leave : function() {
    return this.status === Leave.status_new();
},

is_pended_revoke_leave : function(){
  return this.status === Leave.status_pended_revoke();
},

// Leave is treated as "approved" one if it is in approved staus
// or if it is waiting decision on revoke action
//
is_approved_leave : function() {
  return this.status === Leave.status_approved() ||
    this.status === Leave.status_pended_revoke() ;
},

get_start_leave_day : function(){
    return this.get_days()[0];
},

get_end_leave_day : function(){
    return this.get_days()[ this.get_days().length - 1 ];
},

get_deducted_days_number : function(args) {
  var number_of_days = this.get_deducted_days(args).length;

  // leave spans via on working day only, pay attention only to the start date
  if (number_of_days === 1 && !this.get_start_leave_day().is_all_day_leave()) {
    number_of_days = number_of_days - 0.5;
  }

  // case when leave spreads for more then one day, then check if both start and day
  // are halfs
  else if (number_of_days > 1) {
    if ( ! this.get_start_leave_day().is_all_day_leave() ){
      number_of_days = number_of_days - 0.5;
    }
    if ( ! this.get_end_leave_day().is_all_day_leave() ) {
      number_of_days = number_of_days - 0.5;
    }
  }

  return number_of_days;
},

get_deducted_days : function(args) {

  var leave_days = [],
    ignore_allowance = false,
    leave_type = this.leave_type || args.leave_type,
    year;

  if (args && args.hasOwnProperty('ignore_allowance')) {
    ignore_allowance = args.ignore_allowance;
  }

  if (args && args.hasOwnProperty('year')) {
    year = moment(args.year, 'YYYY');
  }

  // If current Leave stands for type that does not use
  // allowance, ignore rest of the code;
  if (! ignore_allowance && !leave_type.use_allowance) return leave_days;

  var user = this.approver || this.user || args.user;

  var bank_holiday_map = {};

  user.company.bank_holidays.forEach(function(bank_holiday){
    bank_holiday_map[ bank_holiday.get_pretty_date() ] = 1;
  });

  leave_days = _.filter(
    _.map(this.get_days(), function(leave_day){

      // Ignore bank holidays
      if ( bank_holiday_map[ leave_day.get_pretty_date() ] ) return;

      // If it happenned that current leave day is from the year current
      // call was made of, ignore that day
      if (year && year.year() !== moment(leave_day.date).year()) return;

      // Ignore weekends
      //
      // Here 6 and 0 are moment's day() indexes for
      // weekend days
      if ( moment(leave_day.date).day() === 6
        || moment(leave_day.date).day() === 0
      ) return;

      return leave_day;
    }),
    function(leave_day){
      return !! leave_day;
    }
  ) || [];

  return leave_days;
}, // End get_deducted_days

promise_to_reject : function() {
  // See explanation to promise_to_approve
  this.status = this.is_pended_revoke_leave() ?
    Leave.status_approved():
    Leave.status_rejected();
  return this.save();
},

promise_to_approve : function() {
  // If current leave is one with requested revoke, then
  // approve action set it into Rejected status
  // otherwise it is approve action for new leave
  // so put leave into Approved
  this.status = this.is_pended_revoke_leave() ?
    Leave.status_rejected():
    Leave.status_approved();
  return this.save();
},

promise_to_revoke : function(){
  var self = this;

  return self.getUser({
      include : [
        {
          model : sequelize.models.Department,
          as    : 'department',
        }
      ],
    })
    .then(function(user){
      self.approverId = user.department.bossId;
      self.status = Leave.status_pended_revoke();

      return self.save();
    })
},

get_leave_type_name : function() {
  var leave_type = this.get('leave_type');

  if (! leave_type ) {
    return '';
  } else {
    return leave_type.name;
  }
},

promise_approver : function() {

  return this.getApprover({
    include : [{
      model : sequelize.models.Company,
      as : 'company',
      include : [{
        model : sequelize.models.BankHoliday,
        as : 'bank_holidays',
      }],
    }],
  })
},

        },
    });

    return Leave;
};
<commit_msg>Fix column name<commit_after>
"use strict";

var
    _       = require('underscore'),
    moment  = require('moment'),
    Promise = require("bluebird"),
    LeaveDay = require('../leave_day');

module.exports = function(sequelize, DataTypes) {
    var Leave = sequelize.define("Leave", {
        // TODO add validators!
        'status' : {
            type      : DataTypes.INTEGER,
            allowNull : false
        },
        employee_comment : {
            type      : DataTypes.STRING,
            allowNull : true,
        },
        approver_comment : {
            type      : DataTypes.STRING,
            allowNull : true,
        },
        decided_at : {
            type      : DataTypes.DATE,
            allowNull : true,
        },

        date_start : {
            type         : DataTypes.DATE,
            allowNull    : false,
            defaultValue : sequelize.NOW,
        },
        day_part_start : {
            type         : DataTypes.INTEGER,
            allowNull    : false,
            defaultValue : 1, // VPP TODO replace with constant value
        },
        date_end : {
            type         : DataTypes.DATE,
            allowNull    : false,
            defaultValue : sequelize.NOW,
        },
        day_part_end : {
            type         : DataTypes.INTEGER,
            allowNull    : false,
            defaultValue : 1, // VPP TODO replace with constant value
        },
    }, {

        indexes : [
          {
            fields : ['userId'],
          },
          {
            fields : ['leaveTypeId'],
          },
          {
            fields : ['approverId'],
          },
        ],
        classMethods : {
            associate : function( models ){
                Leave.belongsTo(models.User, { as : 'user',foreignKey     : 'userId' });
                Leave.belongsTo(models.User, { as : 'approver',foreignKey : 'approverId' });
                Leave.belongsTo(models.LeaveType, { as : 'leave_type' } );
            },

            status_new : function(){
                return 1;
            },

            status_approved : function(){
                return 2;
            },

            status_rejected : function() {
              return 3;
            },

            status_pended_revoke : function(){
              return 4;
            },

            leave_day_part_all : function(){
                return 1;
            },

            leave_day_part_morning : function(){
                return 2;
            },

            leave_day_part_afternoon : function(){
                return 3;
            },

            /*
             * Create new leave for provided parameters.
             * Returns promise that is resolved with newly created leave row
             * */
            create_new_leave : function(args){

              // Make sure all required data is provided
              _.each(
                  ['for_employee','of_type','with_parameters'],
                  function(property){
                      if (! _.has(args, property)) {
                          throw new Error('No mandatory '+property+' was provided');
                      }
                  }
              );

              var employee      = args.for_employee,
              leave_type        = args.of_type,
              valide_attributes = args.with_parameters;

              // Make sure that booking to be created is not going to ovelap with
              // any existing bookings
              return Promise.try(function(){
                  return employee.validate_overlapping(valide_attributes);
              })
              .then(function(){
                  return employee.promise_superviser();
              })

              .then(function(superviser){

                  var start_date = moment(valide_attributes.from_date),
                  end_date       = moment(valide_attributes.to_date);

                  // Check that start date is not bigger then end one
                  if ( start_date.toDate() > end_date.toDate() ) {
                      throw new Error('Start date is later than end date');
                  }

                  // Following statement creates in memory only leave object
                  // it is not in database until .save() method is called
                  var leave_to_create = sequelize.models.Leave.build({
                    userId           : employee.id,
                    leaveTypeId      : leave_type.id,
                    status           : Leave.status_new(),
                    approverId       : superviser.id,
                    employee_comment : valide_attributes.reason,

                    date_start     : start_date.format('YYYY-MM-DD'),
                    date_end       : end_date.format('YYYY-MM-DD'),
                    day_part_start : valide_attributes.from_date_part,
                    day_part_end   : valide_attributes.to_date_part,
                  });

                  return employee.validate_leave_fits_into_remaining_allowance({
                    year       : start_date,
                    leave_type : leave_type,
                    leave      : leave_to_create,
                  })
                  .then(function(){
                    return leave_to_create.save();
                  });

              });
            }, // End of create_new_leave
        }, // End of class methods

        instanceMethods : {

get_days : function() {

  var self   = this,
  start_date = moment(this.date_start),
  end_date   = moment(this.date_end),
  days       = [ start_date ];

  if (self.hasOwnProperty('_days')) {
    return self._days;
  }

  if ( ! start_date.isSame( end_date, 'day') ){

      var days_in_between = end_date.diff( start_date, 'days' ) - 1;

      for (var i=1; i<=days_in_between; i++) {
          days.push( start_date.clone().add(i, 'days') );
      }

      days.push( end_date );
  }

  days = _.map(
      days,
      function(day){
          return new LeaveDay({
            sequelize : sequelize,
            date     : day.format('YYYY-MM-DD'),
            day_part : day.isSame(start_date, 'day')
              ? self.day_part_start
              : day.isSame(end_date, 'day')
              ? self.day_part_end
              : Leave.leave_day_part_all(),
          });
      }
  );

  return self._days = days;
},

fit_with_leave_request : function(leave_request) {

    // If start and end dates are the same, check if one of them fit
    // into fist or last leave_days.
    if (
        leave_request.is_within_one_day() && (
            leave_request.does_fit_with_leave_day( _.last(this.get_days()) )
            ||
            leave_request.does_fit_with_leave_day( _.first(this.get_days()) )
        )
      ) {
        return true;
    }

    // If start and end dates are different, check if start date
    // fits into end leave_day or end date fits int start leave_date.
    if (
        (! leave_request.is_within_one_day()) && (
            leave_request.does_fit_with_leave_day_at_start(
                 _.last(this.get_days())
            )
            ||
            leave_request.does_fit_with_leave_day_at_end(
                 _.first(this.get_days())
            )
        )
    ) {
        return true;
    }

    return false;
}, // End of fit_with_leave_request

is_new_leave : function() {
    return this.status === Leave.status_new();
},

is_pended_revoke_leave : function(){
  return this.status === Leave.status_pended_revoke();
},

// Leave is treated as "approved" one if it is in approved staus
// or if it is waiting decision on revoke action
//
is_approved_leave : function() {
  return this.status === Leave.status_approved() ||
    this.status === Leave.status_pended_revoke() ;
},

get_start_leave_day : function(){
    return this.get_days()[0];
},

get_end_leave_day : function(){
    return this.get_days()[ this.get_days().length - 1 ];
},

get_deducted_days_number : function(args) {
  var number_of_days = this.get_deducted_days(args).length;

  // leave spans via on working day only, pay attention only to the start date
  if (number_of_days === 1 && !this.get_start_leave_day().is_all_day_leave()) {
    number_of_days = number_of_days - 0.5;
  }

  // case when leave spreads for more then one day, then check if both start and day
  // are halfs
  else if (number_of_days > 1) {
    if ( ! this.get_start_leave_day().is_all_day_leave() ){
      number_of_days = number_of_days - 0.5;
    }
    if ( ! this.get_end_leave_day().is_all_day_leave() ) {
      number_of_days = number_of_days - 0.5;
    }
  }

  return number_of_days;
},

get_deducted_days : function(args) {

  var leave_days = [],
    ignore_allowance = false,
    leave_type = this.leave_type || args.leave_type,
    year;

  if (args && args.hasOwnProperty('ignore_allowance')) {
    ignore_allowance = args.ignore_allowance;
  }

  if (args && args.hasOwnProperty('year')) {
    year = moment(args.year, 'YYYY');
  }

  // If current Leave stands for type that does not use
  // allowance, ignore rest of the code;
  if (! ignore_allowance && !leave_type.use_allowance) return leave_days;

  var user = this.approver || this.user || args.user;

  var bank_holiday_map = {};

  user.company.bank_holidays.forEach(function(bank_holiday){
    bank_holiday_map[ bank_holiday.get_pretty_date() ] = 1;
  });

  leave_days = _.filter(
    _.map(this.get_days(), function(leave_day){

      // Ignore bank holidays
      if ( bank_holiday_map[ leave_day.get_pretty_date() ] ) return;

      // If it happenned that current leave day is from the year current
      // call was made of, ignore that day
      if (year && year.year() !== moment(leave_day.date).year()) return;

      // Ignore weekends
      //
      // Here 6 and 0 are moment's day() indexes for
      // weekend days
      if ( moment(leave_day.date).day() === 6
        || moment(leave_day.date).day() === 0
      ) return;

      return leave_day;
    }),
    function(leave_day){
      return !! leave_day;
    }
  ) || [];

  return leave_days;
}, // End get_deducted_days

promise_to_reject : function() {
  // See explanation to promise_to_approve
  this.status = this.is_pended_revoke_leave() ?
    Leave.status_approved():
    Leave.status_rejected();
  return this.save();
},

promise_to_approve : function() {
  // If current leave is one with requested revoke, then
  // approve action set it into Rejected status
  // otherwise it is approve action for new leave
  // so put leave into Approved
  this.status = this.is_pended_revoke_leave() ?
    Leave.status_rejected():
    Leave.status_approved();
  return this.save();
},

promise_to_revoke : function(){
  var self = this;

  return self.getUser({
      include : [
        {
          model : sequelize.models.Department,
          as    : 'department',
        }
      ],
    })
    .then(function(user){
      self.approverId = user.department.bossId;
      self.status = Leave.status_pended_revoke();

      return self.save();
    })
},

get_leave_type_name : function() {
  var leave_type = this.get('leave_type');

  if (! leave_type ) {
    return '';
  } else {
    return leave_type.name;
  }
},

promise_approver : function() {

  return this.getApprover({
    include : [{
      model : sequelize.models.Company,
      as : 'company',
      include : [{
        model : sequelize.models.BankHoliday,
        as : 'bank_holidays',
      }],
    }],
  })
},

        },
    });

    return Leave;
};
<|endoftext|>


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

/**
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  var NodeIO = require( 'SCENERY/nodes/NodeIO' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );
  var sun = require( 'SUN/sun' );

  /**
   * Wrapper type for phet/sun's RadioButton class.
   * @param {RadioButton} radioButton
   * @param {String} phetioID
   * @constructor
   */
  function RadioButtonIO( radioButton, phetioID ) {
    assert && assertInstanceOf( radioButton, phet.sun.RadioButton );
    NodeIO.call( this, radioButton, phetioID );
  }

  phetioInherit( NodeIO, 'RadioButtonIO', RadioButtonIO, {}, {
    documentation: 'A traditional radio button',
    events: [ 'fired' ],
    toStateObject: function( radioButton ) {
      assert && assertInstanceOf( radioButton, phet.sun.RadioButton );
      return NodeIO.toStateObject( radioButton );
    },
    fromStateObject: function( stateObject ) { return NodeIO.fromStateObject( stateObject ); },
    setValue: function( instance, stateObject ) {
      // assert && assertInstanceOf( radioButton, phet.sun.RadioButton );
      NodeIO.setValue( instance, stateObject );
    }
  } );

  sun.register( 'RadioButtonIO', RadioButtonIO );

  return RadioButtonIO;
} );

<commit_msg>Fixed radio button vars<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  var NodeIO = require( 'SCENERY/nodes/NodeIO' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );
  var sun = require( 'SUN/sun' );

  /**
   * Wrapper type for phet/sun's RadioButton class.
   * @param {RadioButton} radioButton
   * @param {String} phetioID
   * @constructor
   */
  function RadioButtonIO( radioButton, phetioID ) {
    assert && assertInstanceOf( radioButton, phet.sun.RadioButton );
    NodeIO.call( this, radioButton, phetioID );
  }

  phetioInherit( NodeIO, 'RadioButtonIO', RadioButtonIO, {}, {
    documentation: 'A traditional radio button',
    events: [ 'fired' ],
    toStateObject: function( radioButton ) {
      assert && assertInstanceOf( radioButton, phet.sun.RadioButton );
      return NodeIO.toStateObject( radioButton );
    },
    fromStateObject: function( stateObject ) { return NodeIO.fromStateObject( stateObject ); },
    setValue: function( radioButton, stateObject ) {
      assert && assertInstanceOf( radioButton, phet.sun.RadioButton );
      NodeIO.setValue( radioButton, stateObject );
    }
  } );

  sun.register( 'RadioButtonIO', RadioButtonIO );

  return RadioButtonIO;
} );

<|endoftext|>


<commit_before>var formatUSD = require('../index.js');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports.formatUSD = {
  setUp: function(done) {
    // setup here
    done();
  },
  'Format numbers w/o decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(1.25, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(1.258, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(1.25889349857, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(798127394873, {decimalPlaces: 0}), '$798,127,394,873' );
    test.equal( formatUSD(000423, {decimalPlaces: 0}), '$275' );
    test.done();
  },
  'Format numbers w/ one decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1, {decimalPlaces: 1}), '$1.0' );
    test.equal( formatUSD(1.25, {decimalPlaces: 1}), '$1.3' );
    test.equal( formatUSD(1.258, {decimalPlaces: 1}), '$1.3' );
    test.equal( formatUSD(1.25889349857, {decimalPlaces: 1}), '$1.3' );
    test.equal( formatUSD(798127394873, {decimalPlaces: 1}), '$798,127,394,873.0' );
    test.equal( formatUSD(000423, {decimalPlaces: 1}), '$275.0' );
    test.done();
  },
  'Format numbers w/ two decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1), '$1.00' );
    test.equal( formatUSD(1.25), '$1.25' );
    test.equal( formatUSD(1.258), '$1.26' );
    test.equal( formatUSD(1.25889349857), '$1.26' );
    test.equal( formatUSD(798127394873), '$798,127,394,873.00' );
    test.equal( formatUSD(000423), '$275.00' );
    test.done();
  },
  'Format numbers w/ three decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1, {decimalPlaces: 3}), '$1.000' );
    test.equal( formatUSD(1.25, {decimalPlaces: 3}), '$1.250' );
    test.equal( formatUSD(1.258, {decimalPlaces: 3}), '$1.258' );
    test.equal( formatUSD(1.25889349857, {decimalPlaces: 3}), '$1.259' );
    test.equal( formatUSD(798127394873, {decimalPlaces: 3}), '$798,127,394,873.000' );
    test.equal( formatUSD(000423, {decimalPlaces: 3}), '$275.000' );
    test.done();
  },
  'Format strings by removing non-numeric characters': function(test) {
    test.expect(6);
    test.equal( formatUSD("foo99", {decimalPlaces: 0}), '$99' );
    test.equal( formatUSD("--??!!1,2,3,4,5,6,7", {decimalPlaces: 0}), '$1,234,567' );
    test.equal( formatUSD("zero", {decimalPlaces: 0}), '$0' );
  }
};
<commit_msg>Woops, test stuff fixed.<commit_after>var formatUSD = require('../index.js');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports.formatUSD = {
  setUp: function(done) {
    // setup here
    done();
  },
  'Format numbers w/o decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(1.25, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(1.258, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(1.25889349857, {decimalPlaces: 0}), '$1' );
    test.equal( formatUSD(798127394873, {decimalPlaces: 0}), '$798,127,394,873' );
    test.equal( formatUSD(000423, {decimalPlaces: 0}), '$275' );
    test.done();
  },
  'Format numbers w/ one decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1, {decimalPlaces: 1}), '$1.0' );
    test.equal( formatUSD(1.25, {decimalPlaces: 1}), '$1.3' );
    test.equal( formatUSD(1.258, {decimalPlaces: 1}), '$1.3' );
    test.equal( formatUSD(1.25889349857, {decimalPlaces: 1}), '$1.3' );
    test.equal( formatUSD(798127394873, {decimalPlaces: 1}), '$798,127,394,873.0' );
    test.equal( formatUSD(000423, {decimalPlaces: 1}), '$275.0' );
    test.done();
  },
  'Format numbers w/ two decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1), '$1.00' );
    test.equal( formatUSD(1.25), '$1.25' );
    test.equal( formatUSD(1.258), '$1.26' );
    test.equal( formatUSD(1.25889349857), '$1.26' );
    test.equal( formatUSD(798127394873), '$798,127,394,873.00' );
    test.equal( formatUSD(000423), '$275.00' );
    test.done();
  },
  'Format numbers w/ three decimal places': function(test) {
    test.expect(6);
    // tests here
    test.equal( formatUSD(1, {decimalPlaces: 3}), '$1.000' );
    test.equal( formatUSD(1.25, {decimalPlaces: 3}), '$1.250' );
    test.equal( formatUSD(1.258, {decimalPlaces: 3}), '$1.258' );
    test.equal( formatUSD(1.25889349857, {decimalPlaces: 3}), '$1.259' );
    test.equal( formatUSD(798127394873, {decimalPlaces: 3}), '$798,127,394,873.000' );
    test.equal( formatUSD(000423, {decimalPlaces: 3}), '$275.000' );
    test.done();
  },
  'Format strings by removing non-numeric characters': function(test) {
    test.expect(3);
    test.equal( formatUSD("foo99", {decimalPlaces: 0}), '$99' );
    test.equal( formatUSD("--??!!1,2,3,4,5,6,7", {decimalPlaces: 0}), '$1,234,567' );
    test.equal( formatUSD("zero", {decimalPlaces: 0}), '$0' );
    test.done();
  }
};
<|endoftext|>