<commit_before>
/* A component that renders a grid of whole-days that runs horizontally. There can be multiple rows, one per week.
----------------------------------------------------------------------------------------------------------------------*/

var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {

	numbersVisible: false, // should render a row for day/week numbers? set by outside view. TODO: make internal
	bottomCoordPadding: 0, // hack for extending the hit area for the last row of the coordinate grid

	rowEls: null, // set of fake row elements
	cellEls: null, // set of whole-day elements comprising the row's background
	helperEls: null, // set of cell skeleton elements for rendering the mock event "helper"

	rowCoordCache: null,
	colCoordCache: null,

	// Renders the rows and columns into the component's `this.el`, which should already be assigned.
	// isRigid determins whether the individual rows should ignore the contents and be a constant height.
	// Relies on the view's colCnt and rowCnt. In the future, this component should probably be self-sufficient.
	renderDates: function(isRigid) {
		var view = this.view;
		var rowCnt = this.rowCnt;
		var colCnt = this.colCnt;
		var html = '';
		var row;
		var col;

		for (row = 0; row < rowCnt; row++) {
			html += this.renderDayRowHtml(row, isRigid);
		}
		this.el.html(html);

		this.rowEls = this.el.find('.fc-row');
		this.cellEls = this.el.find('.fc-day');

		this.rowCoordCache = new CoordCache({
			els: this.rowEls,
			isVertical: true
		});
		this.colCoordCache = new CoordCache({
			els: this.cellEls.slice(0, this.colCnt), // only the first row
			isHorizontal: true
		});

		// trigger dayRender with each cell's element
		for (row = 0; row < rowCnt; row++) {
			for (col = 0; col < colCnt; col++) {
				view.trigger(
					'dayRender',
					null,
					this.getCellDate(row, col),
					this.getCellEl(row, col)
				);
			}
		}
	},

	unrenderDates: function() {
		this.removeSegPopover();
	},

	renderBusinessHours: function() {
		var segs = this.buildBusinessHourSegs(true); // wholeDay=true
		this.renderFill('businessHours', segs, 'bgevent');
	},

	unrenderBusinessHours: function() {
		this.unrenderFill('businessHours');
	},

	// Generates the HTML for a single row, which is a div that wraps a table.
	// `row` is the row number.
	renderDayRowHtml: function(row, isRigid) {
		var view = this.view;
		var classes = [ 'fc-row', 'fc-week', view.widgetContentClass ];

		if (isRigid) {
			classes.push('fc-rigid');
		}

		return '' +
			'
' +
				'
' +
					'
' +
						this.renderBgTrHtml(row) +
					'
' +
				'
' +
				'
' +
					'
' +
						(this.numbersVisible ?
							'
' +
								this.renderNumberTrHtml(row) +
							'
' :
							''
							) +
					'
' +
				'
' +
			'
';
	},

	/* Grid Number Rendering
	------------------------------------------------------------------------------------------------------------------*/

	renderNumberTrHtml: function(row) {
		return '' +
			'
' +
				(this.isRTL ? '' : this.renderNumberIntroHtml(row)) +
				this.renderNumberCellsHtml(row) +
				(this.isRTL ? this.renderNumberIntroHtml(row) : '') +
			'
';
	},

	renderNumberIntroHtml: function(row) {
		return this.renderIntroHtml();
	},

	renderNumberCellsHtml: function(row) {
		var htmls = [];
		var col, date;

		for (col = 0; col < this.colCnt; col++) {
			date = this.getCellDate(row, col);
			htmls.push(this.renderNumberCellHtml(date));
		}

		return htmls.join('');
	},

	// Generates the HTML for the s of the "number" row in the DayGrid's content skeleton.
	// The number row will only exist if either day numbers or week numbers are turned on.
	renderNumberCellHtml: function(date) {
		var classes;

		if (!this.view.dayNumbersVisible) { // if there are week numbers but not day numbers
			return ''; //  will create an empty space above events :(
		}

		classes = this.getDayClasses(date);
		classes.unshift('fc-day-number');

		return '' +
			'' +
				date.date() +
			'	';
	},

	/* Options
	------------------------------------------------------------------------------------------------------------------*/

	// Computes a default event time formatting string if `timeFormat` is not explicitly defined
	computeEventTimeFormat: function() {
		return this.view.opt('extraSmallTimeFormat'); // like "6p" or "6:30p"
	},

	// Computes a default `displayEventEnd` value if one is not expliclty defined
	computeDisplayEventEnd: function() {
		return this.colCnt == 1; // we'll likely have space if there's only one day
	},

	/* Dates
	------------------------------------------------------------------------------------------------------------------*/

	rangeUpdated: function() {
		this.updateDayTable();
	},

	// Slices up the given span (unzoned start/end with other misc data) into an array of segments
	spanToSegs: function(span) {
		var segs = this.sliceRangeByRow(span);
		var i, seg;

		for (i = 0; i < segs.length; i++) {
			seg = segs[i];
			if (this.isRTL) {
				seg.leftCol = this.daysPerRow - 1 - seg.lastRowDayIndex;
				seg.rightCol = this.daysPerRow - 1 - seg.firstRowDayIndex;
			}
			else {
				seg.leftCol = seg.firstRowDayIndex;
				seg.rightCol = seg.lastRowDayIndex;
			}
		}

		return segs;
	},

	/* Hit System
	------------------------------------------------------------------------------------------------------------------*/

	prepareHits: function() {
		this.colCoordCache.build();
		this.rowCoordCache.build();
		this.rowCoordCache.bottoms[this.rowCnt - 1] += this.bottomCoordPadding; // hack
	},

	releaseHits: function() {
		this.colCoordCache.clear();
		this.rowCoordCache.clear();
	},

	queryHit: function(leftOffset, topOffset) {
		if (this.colCoordCache.isLeftInBounds(leftOffset) && this.rowCoordCache.isTopInBounds(topOffset)) {
			var col = this.colCoordCache.getHorizontalIndex(leftOffset);
			var row = this.rowCoordCache.getVerticalIndex(topOffset);

			if (row != null && col != null) {
				return this.getCellHit(row, col);
			}
		}
	},

	getHitSpan: function(hit) {
		return this.getCellRange(hit.row, hit.col);
	},

	getHitEl: function(hit) {
		return this.getCellEl(hit.row, hit.col);
	},

	/* Cell System
	------------------------------------------------------------------------------------------------------------------*/
	// FYI: the first column is the leftmost column, regardless of date

	getCellHit: function(row, col) {
		return {
			row: row,
			col: col,
			component: this, // needed unfortunately :(
			left: this.colCoordCache.getLeftOffset(col),
			right: this.colCoordCache.getRightOffset(col),
			top: this.rowCoordCache.getTopOffset(row),
			bottom: this.rowCoordCache.getBottomOffset(row)
		};
	},

	getCellEl: function(row, col) {
		return this.cellEls.eq(row * this.colCnt + col);
	},

	/* Event Drag Visualization
	------------------------------------------------------------------------------------------------------------------*/
	// TODO: move to DayGrid.event, similar to what we did with Grid's drag methods

	// Renders a visual indication of an event or external element being dragged.
	// `eventLocation` has zoned start and end (optional)
	renderDrag: function(eventLocation, seg) {

		// always render a highlight underneath
		this.renderHighlight(this.eventToSpan(eventLocation));

		// if a segment from the same calendar but another component is being dragged, render a helper event
		if (seg.component !== this) {
			return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
		}
	},

	// Unrenders any visual indication of a hovering event
	unrenderDrag: function() {
		this.unrenderHighlight();
		this.unrenderHelper();
	},

	/* Event Resize Visualization
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a visual indication of an event being resized
	renderEventResize: function(eventLocation, seg) {
		this.renderHighlight(this.eventToSpan(eventLocation));
		return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
	},

	// Unrenders a visual indication of an event being resized
	unrenderEventResize: function() {
		this.unrenderHighlight();
		this.unrenderHelper();
	},

	/* Event Helper
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null.
	renderHelper: function(event, sourceSeg) {
		var helperNodes = [];
		var segs = this.eventToSegs(event);
		var rowStructs;

		segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered
		rowStructs = this.renderSegRows(segs);

		// inject each new event skeleton into each associated row
		this.rowEls.each(function(row, rowNode) {
			var rowEl = $(rowNode); // the .fc-row
			var skeletonEl = $('

'); // will be absolutely positioned
			var skeletonTop;

			// If there is an original segment, match the top position. Otherwise, put it at the row's top level
			if (sourceSeg && sourceSeg.row === row) {
				skeletonTop = sourceSeg.el.position().top;
			}
			else {
				skeletonTop = rowEl.find('.fc-content-skeleton tbody').position().top;
			}

			skeletonEl.css('top', skeletonTop)
				.find('table')
					.append(rowStructs[row].tbodyEl);

			rowEl.append(skeletonEl);
			helperNodes.push(skeletonEl[0]);
		});

		return ( // must return the elements rendered
			this.helperEls = $(helperNodes) // array -> jQuery set
		);
	},

	// Unrenders any visual indication of a mock helper event
	unrenderHelper: function() {
		if (this.helperEls) {
			this.helperEls.remove();
			this.helperEls = null;
		}
	},

	/* Fill System (highlight, background events, business hours)
	------------------------------------------------------------------------------------------------------------------*/

	fillSegTag: 'td', // override the default tag name

	// Renders a set of rectangles over the given segments of days.
	// Only returns segments that successfully rendered.
	renderFill: function(type, segs, className) {
		var nodes = [];
		var i, seg;
		var skeletonEl;

		segs = this.renderFillSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs

		for (i = 0; i < segs.length; i++) {
			seg = segs[i];
			skeletonEl = this.renderFillRow(type, seg, className);
			this.rowEls.eq(seg.row).append(skeletonEl);
			nodes.push(skeletonEl[0]);
		}

		this.elsByFill[type] = $(nodes);

		return segs;
	},

	// Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered.
	renderFillRow: function(type, seg, className) {
		var colCnt = this.colCnt;
		var startCol = seg.leftCol;
		var endCol = seg.rightCol + 1;
		var skeletonEl;
		var trEl;

		className = className || type.toLowerCase();

		skeletonEl = $(
			'
' +
				'

' +
			'
'
		);
		trEl = skeletonEl.find('tr');

		if (startCol > 0) {
			trEl.append('');
		}

		trEl.append(
			seg.el.attr('colspan', endCol - startCol)
		);

		if (endCol < colCnt) {
			trEl.append('');
		}

		this.bookendCells(trEl);

		return skeletonEl;
	}

});
<commit_msg>fix error<commit_after>
/* A component that renders a grid of whole-days that runs horizontally. There can be multiple rows, one per week.
----------------------------------------------------------------------------------------------------------------------*/

var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {

	numbersVisible: false, // should render a row for day/week numbers? set by outside view. TODO: make internal
	bottomCoordPadding: 0, // hack for extending the hit area for the last row of the coordinate grid

	rowEls: null, // set of fake row elements
	cellEls: null, // set of whole-day elements comprising the row's background
	helperEls: null, // set of cell skeleton elements for rendering the mock event "helper"

	rowCoordCache: null,
	colCoordCache: null,

	// Renders the rows and columns into the component's `this.el`, which should already be assigned.
	// isRigid determins whether the individual rows should ignore the contents and be a constant height.
	// Relies on the view's colCnt and rowCnt. In the future, this component should probably be self-sufficient.
	renderDates: function(isRigid) {
		var view = this.view;
		var rowCnt = this.rowCnt;
		var colCnt = this.colCnt;
		var html = '';
		var row;
		var col;

		for (row = 0; row < rowCnt; row++) {
			html += this.renderDayRowHtml(row, isRigid);
		}
		this.el.html(html);

		this.rowEls = this.el.find('.fc-row');
		this.cellEls = this.el.find('.fc-day');

		this.rowCoordCache = new CoordCache({
			els: this.rowEls,
			isVertical: true
		});
		this.colCoordCache = new CoordCache({
			els: this.cellEls.slice(0, this.colCnt), // only the first row
			isHorizontal: true
		});

		// trigger dayRender with each cell's element
		for (row = 0; row < rowCnt; row++) {
			for (col = 0; col < colCnt; col++) {
				view.trigger(
					'dayRender',
					null,
					this.getCellDate(row, col),
					this.getCellEl(row, col)
				);
			}
		}
	},

	unrenderDates: function() {
		this.removeSegPopover();
	},

	renderBusinessHours: function() {
		var segs = this.buildBusinessHourSegs(true); // wholeDay=true
		this.renderFill('businessHours', segs, 'bgevent');
	},

	unrenderBusinessHours: function() {
		this.unrenderFill('businessHours');
	},

	// Generates the HTML for a single row, which is a div that wraps a table.
	// `row` is the row number.
	renderDayRowHtml: function(row, isRigid) {
		var view = this.view;
		var classes = [ 'fc-row', 'fc-week', view.widgetContentClass ];

		if (isRigid) {
			classes.push('fc-rigid');
		}

		return '' +
			'
' +
				'
' +
					'
' +
						this.renderBgTrHtml(row) +
					'
' +
				'
' +
				'
' +
					'
' +
						(this.numbersVisible ?
							'
' +
								this.renderNumberTrHtml(row) +
							'
' :
							''
							) +
					'
' +
				'
' +
			'
';
	},

	/* Grid Number Rendering
	------------------------------------------------------------------------------------------------------------------*/

	renderNumberTrHtml: function(row) {
		return '' +
			'
' +
				(this.isRTL ? '' : this.renderNumberIntroHtml(row)) +
				this.renderNumberCellsHtml(row) +
				(this.isRTL ? this.renderNumberIntroHtml(row) : '') +
			'
';
	},

	renderNumberIntroHtml: function(row) {
		return this.renderIntroHtml();
	},

	renderNumberCellsHtml: function(row) {
		var htmls = [];
		var col, date;

		for (col = 0; col < this.colCnt; col++) {
			date = this.getCellDate(row, col);
			htmls.push(this.renderNumberCellHtml(date));
		}

		return htmls.join('');
	},

	// Generates the HTML for the s of the "number" row in the DayGrid's content skeleton.
	// The number row will only exist if either day numbers or week numbers are turned on.
	renderNumberCellHtml: function(date) {
		var classes;

		if (!this.view.dayNumbersVisible) { // if there are week numbers but not day numbers
			return ''; //  will create an empty space above events :(
		}

		classes = this.getDayClasses(date);
		classes.unshift('fc-day-number');

		return '' +
			'' +
				date.date() +
			'	';
	},

	/* Options
	------------------------------------------------------------------------------------------------------------------*/

	// Computes a default event time formatting string if `timeFormat` is not explicitly defined
	computeEventTimeFormat: function() {
		return this.view.opt('extraSmallTimeFormat'); // like "6p" or "6:30p"
	},

	// Computes a default `displayEventEnd` value if one is not expliclty defined
	computeDisplayEventEnd: function() {
		return this.colCnt == 1; // we'll likely have space if there's only one day
	},

	/* Dates
	------------------------------------------------------------------------------------------------------------------*/

	rangeUpdated: function() {
		this.updateDayTable();
	},

	// Slices up the given span (unzoned start/end with other misc data) into an array of segments
	spanToSegs: function(span) {
		var segs = this.sliceRangeByRow(span);
		var i, seg;

		for (i = 0; i < segs.length; i++) {
			seg = segs[i];
			if (this.isRTL) {
				seg.leftCol = this.daysPerRow - 1 - seg.lastRowDayIndex;
				seg.rightCol = this.daysPerRow - 1 - seg.firstRowDayIndex;
			}
			else {
				seg.leftCol = seg.firstRowDayIndex;
				seg.rightCol = seg.lastRowDayIndex;
			}
		}

		return segs;
	},

	/* Hit System
	------------------------------------------------------------------------------------------------------------------*/

	prepareHits: function() {
		this.colCoordCache.build();
		this.rowCoordCache.build();
		this.rowCoordCache.bottoms[this.rowCnt - 1] += this.bottomCoordPadding; // hack
	},

	releaseHits: function() {
		this.colCoordCache.clear();
		this.rowCoordCache.clear();
	},

	queryHit: function(leftOffset, topOffset) {
		if (this.colCoordCache.isLeftInBounds(leftOffset) && this.rowCoordCache.isTopInBounds(topOffset)) {
			var col = this.colCoordCache.getHorizontalIndex(leftOffset);
			var row = this.rowCoordCache.getVerticalIndex(topOffset);

			if (row != null && col != null) {
				return this.getCellHit(row, col);
			}
		}
	},

	getHitSpan: function(hit) {
		return this.getCellRange(hit.row, hit.col);
	},

	getHitEl: function(hit) {
		return this.getCellEl(hit.row, hit.col);
	},

	/* Cell System
	------------------------------------------------------------------------------------------------------------------*/
	// FYI: the first column is the leftmost column, regardless of date

	getCellHit: function(row, col) {
		return {
			row: row,
			col: col,
			component: this, // needed unfortunately :(
			left: this.colCoordCache.getLeftOffset(col),
			right: this.colCoordCache.getRightOffset(col),
			top: this.rowCoordCache.getTopOffset(row),
			bottom: this.rowCoordCache.getBottomOffset(row)
		};
	},

	getCellEl: function(row, col) {
		return this.cellEls.eq(row * this.colCnt + col);
	},

	/* Event Drag Visualization
	------------------------------------------------------------------------------------------------------------------*/
	// TODO: move to DayGrid.event, similar to what we did with Grid's drag methods

	// Renders a visual indication of an event or external element being dragged.
	// `eventLocation` has zoned start and end (optional)
	renderDrag: function(eventLocation, seg) {

		// always render a highlight underneath
		this.renderHighlight(this.eventToSpan(eventLocation));

		// if a segment from the same calendar but another component is being dragged, render a helper event
		if (seg && seg.component !== this) {
			return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
		}
	},

	// Unrenders any visual indication of a hovering event
	unrenderDrag: function() {
		this.unrenderHighlight();
		this.unrenderHelper();
	},

	/* Event Resize Visualization
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a visual indication of an event being resized
	renderEventResize: function(eventLocation, seg) {
		this.renderHighlight(this.eventToSpan(eventLocation));
		return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
	},

	// Unrenders a visual indication of an event being resized
	unrenderEventResize: function() {
		this.unrenderHighlight();
		this.unrenderHelper();
	},

	/* Event Helper
	------------------------------------------------------------------------------------------------------------------*/

	// Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null.
	renderHelper: function(event, sourceSeg) {
		var helperNodes = [];
		var segs = this.eventToSegs(event);
		var rowStructs;

		segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered
		rowStructs = this.renderSegRows(segs);

		// inject each new event skeleton into each associated row
		this.rowEls.each(function(row, rowNode) {
			var rowEl = $(rowNode); // the .fc-row
			var skeletonEl = $('

'); // will be absolutely positioned
			var skeletonTop;

			// If there is an original segment, match the top position. Otherwise, put it at the row's top level
			if (sourceSeg && sourceSeg.row === row) {
				skeletonTop = sourceSeg.el.position().top;
			}
			else {
				skeletonTop = rowEl.find('.fc-content-skeleton tbody').position().top;
			}

			skeletonEl.css('top', skeletonTop)
				.find('table')
					.append(rowStructs[row].tbodyEl);

			rowEl.append(skeletonEl);
			helperNodes.push(skeletonEl[0]);
		});

		return ( // must return the elements rendered
			this.helperEls = $(helperNodes) // array -> jQuery set
		);
	},

	// Unrenders any visual indication of a mock helper event
	unrenderHelper: function() {
		if (this.helperEls) {
			this.helperEls.remove();
			this.helperEls = null;
		}
	},

	/* Fill System (highlight, background events, business hours)
	------------------------------------------------------------------------------------------------------------------*/

	fillSegTag: 'td', // override the default tag name

	// Renders a set of rectangles over the given segments of days.
	// Only returns segments that successfully rendered.
	renderFill: function(type, segs, className) {
		var nodes = [];
		var i, seg;
		var skeletonEl;

		segs = this.renderFillSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs

		for (i = 0; i < segs.length; i++) {
			seg = segs[i];
			skeletonEl = this.renderFillRow(type, seg, className);
			this.rowEls.eq(seg.row).append(skeletonEl);
			nodes.push(skeletonEl[0]);
		}

		this.elsByFill[type] = $(nodes);

		return segs;
	},

	// Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered.
	renderFillRow: function(type, seg, className) {
		var colCnt = this.colCnt;
		var startCol = seg.leftCol;
		var endCol = seg.rightCol + 1;
		var skeletonEl;
		var trEl;

		className = className || type.toLowerCase();

		skeletonEl = $(
			'
' +
				'

' +
			'
'
		);
		trEl = skeletonEl.find('tr');

		if (startCol > 0) {
			trEl.append('');
		}

		trEl.append(
			seg.el.attr('colspan', endCol - startCol)
		);

		if (endCol < colCnt) {
			trEl.append('');
		}

		this.bookendCells(trEl);

		return skeletonEl;
	}

});
<|endoftext|>


<commit_before>var LayerModelBase = require('../../../../src/geo/map/layer-model-base');

var MyLayer = LayerModelBase.extend({});

fdescribe('geo/map/layer-model-base.js', function () {
  beforeEach(function () {
    this.layer = new MyLayer();
  });

  describe('.remove', function () {
    it('should trigger a destroy event', function () {
      var callback = jasmine.createSpy('callback');
      this.layer.bind('destroy', callback);

      this.layer.remove();

      expect(callback).toHaveBeenCalled();
    });
  });

  describe('.update', function () {
    it('should set the attributes', function () {
      this.layer.update({
        a: 1,
        b: 2
      });

      expect(this.layer.get('a')).toEqual(1);
      expect(this.layer.get('b')).toEqual(2);
    });
  });

  describe('.show', function () {
    it('should set the visible attribute to true', function () {
      this.layer.set('visible', false);

      this.layer.show();

      expect(this.layer.get('visible')).toBeTruthy();
    });
  });

  describe('.hide', function () {
    it('should set the visible attribute to false', function () {
      this.layer.set('visible', true);

      this.layer.hide();

      expect(this.layer.get('visible')).toBeFalsy();
    });
  });

  describe('.toggle', function () {
    it('should toggel the visible attribute', function () {
      this.layer.set('visible', false);

      this.layer.toggle();

      expect(this.layer.get('visible')).toBeTruthy();

      this.layer.toggle();

      expect(this.layer.get('visible')).toBeFalsy();
    });
  });
});
<commit_msg>Added some basic tests for src/geo/map/layer-model-base<commit_after>var LayerModelBase = require('../../../../src/geo/map/layer-model-base');

var MyLayer = LayerModelBase.extend({});

describe('geo/map/layer-model-base.js', function () {
  beforeEach(function () {
    this.layer = new MyLayer();
  });

  describe('.remove', function () {
    it('should trigger a destroy event', function () {
      var callback = jasmine.createSpy('callback');
      this.layer.bind('destroy', callback);

      this.layer.remove();

      expect(callback).toHaveBeenCalled();
    });
  });

  describe('.update', function () {
    it('should set the attributes', function () {
      this.layer.update({
        a: 1,
        b: 2
      });

      expect(this.layer.get('a')).toEqual(1);
      expect(this.layer.get('b')).toEqual(2);
    });
  });

  describe('.show', function () {
    it('should set the visible attribute to true', function () {
      this.layer.set('visible', false);

      this.layer.show();

      expect(this.layer.get('visible')).toBeTruthy();
    });
  });

  describe('.hide', function () {
    it('should set the visible attribute to false', function () {
      this.layer.set('visible', true);

      this.layer.hide();

      expect(this.layer.get('visible')).toBeFalsy();
    });
  });

  describe('.toggle', function () {
    it('should toggel the visible attribute', function () {
      this.layer.set('visible', false);

      this.layer.toggle();

      expect(this.layer.get('visible')).toBeTruthy();

      this.layer.toggle();

      expect(this.layer.get('visible')).toBeFalsy();
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

import end from './end';
import operator from './operator';
import singleQuoting from './single-quoting';
import doubleQuoting from './double-quoting';
import expansionStart from './expansion-start';
import expansionCommandTick from './expansion-command-tick';

import {tokenOrEmpty, newLine, isPartOfOperator, appendEmptyExpansion} from '..';

export default function start(state, char) {
	if (char === undefined) {
		return {
			nextReduction: end,
			tokensToEmit: tokenOrEmpty(state),
			nextState: {...state, current: '', expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (char === '\n' && state.escaping) {
		return {
			nextReduction: start,
			nextState: {...state, escaping: false, current: state.current.slice(0, -1)}
		};
	}

	if (state.current === '\n' || (!state.escaping && char === '\n')) {
		return {
			nextReduction: start,
			tokensToEmit: tokenOrEmpty(state).concat(newLine()),
			nextState: {...state, current: '', expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (!state.escaping && char === '\\') {
		return {
			nextReduction: start,
			nextState: {...state, current: state.current + char, escaping: true}
		};
	}

	if (!state.escaping && isPartOfOperator(char)) {
		return {
			nextReduction: operator,
			tokensToEmit: tokenOrEmpty(state),
			nextState: {...state, current: char, expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (!state.escaping && char === '\'') {
		return {
			nextReduction: singleQuoting,
			nextState: {...state, current: state.current + char}
		};
	}

	if (!state.escaping && char === '"') {
		return {
			nextReduction: doubleQuoting,
			nextState: {...state, current: state.current + char}
		};
	}

	if (!state.escaping && char.match(/\s/)) {
		return {
			nextReduction: start,
			tokensToEmit: tokenOrEmpty(state),
			nextState: {...state, current: '', expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (!state.escaping && char === '$') {
		return {
			nextReduction: expansionStart,
			nextState: {
				...state,
				current: state.current + char,
				expansion: appendEmptyExpansion(state)
			}
		};
	}

	if (!state.escaping && char === '`') {
		return {
			nextReduction: expansionCommandTick,
			nextState: {
				...state,
				current: state.current + char,
				expansion: appendEmptyExpansion(state)
			}
		};
	}

	return {
		nextReduction: start,
		nextState: {...state, escaping: false, current: state.current + char}
	};
}
<commit_msg>Removed unused files, restored skipped tests<commit_after>'use strict';

import end from './end';
import operator from './operator';
import singleQuoting from './single-quoting';
import doubleQuoting from './double-quoting';
import expansionStart from './expansion-start';
import expansionCommandTick from './expansion-command-tick';

import {tokenOrEmpty, newLine, isPartOfOperator, appendEmptyExpansion} from '..';

export default function start(state, char) {
	if (char === undefined) {
		return {
			nextReduction: end,
			tokensToEmit: tokenOrEmpty(state),
			nextState: {...state, current: '', expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (char === '\n' && state.escaping) {
		return {
			nextReduction: start,
			nextState: {...state, escaping: false, current: state.current.slice(0, -1)}
		};
	}

	if ((!state.escaping && char === '\n')) {
		return {
			nextReduction: start,
			tokensToEmit: tokenOrEmpty(state).concat(newLine()),
			nextState: {...state, current: '', expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (!state.escaping && char === '\\') {
		return {
			nextReduction: start,
			nextState: {...state, current: state.current + char, escaping: true}
		};
	}

	if (!state.escaping && isPartOfOperator(char)) {
		return {
			nextReduction: operator,
			tokensToEmit: tokenOrEmpty(state),
			nextState: {...state, current: char, expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (!state.escaping && char === '\'') {
		return {
			nextReduction: singleQuoting,
			nextState: {...state, current: state.current + char}
		};
	}

	if (!state.escaping && char === '"') {
		return {
			nextReduction: doubleQuoting,
			nextState: {...state, current: state.current + char}
		};
	}

	if (!state.escaping && char.match(/\s/)) {
		return {
			nextReduction: start,
			tokensToEmit: tokenOrEmpty(state),
			nextState: {...state, current: '', expansion: [], loc: {...state.loc, start: state.loc.current}}
		};
	}

	if (!state.escaping && char === '$') {
		return {
			nextReduction: expansionStart,
			nextState: {
				...state,
				current: state.current + char,
				expansion: appendEmptyExpansion(state)
			}
		};
	}

	if (!state.escaping && char === '`') {
		return {
			nextReduction: expansionCommandTick,
			nextState: {
				...state,
				current: state.current + char,
				expansion: appendEmptyExpansion(state)
			}
		};
	}

	return {
		nextReduction: start,
		nextState: {...state, escaping: false, current: state.current + char}
	};
}
<|endoftext|>


<commit_before>/*
 *  Draws a fuel gauge widget using raphaeljs on the element identified by id.
 *
 *  The element must have the following styles defined for it:
 *    font-size
 *    color
 *    width
 */

// Constructor
/**
 * parameters:
 *  id : the id of the DOM element to draw the fuel gauge widget on.
 *  score: a number.
 *  cutpoints: an array of numbers. Contains N+1 elements, where N is the total number of levels. The ith level's lowest score is (cutpoints[i]+1) and highest score is cutpoints[i+1]
 */
function FuelGaugeWidget (id, score, cutpoints) {

    // constants
    this.PADDING = 1; // paddings between levels, in pixel
    this.BACKGROUNDCOLOUR = "#eeeeee"; // background colour of rectangles

    this.id = id;
    this.score = score;
    this.cutpoints = cutpoints;

}

// Do the actual drawing.
FuelGaugeWidget.prototype.create = function()
{
    // Check we have all the information to draw a fuel gauge.
    var element = document.getElementById(this.id);
    var elemStyle = FuelGaugeWidget.getStyleDeclaration(element);

    var fontSize = parseInt(elemStyle.fontSize);
    var width = parseInt(elemStyle.width);
    var color = elemStyle.color;

    // missing info. Return an error.
    if (!fontSize || !width || !color || isNaN(fontSize) || isNaN(width)) {
	alert("Fuel Gauge widget: font size and width property must be defined for elementID: " + this.id);
	return;
    }

    // calculate the widths of each level rectangle
    var fullLevelRectWidth = width / (this.cutpoints.length - 1)
    fullLevelRectWidth -= this.PADDING;
    var rects = new Array();
    for (var i = 0; i < this.cutpoints.length - 1; i++) {
	if (this.score > this.cutpoints[i+1]) {
	    // higher than the ith level
	    rects[i] = fullLevelRectWidth;
	} else if (this.score > this.cutpoints[i]) {
	    // in the ith level
	    rects[i] = (this.score - this.cutpoints[i]) /
                       (this.cutpoints[i+1] - this.cutpoints[i]) *
		       fullLevelRectWidth;
	} else {
	    // lower than the ith level
	}
    }

    // Now call raphael.
    this.paper = Raphael(element, width, fontSize);
    // draw background first
    for (var i = 0; i < this.cutpoints.length - 1; i++) {
	this.paper.rect(i * (fullLevelRectWidth+1),0, fullLevelRectWidth, fontSize, 0)
                  .attr("fill", this.BACKGROUNDCOLOUR)
                  .attr("stroke", "none");
    }
    // draw actual rectangle
    for (var i = 0; i < rects.length; i++) {
	this.paper.rect(i * (fullLevelRectWidth+1),0, rects[i], fontSize, 1)
                  .attr("fill", color)
                  .attr("stroke", "none");
    }

};

// --- static helper function ---
// Gets the style object for the element where we're drawing the fuel gauge.
// Returns a CSSStyleDeclaration object
FuelGaugeWidget.getStyleDeclaration = function (element)
{
    if (window.getComputedStyle) {
        var compStyle = window.getComputedStyle (element, null);
    } else {
	var compStyle = button.currentStyle;
    }
    return compStyle;
};

<commit_msg>Tweaks to fuel gauge widget code.<commit_after>/*
 *  Draws a fuel gauge widget using raphaeljs on the element identified by id.
 *
 *  The element must have the following styles defined for it:
 *    font-size
 *    color
 *    width
 */

// Constructor
/**
 * parameters:
 *  id : the id of the DOM element to draw the fuel gauge widget on.
 *  score: a number.
 *  cutpoints: an array of numbers. Contains N+1 elements, where N is the total number of levels. The ith level's lowest score is (cutpoints[i]+1) and highest score is cutpoints[i+1]
 */
function FuelGaugeWidget (id, score, cutpoints) {

    // constants
    this.PADDING = 1; // paddings between levels, in pixel
    this.BACKGROUNDCOLOUR = "#eeeeee"; // background colour of rectangles

    this.id = id;
    this.score = score;
    this.cutpoints = cutpoints;

}

// Do the actual drawing.
FuelGaugeWidget.prototype.create = function()
{
    // Check we have all the information to draw a fuel gauge.
    var element = document.getElementById(this.id);
    var elemStyle = FuelGaugeWidget.getStyleDeclaration(element);

    var fontSize = parseInt(elemStyle.fontSize);
    var width = parseInt(elemStyle.width);
    var color = elemStyle.color;

    // missing info. Return an error.
    if (!fontSize || !width || !color || isNaN(fontSize) || isNaN(width)) {
	alert("Fuel Gauge widget: font size and width property must be defined for elementID: " + this.id);
	return;
    }

    // calculate the widths of each level rectangle
    var fullLevelRectWidth = width / (this.cutpoints.length - 1)
    fullLevelRectWidth -= this.PADDING;
    var rects = new Array();
    for (var i = 0; i < this.cutpoints.length - 1; i++) {
	if (this.score > this.cutpoints[i+1]) {
	    // higher than the ith level
	    rects[i] = fullLevelRectWidth;
	} else if (this.score > this.cutpoints[i]) {
	    // in the ith level
	    rects[i] = (this.score - this.cutpoints[i]) /
                       (this.cutpoints[i+1] - this.cutpoints[i]) *
		       fullLevelRectWidth;
	} else {
	    // lower than the ith level
	}
    }

    // Now call raphael.
    this.paper = Raphael(element, width, fontSize);
    // draw background first
    for (var i = 0; i < this.cutpoints.length - 1; i++) {
	this.paper.rect(i * (fullLevelRectWidth+this.PADDING),0, fullLevelRectWidth, fontSize, 0)
                  .attr("fill", this.BACKGROUNDCOLOUR)
                  .attr("stroke", "none");
    }
    // draw actual rectangle
    for (var i = 0; i < rects.length; i++) {
	this.paper.rect(i * (fullLevelRectWidth+this.PADDING),0, rects[i], fontSize, 1)
                  .attr("fill", color)
                  .attr("stroke", "none");
    }

};

// --- static helper function ---
// Gets the style object for the element where we're drawing the fuel gauge.
// Returns a CSSStyleDeclaration object
FuelGaugeWidget.getStyleDeclaration = function (element)
{
    if (window.getComputedStyle) {
        var compStyle = window.getComputedStyle (element, null);
    } else {
	var compStyle = button.currentStyle;
    }
    return compStyle;
};

<|endoftext|>


<commit_before>var w;
var toggle =1;
var lat;
var lon;
var ip;

// weather wgt
$(document).ready(function() {
	setInterval(function(){StartProcess()},3000);
	$( "#temp" ).bind( "click", togglet);

	$('#getgeo').click(function(){
				/* Does your browser support geolocation? */
if ("geolocation" in navigator) {
  navigator.geolocation.getCurrentPosition(function(position) {
  lat = position.coords.latitude;
			lon=position.coords.longitude;
getData(lat,lon);
},errorPosition);
} else {
  errorPosition();
}

});
});

// use ip if position is not granted
function errorPosition(){
	$.getJSON('//freegeoip.net/json/?callback=?', function(data) {
  lat = data.latitude;
	lon = data.longitude;
	getData(lat,lon);
});

}

function getData(lat,lon){
	if(toggle==1)var unit = 'c';
	else var unit = 'f';
			  $.simpleWeather({
    location: lat+','+lon,
    woeid: '',
    unit: unit,
    success: function(weather) {
			w = weather;
			$('#temp').html('
 '+weather.temp+'°'+weather.units.temp+'
');
			$('.currently').html(weather.city);
			var lis= $( ".next li" );
			for(var i=0; i<4;i++){

					var string =""+w.forecast[i].day+"
 MAX "+w.forecast[i].high+"
 MIN "+w.forecast[i].low;
					lis.eq(i).html(string);

			}
				var pos = lat+","+lon;
				showPosition(pos);
    },
    error: function(error) {
      $("#weather").html('
'+error+'
');
    }
  });
}

// toggle units
function togglet(){

if(toggle == 0){
	toggle =1;
	$( "#temp" ).html('
 '+w.temp+'°C
');
	getData(lat,lon);
}else{
	toggle =0;
	$( "#temp" ).html('
 '+w.alt.temp+'°F
');
	getData(lat,lon);
}
}
function showPosition(position) {

    var img_url = "https://maps.googleapis.com/maps/api/staticmap?center="
    +position+"&zoom=14&size=400x300&sensor=false";
    document.getElementById("mapholder").innerHTML = "";
}

function StartProcess(){
	if ("geolocation" in navigator) {
  navigator.geolocation.getCurrentPosition(function(position) {
  lat = position.coords.latitude;
			lon=position.coords.longitude;
getData(lat,lon);
},errorPosition);
} else {
  errorPosition();
}

}<commit_msg>Delete index.js<commit_after><|endoftext|>


<commit_before>/**
 *
 * Initilize TinyMCE editor with all required options
 */

// TinyMCE DomQuery variable
let dom = tinymce.dom.DomQuery

// Invisible space constant
const ZERO_SPACE = ''
const RAJE_SELECTOR = 'body#tinymce'

const DISABLE_SELECTOR_FIGURES = 'figure *, h1, h2, h3, h4, h5, h6'
const HEADER_SELECTOR = 'header.page-header.container.cgen.mceNonEditable'
const FIRST_HEADING = `${RAJE_SELECTOR}>section:first>h1:first`

$(document).ready(function () {
  //hide footer
  $('footer.footer').hide()

  //attach whole body inside a placeholder div
  $('body').html(`<div id="raje_root">${$('body').html()}</div>`)

  $('header.page-header').addClass('mceNonEditable')

  tinymce.init({

    // Select the element to wrap
    selector: '#raje_root',

    // Set the styles of the content wrapped inside the element
    content_css: ['css/bootstrap.min.css', 'css/rash.css', 'css/rajemce.css'],

    // Set plugins
    plugins: "raje_inlineFigure fullscreen link codesample raje_inlineCode raje_inlineQuote raje_section table image noneditable raje_figure raje_table raje_listing raje_formula raje_crossref raje_footnotes paste lists",

    // Remove menubar
    menubar: false,

    // Custom toolbar
    toolbar: 'undo redo bold italic link superscript subscript raje_inlineCode raje_inlineQuote raje_inlineFigure raje_crossref raje_footnotes | numlist bullist codesample blockquote raje_table raje_figure raje_listing raje_formula | raje_section',

    // Setup full screen on init
    setup: function (editor) {

      // Set fullscreen
      editor.on('init', function (e) {

        editor.execCommand('mceFullScreen')

        // Move caret at the first h1 element of main section
        // Or right after heading
        tinymce.activeEditor.selection.setCursorLocation(tinymce.activeEditor.dom.select(FIRST_HEADING)[0], 0)
      })

      editor.on('keyDown', function (e) {

        // Prevent shift+enter
        if (e.keyCode == 13 && e.shiftKey) {
          e.preventDefault()
        }
      })

      // Prevent span
      editor.on('nodeChange', function (e) {

        let selectedElement = $(tinymce.activeEditor.selection.getNode())

        // Move caret to first heading if is after or before not editable header
        if (selectedElement.is('p') && (selectedElement.next().is(HEADER_SELECTOR) || (selectedElement.prev().is(HEADER_SELECTOR) && tinymce.activeEditor.dom.select(FIRST_HEADING).length)))
          tinymce.activeEditor.selection.setCursorLocation(tinymce.activeEditor.dom.select(FIRST_HEADING)[0], 0)

        // If the current element isn't inside header, only in section this is permitted
        if (selectedElement.parents('section').length) {

          if (selectedElement.is('span#_mce_caret[data-mce-bogus]') || selectedElement.parent().is('span#_mce_caret[data-mce-bogus]')) {

            // Remove span normally created with bold
            if (selectedElement.parent().is('span#_mce_caret[data-mce-bogus]'))
              selectedElement = selectedElement.parent()

            let bm = tinymce.activeEditor.selection.getBookmark()
            selectedElement.replaceWith(selectedElement.html())
            tinymce.activeEditor.selection.moveToBookmark(bm)
          }
        }

      })

      // Update saved content on undo and redo events
      editor.on('Undo', function (e) {
        tinymce.triggerSave()
      })

      editor.on('Redo', function (e) {
        tinymce.triggerSave()
      })
    },

    // Set default target
    default_link_target: "_blank",

    // Prepend protocol if the link starts with www
    link_assume_external_targets: true,

    // Hide target list
    target_list: false,

    // Hide title
    link_title: false,

    // Set formats
    formats: {
      inline_quote: {
        inline: 'q'
      }
    },

    // Remove "powered by tinymce"
    branding: false,

    // Prevent auto br on element insert
    apply_source_formatting: false,

    // Prevent non editable object resize
    object_resizing: false,

    // Update the table popover layout
    table_toolbar: "tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol",

    image_advtab: true,

    paste_block_drop: true,

    extended_valid_elements: "span[data-mathml,contenteditable],a[class|name|href|target|title|onclick|rel],script[type|src],iframe[src|style|width|height|scrolling|marginwidth|marginheight|frameborder],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],$elements",
  })
})

/**
 * Update content in the iframe, with the one stored by tinymce
 * And save/restore the selection
 */
function updateIframeFromSavedContent() {

  // Save the bookmark
  let bookmark = tinymce.activeEditor.selection.getBookmark(2, true)

  // Update iframe content
  tinymce.activeEditor.setContent($('#raje_root').html())

  // Restore the bookmark
  tinymce.activeEditor.selection.moveToBookmark(bookmark)
}

/**
 * Accept a js object that exists in frame
 * @param {*} element
 */
function moveCaret(element, toStart) {
  tinymce.activeEditor.selection.select(element)
  tinymce.activeEditor.selection.collapse(toStart)
}

function moveCursorToEnd(element) {

  let heading = element
  let offset = 0

  if (heading.contents().length) {

    heading = heading.contents().last()
    offset = heading[0].wholeText.length
  }

  tinymce.activeEditor.focus()
  tinymce.activeEditor.selection.setCursorLocation(heading[0], offset)
}

/**
 * Create custom into notification
 * @param {*} text
 * @param {*} timeout
 */
function notify(text, type, timeout) {

  // Display only one notification, blocking all others
  if (tinymce.activeEditor.notificationManager.getNotifications().length == 0) {

    let notify = {
      text: text,
      type: type ? type : 'info',
      timeout: timeout ? timeout : 1000
    }

    tinymce.activeEditor.notificationManager.open(notify)
  }
}

function scrollTo(elementSelector) {
  $(tinymce.activeEditor.getBody()).find(elementSelector).get(0).scrollIntoView();
}

function headingDimension() {
  $('h1,h2,h3,h4,h5,h6').each(function () {

    if (!$(this).parents(HEADER_SELECTOR).length) {
      var counter = 0;
      $(this).parents("section").each(function () {
        if ($(this).children("h1,h2,h3,h4,h5,h6").length > 0) {
          counter++;
        }
      });
      $(this).replaceWith("<h" + counter + ">" + $(this).html() + "</h" + counter + ">")
    }
  });
}<commit_msg>Open and close menu dropdown on window loaded<commit_after>/**
 *
 * Initilize TinyMCE editor with all required options
 */

// TinyMCE DomQuery variable
let dom = tinymce.dom.DomQuery

// Invisible space constant
const ZERO_SPACE = ''
const RAJE_SELECTOR = 'body#tinymce'

const DISABLE_SELECTOR_FIGURES = 'figure *, h1, h2, h3, h4, h5, h6'
const HEADER_SELECTOR = 'header.page-header.container.cgen.mceNonEditable'
const FIRST_HEADING = `${RAJE_SELECTOR}>section:first>h1:first`

$(document).ready(function () {
  //hide footer
  $('footer.footer').hide()

  //attach whole body inside a placeholder div
  $('body').html(`<div id="raje_root">${$('body').html()}</div>`)

  $('header.page-header').addClass('mceNonEditable')

  tinymce.init({

    // Select the element to wrap
    selector: '#raje_root',

    // Set the styles of the content wrapped inside the element
    content_css: ['css/bootstrap.min.css', 'css/rash.css', 'css/rajemce.css'],

    // Set plugins
    plugins: "raje_inlineFigure fullscreen link codesample raje_inlineCode raje_inlineQuote raje_section table image noneditable raje_figure raje_table raje_listing raje_formula raje_crossref raje_footnotes paste lists",

    // Remove menubar
    menubar: false,

    // Custom toolbar
    toolbar: 'undo redo bold italic link superscript subscript raje_inlineCode raje_inlineQuote raje_inlineFigure raje_crossref raje_footnotes | numlist bullist codesample blockquote raje_table raje_figure raje_listing raje_formula | raje_section',

    // Setup full screen on init
    setup: function (editor) {

      // Set fullscreen
      editor.on('init', function (e) {

        editor.execCommand('mceFullScreen')

        // Move caret at the first h1 element of main section
        // Or right after heading
        tinymce.activeEditor.selection.setCursorLocation(tinymce.activeEditor.dom.select(FIRST_HEADING)[0], 0)
      })

      editor.on('keyDown', function (e) {

        // Prevent shift+enter
        if (e.keyCode == 13 && e.shiftKey) {
          e.preventDefault()
        }
      })

      // Prevent span
      editor.on('nodeChange', function (e) {

        let selectedElement = $(tinymce.activeEditor.selection.getNode())

        // Move caret to first heading if is after or before not editable header
        if (selectedElement.is('p') && (selectedElement.next().is(HEADER_SELECTOR) || (selectedElement.prev().is(HEADER_SELECTOR) && tinymce.activeEditor.dom.select(FIRST_HEADING).length)))
          tinymce.activeEditor.selection.setCursorLocation(tinymce.activeEditor.dom.select(FIRST_HEADING)[0], 0)

        // If the current element isn't inside header, only in section this is permitted
        if (selectedElement.parents('section').length) {

          if (selectedElement.is('span#_mce_caret[data-mce-bogus]') || selectedElement.parent().is('span#_mce_caret[data-mce-bogus]')) {

            // Remove span normally created with bold
            if (selectedElement.parent().is('span#_mce_caret[data-mce-bogus]'))
              selectedElement = selectedElement.parent()

            let bm = tinymce.activeEditor.selection.getBookmark()
            selectedElement.replaceWith(selectedElement.html())
            tinymce.activeEditor.selection.moveToBookmark(bm)
          }
        }

      })

      // Update saved content on undo and redo events
      editor.on('Undo', function (e) {
        tinymce.triggerSave()
      })

      editor.on('Redo', function (e) {
        tinymce.triggerSave()
      })
    },

    // Set default target
    default_link_target: "_blank",

    // Prepend protocol if the link starts with www
    link_assume_external_targets: true,

    // Hide target list
    target_list: false,

    // Hide title
    link_title: false,

    // Set formats
    formats: {
      inline_quote: {
        inline: 'q'
      }
    },

    // Remove "powered by tinymce"
    branding: false,

    // Prevent auto br on element insert
    apply_source_formatting: false,

    // Prevent non editable object resize
    object_resizing: false,

    // Update the table popover layout
    table_toolbar: "tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol",

    image_advtab: true,

    paste_block_drop: true,

    extended_valid_elements: "span[data-mathml,contenteditable],a[class|name|href|target|title|onclick|rel],script[type|src|#text],iframe[src|style|width|height|scrolling|marginwidth|marginheight|frameborder],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],$elements",
  })
})

/**
 * Update content in the iframe, with the one stored by tinymce
 * And save/restore the selection
 */
function updateIframeFromSavedContent() {

  // Save the bookmark
  let bookmark = tinymce.activeEditor.selection.getBookmark(2, true)

  // Update iframe content
  tinymce.activeEditor.setContent($('#raje_root').html())

  // Restore the bookmark
  tinymce.activeEditor.selection.moveToBookmark(bookmark)
}

/**
 * Accept a js object that exists in frame
 * @param {*} element
 */
function moveCaret(element, toStart) {
  tinymce.activeEditor.selection.select(element)
  tinymce.activeEditor.selection.collapse(toStart)
}

function moveCursorToEnd(element) {

  let heading = element
  let offset = 0

  if (heading.contents().length && heading.contents().last()[0].nodeType == 3) {

    heading = heading.contents().last()
    offset = heading[0].wholeText.length
  }

  tinymce.activeEditor.focus()
  tinymce.activeEditor.selection.setCursorLocation(heading[0], offset)
}

/**
 * Create custom into notification
 * @param {*} text
 * @param {*} timeout
 */
function notify(text, type, timeout) {

  // Display only one notification, blocking all others
  if (tinymce.activeEditor.notificationManager.getNotifications().length == 0) {

    let notify = {
      text: text,
      type: type ? type : 'info',
      timeout: timeout ? timeout : 1000
    }

    tinymce.activeEditor.notificationManager.open(notify)
  }
}

function scrollTo(elementSelector) {
  $(tinymce.activeEditor.getBody()).find(elementSelector).get(0).scrollIntoView();
}

function headingDimension() {
  $('h1,h2,h3,h4,h5,h6').each(function () {

    if (!$(this).parents(HEADER_SELECTOR).length) {
      var counter = 0;
      $(this).parents("section").each(function () {
        if ($(this).children("h1,h2,h3,h4,h5,h6").length > 0) {
          counter++;
        }
      });
      $(this).replaceWith("<h" + counter + ">" + $(this).html() + "</h" + counter + ">")
    }
  });
}

$(window).load(function () {
  // Open and close menu headings Näive way
  $('span:contains(\'Headings\')').parent('button').trigger('click')
  $('span:contains(\'Headings\')').parent('button').trigger('click')
})<|endoftext|>


<commit_before>var elemTemp = ['
' +
                '
',
                '

'
                +'

0%

'];

var totalPercents = {};

var addMetric = function(elemClass, metTitle) {
    var div = elemTemp[0] + elemClass;
    div += elemTemp[1] + metTitle;
    div += elemTemp[2] + elemClass;
    div += elemTemp[3] + elemClass;
    div += elemTemp[4] + elemClass;
    div += elemTemp[5] + elemClass;
    div += elemTemp[6] + elemClass;
    div += elemTemp[7];
    $("#metrics").append(div);
    setTimeout(
        function() {
            $(".metcont").css("opacity", "1");
        }, 100);
};

var setMainProg = function() {
    var res = 0;
    var num = 0;
    $.each(totalPercents, function(i) { res += totalPercents[i]; num++; });
    var amt = res/num;
    $(".overview.progfill").css("background-color", getCol(amt));
    $(".overview.progfill").css("box-shadow", "0px 7px 0px 0px " + getShadCol(amt));
    $(".overview.proglab").text(Math.round(amt).toString() + "%");
    if (amt > 100) {
        amt = 100;
    }
    console.log
    $(".overview.progfill").css("width", (amt/100) * parseInt($(".overview.progbase").css("width")));
    console.log(amt);
};

var setProg = function(elemClass, progAmt, descUp) {
    var bar = $("." + elemClass + ".progfill");
    var base = $("." + elemClass + ".progbase");
    var label = $("." + elemClass + ".proglab");
    var desc = $("." + elemClass + ".progdesc");
    bar.css("background-color", getCol(progAmt));
    bar.css("box-shadow", "0px 7px 0px 0px " + getShadCol(progAmt));
    label.text(Math.round(progAmt).toString() + "%");
    totalPercents[elemClass] = progAmt;
    if (progAmt > 100) {
        progAmt = 100;
    }
    bar.css("width", (progAmt / 100) * parseInt(base.css("width")));
    desc.text(descUp);
    setMainProg();
};

var getCol = function(progAmt) {
    return (progAmt < 50) ? "#F75E5E" : (progAmt < 80) ? "#F59631" : "#5EE66E";
};

var getShadCol = function(progAmt) {
    return (progAmt < 50) ? "#CF3636" : (progAmt < 80) ? "#DE7C14" : "#22BF35";
};

$("#metrics").css("margin-top", parseInt($("#mainhead").css("height")) - 75);

window.onscroll = function () {
    if($("body").scrollTop() > 10) {
        $("#divider").css("opacity", "1");
    } else {
        $("#divider").css("opacity", "0");
    }
};

addMetric("sleep", "Sleep"); setProg("sleep", 78.9, "6.45 hours."); addMetric("motion", "Steps"); setProg("motion", 96.45, "234 steps.");<commit_msg>Stuff.<commit_after>var elemTemp = ['
' +
                '
',
                '

'
                +'

0%

'];

var totalPercents = {};

var addMetric = function(elemClass, metTitle) {
    var div = elemTemp[0] + elemClass;
    div += elemTemp[1] + metTitle;
    div += elemTemp[2] + elemClass;
    div += elemTemp[3] + elemClass;
    div += elemTemp[4] + elemClass;
    div += elemTemp[5] + elemClass;
    div += elemTemp[6] + elemClass;
    div += elemTemp[7];
    $("#metrics").append(div);
    setTimeout(
        function() {
            $(".metcont").css("opacity", "1");
        }, 100);
};

var setMainProg = function() {
    var res = 0;
    var num = 0;
    $.each(totalPercents, function(i) { res += totalPercents[i]; num++; });
    var amt = res/num;
    $(".overview.progfill").css("background-color", getCol(amt));
    $(".overview.progfill").css("box-shadow", "0px 7px 0px 0px " + getShadCol(amt));
    $(".overview.proglab").text(Math.round(amt).toString() + "%");
    if (amt > 100) {
        amt = 100;
    }
    console.log
    $(".overview.progfill").css("width", (amt/100) * parseInt($(".overview.progbase").css("width")));
    console.log(amt);
};

var setProg = function(elemClass, progAmt, descUp) {
    var bar = $("." + elemClass + ".progfill");
    var base = $("." + elemClass + ".progbase");
    var label = $("." + elemClass + ".proglab");
    var desc = $("." + elemClass + ".progdesc");
    bar.css("background-color", getCol(progAmt));
    bar.css("box-shadow", "0px 7px 0px 0px " + getShadCol(progAmt));
    label.text(Math.round(progAmt).toString() + "%");
    totalPercents[elemClass] = progAmt;
    if (progAmt > 100) {
        progAmt = 100;
    }
    bar.css("width", (progAmt / 100) * parseInt(base.css("width")));
    desc.text(descUp);
    setMainProg();
};

var getCol = function(progAmt) {
    return (progAmt < 50) ? "#F75E5E" : (progAmt < 80) ? "#F59631" : "#5EE66E";
};

var getShadCol = function(progAmt) {
    return (progAmt < 50) ? "#CF3636" : (progAmt < 80) ? "#DE7C14" : "#22BF35";
};

$("#metrics").css("margin-top", parseInt($("#mainhead").css("height")) - 75);

window.onscroll = function () {
    if($("body").scrollTop() > 10) {
        $("#divider").css("opacity", "1");
    } else {
        $("#divider").css("opacity", "0");
    }
};

<|endoftext|>


<commit_before>describe("luga.validator", function(){

	"use strict";

	it("Lives inside its own namespace", function(){
		expect(luga.validator).toBeDefined();
	});

	describe("Uses separated namespace for storing:", function(){
		it(".rules", function(){
			expect(luga.validator.rules).toBeDefined();
		});
		it(".patterns", function(){
			expect(luga.validator.patterns).toBeDefined();
		});
		it(".dateSpecs", function(){
			expect(luga.validator.dateSpecs).toBeDefined();
		});
		it(".handlers", function(){
			expect(luga.validator.handlers).toBeDefined();
		});
	});

	describe(".version", function(){
		it("Reports the current version number", function(){
			expect(luga.validator.version).toBeDefined();
		});
	});

	describe(".CONST", function(){

		it("Contains default values used by the library", function(){
			expect(luga.validator.CONST).toBeDefined();
		});

		describe(".DEFAULT_DATE_PATTERN", function(){

			it("Default to: YYYY-MM-DD", function(){
				expect(luga.validator.CONST.DEFAULT_DATE_PATTERN).toEqual("YYYY-MM-DD");
			});
			it("It can be changed at run-time", function(){
				// Change to European style
				luga.validator.CONST.DEFAULT_DATE_PATTERN = "DD/MM/YYYY";
				var fieldValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text'>")
				});
				expect(fieldValidator.config.datepattern).toEqual("DD/MM/YYYY");
				luga.validator.CONST.DEFAULT_DATE_PATTERN = "YYYY-MM-DD";
			});

		});
	});
});

describe("luga.validator.handlers", function(){

	"use strict";

	it("Contains handlers for form validation", function(){
		expect(luga.validator.handlers).toBeDefined();
	});

	describe("luga.validator.handlers.errorAlert", function(){
		it("Display error messages inside alert", function(){
			expect(luga.validator.handlers.errorAlert).toBeDefined();
			expect(jQuery.isFunction(luga.validator.handlers.errorAlert)).toBeTruthy();
		});
	});

	describe("luga.validator.handlers.errorBox", function(){
		it("Display errors inside a box above the form", function(){
			expect(luga.validator.handlers.errorBox).toBeDefined();
			expect(jQuery.isFunction(luga.validator.handlers.errorBox)).toBeTruthy();
		});
	});

	describe("luga.validator.handlers.bootstrap", function(){
		it("Use Bootstrap validation states to display errors", function(){
			expect(luga.validator.handlers.bootstrap).toBeDefined();
			expect(jQuery.isFunction(luga.validator.handlers.bootstrap)).toBeTruthy();
		});
	});

});

describe("luga.validator.fieldValidatorFactory.getInstance()", function(){

	"use strict";

	describe("Returns either:", function(){

		beforeEach(function(){
			loadFixtures("validator/FormValidator/generic.htm");
		});

		it("null if the passed HTML field has no matching validator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("<div>")})).toBeNull();
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("<input type='reset'>")})).toBeNull();
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("<fieldset>")})).toBeNull();
		});

		it("An instance of luga.validator.TextValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#age")}).constructor).toEqual(luga.validator.TextValidator);
		});

		it("An instance of luga.validator.CheckboxValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#boxNicole")}).constructor).toEqual(luga.validator.CheckboxValidator);
		});

		it("An instance of luga.validator.RadioValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#boxNicole")}).constructor).toEqual(luga.validator.CheckboxValidator);
		});

		it("An instance of luga.validator.SelectValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#food")}).constructor).toEqual(luga.validator.SelectValidator);
		});

	});

});

describe("luga.validator.BaseFieldValidator is an abstract class", function(){

	"use strict";

	it("That can't be invoked directly", function(){
		var textNode = jQuery('<input type="text" data-lugavalidator-required="true" disabled="disabled" data-lugavalidator-errorclass="invalid">');
		expect(function(){
			new luga.validator.BaseFieldValidator({
				fieldNode: textNode
			});
		}).toThrow();
	});

	it("Adds/remove error class and title attribute from the associated form field", function(){
		var fieldNode = jQuery('<input type="text" data-lugavalidator-required="true" data-lugavalidator-errorclass="invalid" data-lugavalidator-message="Invalid field!">');
		var fieldValidator = luga.validator.fieldValidatorFactory.getInstance({
			fieldNode: fieldNode
		});
		expect(fieldNode.hasClass("invalid")).toBeFalsy();
		fieldValidator.flagInvalid();
		expect(fieldNode.hasClass("invalid")).toBeTruthy();
		expect(fieldNode.attr("title")).toEqual("Invalid field!");
		fieldValidator.flagValid();
		expect(fieldNode.hasClass("invalid")).toBeFalsy();
		expect(fieldNode.attr("title")).toBeUndefined();
	});

	it("Handle disabled fields as always valid", function(){
		var fieldNode = jQuery('<input type="text" data-lugavalidator-required="true" disabled="disabled" data-lugavalidator-errorclass="invalid">');
		var fieldValidator = luga.validator.fieldValidatorFactory.getInstance({
			fieldNode: fieldNode
		});
		expect(fieldValidator.validate()).toBeFalsy();
		expect(fieldNode.hasClass("invalid")).toBeFalsy();
	});

	describe(".name", function(){

		describe("Either", function(){

			it("Defaults to an empty string", function(){
				var textValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text'>")
				});
				expect(textValidator.name).toEqual("");
			});
			it("Matches the field's name attribute", function(){
				var textValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text' name='myName' id='myId'>")
				});
				expect(textValidator.name).toEqual("myName");
			});
			it("Matches the field's id attribute", function(){
				var textValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text' id='myId'>")
				});
				expect(textValidator.name).toEqual("myId");
			});

		});

	});

});

describe("luga.validator.BaseGroupValidator is an abstract class", function(){

	"use strict";

	it("That can't be invoked directly", function(){
		var boxNode = jQuery("<input type='checkbox'>");
		expect(function(){
			new luga.validator.BaseGroupValidator({
				fieldNode: boxNode
			});
		}).toThrow();
	});

});

<commit_msg>Test: Validator<commit_after>describe("luga.validator", function(){

	"use strict";

	it("Lives inside its own namespace", function(){
		expect(luga.validator).toBeDefined();
	});

	describe("Uses separated namespace for storing:", function(){
		it(".rules", function(){
			expect(luga.validator.rules).toBeDefined();
		});
		it(".patterns", function(){
			expect(luga.validator.patterns).toBeDefined();
		});
		it(".dateSpecs", function(){
			expect(luga.validator.dateSpecs).toBeDefined();
		});
		it(".handlers", function(){
			expect(luga.validator.handlers).toBeDefined();
		});
	});

	describe(".version", function(){
		it("Reports the current version number", function(){
			expect(luga.validator.version).toBeDefined();
		});
	});

	describe(".CONST", function(){

		it("Contains default values used by the library", function(){
			expect(luga.validator.CONST).toBeDefined();
		});

		describe(".DEFAULT_DATE_PATTERN", function(){

			it("Default to: YYYY-MM-DD", function(){
				expect(luga.validator.CONST.DEFAULT_DATE_PATTERN).toEqual("YYYY-MM-DD");
			});
			it("It can be changed at run-time", function(){
				// Change to European style
				luga.validator.CONST.DEFAULT_DATE_PATTERN = "DD/MM/YYYY";
				var fieldValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text'>")
				});
				expect(fieldValidator.config.datepattern).toEqual("DD/MM/YYYY");
				luga.validator.CONST.DEFAULT_DATE_PATTERN = "YYYY-MM-DD";
			});

		});
	});
});

describe("luga.validator.handlers", function(){

	"use strict";

	it("Contains handlers for form validation", function(){
		expect(luga.validator.handlers).toBeDefined();
	});

	describe("luga.validator.handlers.errorAlert", function(){
		it("Display error messages inside alert", function(){
			spyOn(window, "alert")
			spyOn(luga.validator.handlers, "errorAlert").and.callThrough();
			loadFixtures("validator/FormValidator/basic.htm");
			var formValidator = new luga.validator.FormValidator({
				formNode: jQuery("#basic"),
				error: "luga.validator.handlers.errorAlert"
			});

			formValidator.validate()
			expect(luga.validator.handlers.errorAlert).toHaveBeenCalled();
			expect(window.alert).toHaveBeenCalled();
		});
	});

	describe("luga.validator.handlers.errorBox", function(){
		it("Display errors inside a box above the form", function(){
			expect(luga.validator.handlers.errorBox).toBeDefined();
			expect(jQuery.isFunction(luga.validator.handlers.errorBox)).toBeTruthy();
		});
	});

	describe("luga.validator.handlers.bootstrap", function(){
		it("Use Bootstrap validation states to display errors", function(){
			spyOn(luga.validator.handlers, "bootstrap").and.callThrough();
			loadFixtures("validator/FormValidator/basic.htm");
			var formValidator = new luga.validator.FormValidator({
				formNode: jQuery("#basic"),
				error: "luga.validator.handlers.bootstrap"
			});

			formValidator.validate()
			expect(luga.validator.handlers.bootstrap).toHaveBeenCalled();
		});

	});

});

describe("luga.validator.fieldValidatorFactory.getInstance()", function(){

	"use strict";

	describe("Returns either:", function(){

		beforeEach(function(){
			loadFixtures("validator/FormValidator/generic.htm");
		});

		it("null if the passed HTML field has no matching validator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("<div>")})).toBeNull();
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("<input type='reset'>")})).toBeNull();
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("<fieldset>")})).toBeNull();
		});

		it("An instance of luga.validator.TextValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#age")}).constructor).toEqual(luga.validator.TextValidator);
		});

		it("An instance of luga.validator.CheckboxValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#boxNicole")}).constructor).toEqual(luga.validator.CheckboxValidator);
		});

		it("An instance of luga.validator.RadioValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#boxNicole")}).constructor).toEqual(luga.validator.CheckboxValidator);
		});

		it("An instance of luga.validator.SelectValidator", function(){
			expect(luga.validator.fieldValidatorFactory.getInstance({fieldNode: jQuery("#food")}).constructor).toEqual(luga.validator.SelectValidator);
		});

	});

});

describe("luga.validator.BaseFieldValidator is an abstract class", function(){

	"use strict";

	it("That can't be invoked directly", function(){
		var textNode = jQuery('<input type="text" data-lugavalidator-required="true" disabled="disabled" data-lugavalidator-errorclass="invalid">');
		expect(function(){
			new luga.validator.BaseFieldValidator({
				fieldNode: textNode
			});
		}).toThrow();
	});

	it("Adds/remove error class and title attribute from the associated form field", function(){
		var fieldNode = jQuery('<input type="text" data-lugavalidator-required="true" data-lugavalidator-errorclass="invalid" data-lugavalidator-message="Invalid field!">');
		var fieldValidator = luga.validator.fieldValidatorFactory.getInstance({
			fieldNode: fieldNode
		});
		expect(fieldNode.hasClass("invalid")).toBeFalsy();
		fieldValidator.flagInvalid();
		expect(fieldNode.hasClass("invalid")).toBeTruthy();
		expect(fieldNode.attr("title")).toEqual("Invalid field!");
		fieldValidator.flagValid();
		expect(fieldNode.hasClass("invalid")).toBeFalsy();
		expect(fieldNode.attr("title")).toBeUndefined();
	});

	it("Handle disabled fields as always valid", function(){
		var fieldNode = jQuery('<input type="text" data-lugavalidator-required="true" disabled="disabled" data-lugavalidator-errorclass="invalid">');
		var fieldValidator = luga.validator.fieldValidatorFactory.getInstance({
			fieldNode: fieldNode
		});
		expect(fieldValidator.validate()).toBeFalsy();
		expect(fieldNode.hasClass("invalid")).toBeFalsy();
	});

	describe(".name", function(){

		describe("Either", function(){

			it("Defaults to an empty string", function(){
				var textValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text'>")
				});
				expect(textValidator.name).toEqual("");
			});
			it("Matches the field's name attribute", function(){
				var textValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text' name='myName' id='myId'>")
				});
				expect(textValidator.name).toEqual("myName");
			});
			it("Matches the field's id attribute", function(){
				var textValidator = luga.validator.fieldValidatorFactory.getInstance({
					fieldNode: jQuery("<input type='text' id='myId'>")
				});
				expect(textValidator.name).toEqual("myId");
			});

		});

	});

});

describe("luga.validator.BaseGroupValidator is an abstract class", function(){

	"use strict";

	it("That can't be invoked directly", function(){
		var boxNode = jQuery("<input type='checkbox'>");
		expect(function(){
			new luga.validator.BaseGroupValidator({
				fieldNode: boxNode
			});
		}).toThrow();
	});

});

<|endoftext|>


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

import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
import moment from 'moment';
import DateTimePicker from '@react-native-community/datetimepicker';
import PropTypes from 'prop-types';

import { FlexColumn } from '../FlexColumn';
import { FlexRow } from '../FlexRow';

import { APP_FONT_FAMILY, SUSSOL_ORANGE, DARKER_GREY } from '../../globalStyles';
import { CalendarIcon } from '../icons';
import { CircleButton } from '../CircleButton';
import { FormLabel } from './FormLabel';
import { FormInvalidMessage } from './FormInvalidMessage';

/**
 * Form input for entering dates. Renders a TextInput as well as a button, opening a
 * native Date picker.
 *
 * @prop {Date}   value                    A date or moment - The initial date.
 * @prop {Bool}   isRequired               Indicator whether this date is required.
 * @prop {Bool}   isDisabled               Indicator whether this input is disabled.
 * @prop {Func}   onValidate               Callback to validate the input.
 * @prop {Func}   onChangeDate             Callback for date changes[called only with a valid date].
 * @prop {Func}   onSubmit                 Callback after submitting a date from text.
 * @prop {String} label                    Label for the input
 * @prop {String} placeholder              Placeholder text for the text input.
 * @prop {String} placeholderTextColor     Colour of the placeholder text.
 * @prop {String} underlineColorAndroid    Underline colour of the text input.
 * @prop {String} invalidMessage           Message to the user when the current input is invalid.
 * @prop {Object} textInputStyle           Style object to override the underlying text input.
 */
export const FormDateInput = React.forwardRef(
  (
    {
      value,
      isRequired,
      onValidate,
      onChangeDate,
      label,
      invalidMessage,
      textInputStyle,
      placeholder,
      placeholderTextColor,
      underlineColorAndroid,
      onSubmit,
      isDisabled,
    },
    ref
  ) => {
    const initialValue = onValidate(value)
      ? moment(value, 'DD/MM/YYYY')
      : moment(new Date(), 'DD/MM/YYYY');

    const [inputState, setInputState] = React.useState({
      isValid: true,
      inputValue: moment(initialValue).format('DD/MM/YYYY'),
      pickerSeedValue: initialValue.toDate(),
      datePickerOpen: false,
    });

    const { inputValue, isValid, pickerSeedValue, datePickerOpen } = inputState;

    const onUpdate = (newValue, validity = true, pickerVisibility = false) => {
      const newState = {
        isValid: validity,
        inputValue: newValue,
        pickerSeedValue: validity ? moment(newValue, 'DD/MM/YYYY').toDate() : new Date(),
        datePickerOpen: pickerVisibility,
      };
      setInputState(newState);
      onChangeDate(moment(newValue, 'DD/MM/YYYY').toDate());
    };

    // When changing the value of the input, check the new validity and set the new input.
    // Do not restrict input, but provide feedback to the user.
    const onChangeTextCallback = React.useCallback(
      newValue => {
        const newValueIsValid = onValidate ? onValidate(newValue) : true;
        onUpdate(newValue, newValueIsValid);
      },
      [onValidate]
    );

    const onChangeDates = ({ nativeEvent }) => {
      const { timestamp } = nativeEvent;
      if (!timestamp) return;
      const newDate = moment(new Date(timestamp)).format('DD/MM/YYYY');
      onUpdate(newDate, true);
    };

    const openDatePicker = () => setInputState(state => ({ ...state, datePickerOpen: true }));

    const onSubmitEditing = React.useCallback(event => onSubmit?.(event.nativeEvent.text), [
      onSubmit,
    ]);

    return (
      <FlexRow flex={1}>
        <FlexColumn flex={1}>
          <FormLabel value={label} isRequired={isRequired} />
          <FlexRow flex={1}>
            <TextInput
              ref={ref}
              style={textInputStyle}
              value={inputValue}
              placeholderTextColor={placeholderTextColor}
              underlineColorAndroid={underlineColorAndroid}
              placeholder={placeholder}
              selectTextOnFocus
              returnKeyType="next"
              autoCapitalize="none"
              autoCorrect={false}
              onChangeText={onChangeTextCallback}
              onSubmitEditing={onSubmitEditing}
              editable={!isDisabled}
            />
            <CircleButton
              IconComponent={CalendarIcon}
              onPress={openDatePicker}
              isDisabled={isDisabled}
            />
          </FlexRow>

          {datePickerOpen && (
            <DateTimePicker
              onChange={onChangeDates}
              mode="date"
              display="spinner"
              value={pickerSeedValue}
              maximumDate={new Date()}
            />
          )}
          <FormInvalidMessage isValid={isValid} message={invalidMessage} />
        </FlexColumn>
      </FlexRow>
    );
  }
);

const localStyles = StyleSheet.create({
  labelStyle: { marginTop: 15, fontSize: 16, fontFamily: APP_FONT_FAMILY },
  isRequiredStyle: { fontSize: 12, color: SUSSOL_ORANGE, fontFamily: APP_FONT_FAMILY },
  textInputStyle: { flex: 1, fontFamily: APP_FONT_FAMILY },
});

FormDateInput.defaultProps = {
  placeholder: '',
  placeholderTextColor: SUSSOL_ORANGE,
  underlineColorAndroid: DARKER_GREY,
  textInputStyle: localStyles.textInputStyle,
  isRequired: false,
  onValidate: null,
  invalidMessage: '',
  onSubmit: null,
  isDisabled: false,
};

FormDateInput.propTypes = {
  textInputStyle: PropTypes.object,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
  isRequired: PropTypes.bool,
  onValidate: PropTypes.func,
  onChangeDate: PropTypes.func.isRequired,
  label: PropTypes.string.isRequired,
  invalidMessage: PropTypes.string,
  placeholder: PropTypes.string,
  placeholderTextColor: PropTypes.string,
  underlineColorAndroid: PropTypes.string,
  onSubmit: PropTypes.func,
  isDisabled: PropTypes.isDisabled,
};
<commit_msg>Update form date state logic to allow empty string<commit_after>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
import moment from 'moment';
import DateTimePicker from '@react-native-community/datetimepicker';
import PropTypes from 'prop-types';

import { FlexColumn } from '../FlexColumn';
import { FlexRow } from '../FlexRow';

import { APP_FONT_FAMILY, SUSSOL_ORANGE, DARKER_GREY } from '../../globalStyles';
import { CalendarIcon } from '../icons';
import { CircleButton } from '../CircleButton';
import { FormLabel } from './FormLabel';
import { FormInvalidMessage } from './FormInvalidMessage';

/**
 * Form input for entering dates. Renders a TextInput as well as a button, opening a
 * native Date picker.
 *
 * @prop {Date}   value                    A date or moment - The initial date.
 * @prop {Bool}   isRequired               Indicator whether this date is required.
 * @prop {Bool}   isDisabled               Indicator whether this input is disabled.
 * @prop {Func}   onValidate               Callback to validate the input.
 * @prop {Func}   onChangeDate             Callback for date changes[called only with a valid date].
 * @prop {Func}   onSubmit                 Callback after submitting a date from text.
 * @prop {String} label                    Label for the input
 * @prop {String} placeholder              Placeholder text for the text input.
 * @prop {String} placeholderTextColor     Colour of the placeholder text.
 * @prop {String} underlineColorAndroid    Underline colour of the text input.
 * @prop {String} invalidMessage           Message to the user when the current input is invalid.
 * @prop {Object} textInputStyle           Style object to override the underlying text input.
 */
export const FormDateInput = React.forwardRef(
  (
    {
      value,
      isRequired,
      onValidate,
      onChangeDate,
      label,
      invalidMessage,
      textInputStyle,
      placeholder,
      placeholderTextColor,
      underlineColorAndroid,
      onSubmit,
      isDisabled,
    },
    ref
  ) => {
    const initialValue = onValidate(value)
      ? moment(value, 'DD/MM/YYYY')
      : moment(new Date(), 'DD/MM/YYYY');

    const [inputState, setInputState] = React.useState({
      isValid: true,
      inputValue: initialValue.isValid() ? moment(initialValue).format('DD/MM/YYYY') : '',
      pickerSeedValue: initialValue.isValid() ? initialValue.toDate() : new Date(),
      datePickerOpen: false,
    });

    const { inputValue, isValid, pickerSeedValue, datePickerOpen } = inputState;

    const onUpdate = (newValue, validity = true, pickerVisibility = false) => {
      const newState = {
        isValid: validity,
        inputValue: newValue,
        pickerSeedValue: validity ? moment(newValue, 'DD/MM/YYYY').toDate() : new Date(),
        datePickerOpen: pickerVisibility,
      };
      setInputState(newState);
      onChangeDate(moment(newValue, 'DD/MM/YYYY').toDate());
    };

    // When changing the value of the input, check the new validity and set the new input.
    // Do not restrict input, but provide feedback to the user.
    const onChangeTextCallback = React.useCallback(
      newValue => {
        const newValueIsValid = onValidate ? onValidate(newValue) : true;
        onUpdate(newValue, newValueIsValid);
      },
      [onValidate]
    );

    const onChangeDates = ({ nativeEvent }) => {
      const { timestamp } = nativeEvent;
      if (!timestamp) return;
      const newDate = moment(new Date(timestamp)).format('DD/MM/YYYY');
      onUpdate(newDate, true);
    };

    const openDatePicker = () => setInputState(state => ({ ...state, datePickerOpen: true }));

    const onSubmitEditing = React.useCallback(event => onSubmit?.(event.nativeEvent.text), [
      onSubmit,
    ]);

    return (
      <FlexRow flex={1}>
        <FlexColumn flex={1}>
          <FormLabel value={label} isRequired={isRequired} />
          <FlexRow flex={1}>
            <TextInput
              ref={ref}
              style={textInputStyle}
              value={inputValue}
              placeholderTextColor={placeholderTextColor}
              underlineColorAndroid={underlineColorAndroid}
              placeholder={placeholder}
              selectTextOnFocus
              returnKeyType="next"
              autoCapitalize="none"
              autoCorrect={false}
              onChangeText={onChangeTextCallback}
              onSubmitEditing={onSubmitEditing}
              editable={!isDisabled}
            />
            <CircleButton
              IconComponent={CalendarIcon}
              onPress={openDatePicker}
              isDisabled={isDisabled}
            />
          </FlexRow>

          {datePickerOpen && (
            <DateTimePicker
              onChange={onChangeDates}
              mode="date"
              display="spinner"
              value={pickerSeedValue}
              maximumDate={new Date()}
            />
          )}
          <FormInvalidMessage isValid={isValid} message={invalidMessage} />
        </FlexColumn>
      </FlexRow>
    );
  }
);

const localStyles = StyleSheet.create({
  labelStyle: { marginTop: 15, fontSize: 16, fontFamily: APP_FONT_FAMILY },
  isRequiredStyle: { fontSize: 12, color: SUSSOL_ORANGE, fontFamily: APP_FONT_FAMILY },
  textInputStyle: { flex: 1, fontFamily: APP_FONT_FAMILY },
});

FormDateInput.defaultProps = {
  placeholder: '',
  placeholderTextColor: SUSSOL_ORANGE,
  underlineColorAndroid: DARKER_GREY,
  textInputStyle: localStyles.textInputStyle,
  isRequired: false,
  onValidate: null,
  invalidMessage: '',
  onSubmit: null,
  isDisabled: false,
};

FormDateInput.propTypes = {
  textInputStyle: PropTypes.object,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
  isRequired: PropTypes.bool,
  onValidate: PropTypes.func,
  onChangeDate: PropTypes.func.isRequired,
  label: PropTypes.string.isRequired,
  invalidMessage: PropTypes.string,
  placeholder: PropTypes.string,
  placeholderTextColor: PropTypes.string,
  underlineColorAndroid: PropTypes.string,
  onSubmit: PropTypes.func,
  isDisabled: PropTypes.isDisabled,
};
<|endoftext|>


<commit_before>import { REHYDRATE } from 'redux-persist/lib/constants'
import constants from "../services/constants"
import * as converter from "../utils/converter"
import BLOCKCHAIN_INFO from "../../../env"

var initState = constants.INIT_LIMIT_ORDER_STATE
initState.snapshot = constants.INIT_LIMIT_ORDER_STATE

const limitOrder = (state = initState, action) => {
  var newState = { ...state, errors: { ...state.errors } }
  switch (action.type) {
    // case "LIMIT_ORDER.SELECT_TOKEN_ASYNC": {
    //   newState.isSelectToken = true
    //   return newState
    // }

    case "LIMIT_ORDER.SELECT_TOKEN": {

      var {sourceTokenSymbol, sourceToken, destTokenSymbol, destToken, type} = action.payload
      newState.sourceTokenSymbol = sourceTokenSymbol
      newState.sourceToken = sourceToken
      newState.destTokenSymbol = destTokenSymbol
      newState.destToken = destToken

      var errors = newState.errors
      errors.sourceAmount = []
      errors.triggerRate = []
      newState.errors = errors

      newState.selected = true
      newState.isEditRate = false
      return newState
    }

    case "LIMIT_ORDER.INPUT_CHANGE": {
      const {focus, value} = action.payload
      switch(focus) {
        case "source":
          newState.sourceAmount = value
          var errors = newState.errors
          errors.sourceAmount = []
          newState.errors = errors
          var bigRate = converter.roundingRate(state.triggerRate)
          newState.destAmount = converter.caculateDestAmount(value, bigRate, 6)
          break
        case "dest":
          newState.destAmount = value
          var errors = newState.errors
          errors.triggerRate = []
          newState.errors = errors
          newState.triggerRate = converter.caculateTriggerRate(state.sourceAmount, value, 6)
          break
        case "rate":
          newState.triggerRate = value
          var errors = newState.errors
          errors.triggerRate = []
          newState.errors = errors
          var bigRate = converter.roundingRate(value)
          newState.destAmount = converter.caculateDestAmount(state.sourceAmount, bigRate, 6)
          break
      }
      return newState
    }
    case "LIMIT_ORDER.FOCUS_INPUT": {
      newState.inputFocus = action.payload;
      return newState;
    }
    case "LIMIT_ORDER.UPDATE_RATE_COMPLETE": {
      const { rateInit, expectedPrice, slippagePrice, blockNo, isManual, type } = action.payload

      if (expectedPrice == "0") {
        if (rateInit == "0" || rateInit == 0 || rateInit === undefined || rateInit === null) {
          newState.errors.rateSystem = "This token pair is temporarily under maintenance"
        } else {
          newState.errors.rateSystem = "Kyber cannot handle your amount at the moment, please reduce your amount"
        }
      } else {
        newState.errors.rateSystem = ""
      }

      var slippageRate = slippagePrice == "0" ? converter.estimateSlippagerate(rateInit, 18) : converter.toT(slippagePrice, 18)
      var expectedRate = expectedPrice == "0" ? rateInit : expectedPrice

      newState.slippageRate = slippageRate
      newState.offeredRate = expectedRate
      newState.blockNo = blockNo

      if(type === constants.LIMIT_ORDER_CONFIG.updateRateType.selectToken){
        newState.triggerRate = converter.roundingNumber(converter.toT(expectedRate, 18))
        newState.destAmount = converter.caculateDestAmount(newState.sourceAmount, expectedRate, 6)
      }

      newState.isSelectToken = false
      return newState
    }

    case "LIMIT_ORDER.UPDATE_RATE_PENDING": {
      const isManual = action.payload.isManual
      if (isManual) {
        newState.isSelectToken = true
      }
      return newState
    }
    case "LIMIT_ORDER.SET_IS_SELECT_TOKEN_BALANCE": {
      newState.isSelectTokenBalance = action.payload;
      return newState;
    }
    case "LIMIT_ORDER.FETCH_FEE": {
      newState.isFetchingFee = true
      return newState
    }
    case "LIMIT_ORDER.FETCH_FEE_COMPLETE":{
      const {fee, err} = action.payload
      newState.orderFee = fee
      newState.orderFeeErr = err
      newState.isFetchingFee = false
      return newState
    }

    case "LIMIT_ORDER.THROW_ERROR":{
      const { key, msg } = action.payload
      var errors = newState.errors
      errors[key] = msg
      newState.errors = errors
      return newState
    }
    case "LIMIT_ORDER.UPDATE_ORDER_PATH":{
      const {orderPath, currentPathIndex} = action.payload
      newState.orderPath = orderPath
      newState.currentPathIndex = currentPathIndex
      return newState
    }
    case "LIMIT_ORDER.RESET_ORDER_PATH":{
      newState.currentPathIndex = -1
      return newState
    }
    case "LIMIT_ORDER.FORWARD_ORDER_PATH":{
      newState.currentPathIndex += 1
      return newState
    }
    case "LIMIT_ORDER.ADD_LIST_ORDER": {
      const listOrder = action.payload;
      newState.listOrder = listOrder;
      return newState;
    }
    case "LIMIT_ORDER.ADD_NEW_ORDER":{
      const {order} = action.payload
      var listOrder = newState.listOrder
      listOrder.unshift(order)
      newState.listOrder = listOrder
      return newState
    }
    case "LIMIT_ORDER.REMOVE_ORDER": {
      const { order } = action.payload;
      const listOrder = JSON.parse(JSON.stringify(newState.listOrder));
      const target = listOrder.filter(item => item.id === order.id);
      if (target.length > 0) {
        listOrder.splice(listOrder.indexOf(target[0]), 1);
      }
      newState.listOrder = listOrder;
      return newState;
    }
    case "LIMIT_ORDER.UPDATE_ORDER": {
      const { order } = action.payload;
      const newListOrder = newState.listOrder.map(item => {
        if (item.id === order.id) {
          return order;
        }

        return item;
      });
      newState.listOrder = newListOrder;
      return newState;
    }

    case "GLOBAL.SET_GAS_PRICE_COMPLETE": {
      const { safeLowGas, standardGas, fastGas, defaultGas, selectedGas } = action.payload;

      const gasPriceSuggest = { ...newState.gasPriceSuggest };

      gasPriceSuggest.fastGas = Math.round(fastGas * 10) / 10;
      gasPriceSuggest.standardGas = Math.round(standardGas * 10) / 10;
      gasPriceSuggest.safeLowGas = Math.round(safeLowGas * 10) / 10;

      newState.gasPriceSuggest = { ...gasPriceSuggest };
      newState.gasPrice = Math.round(defaultGas * 10) / 10;

      newState.selectedGas = selectedGas;
      return newState;
    }
  }
  return state
}

export default limitOrder;
<commit_msg>Trigger rate has comma will cause dest amount can not be calculated<commit_after>import { REHYDRATE } from 'redux-persist/lib/constants'
import constants from "../services/constants"
import * as converter from "../utils/converter"
import BLOCKCHAIN_INFO from "../../../env"

var initState = constants.INIT_LIMIT_ORDER_STATE
initState.snapshot = constants.INIT_LIMIT_ORDER_STATE

const limitOrder = (state = initState, action) => {
  var newState = { ...state, errors: { ...state.errors } }
  switch (action.type) {
    // case "LIMIT_ORDER.SELECT_TOKEN_ASYNC": {
    //   newState.isSelectToken = true
    //   return newState
    // }

    case "LIMIT_ORDER.SELECT_TOKEN": {

      var {sourceTokenSymbol, sourceToken, destTokenSymbol, destToken, type} = action.payload
      newState.sourceTokenSymbol = sourceTokenSymbol
      newState.sourceToken = sourceToken
      newState.destTokenSymbol = destTokenSymbol
      newState.destToken = destToken

      var errors = newState.errors
      errors.sourceAmount = []
      errors.triggerRate = []
      newState.errors = errors

      newState.selected = true
      newState.isEditRate = false
      return newState
    }

    case "LIMIT_ORDER.INPUT_CHANGE": {
      const {focus, value} = action.payload
      switch(focus) {
        case "source":
          newState.sourceAmount = value
          var errors = newState.errors
          errors.sourceAmount = []
          newState.errors = errors
          var bigRate = converter.roundingRate(state.triggerRate)
          newState.destAmount = converter.caculateDestAmount(value, bigRate, 6)
          break
        case "dest":
          newState.destAmount = value
          var errors = newState.errors
          errors.triggerRate = []
          newState.errors = errors
          newState.triggerRate = converter.caculateTriggerRate(state.sourceAmount, value, 6)
          break
        case "rate":
          newState.triggerRate = value
          var errors = newState.errors
          errors.triggerRate = []
          newState.errors = errors
          var bigRate = converter.roundingRate(value)
          newState.destAmount = converter.caculateDestAmount(state.sourceAmount, bigRate, 6)
          break
      }
      return newState
    }
    case "LIMIT_ORDER.FOCUS_INPUT": {
      newState.inputFocus = action.payload;
      return newState;
    }
    case "LIMIT_ORDER.UPDATE_RATE_COMPLETE": {
      const { rateInit, expectedPrice, slippagePrice, blockNo, isManual, type } = action.payload

      if (expectedPrice == "0") {
        if (rateInit == "0" || rateInit == 0 || rateInit === undefined || rateInit === null) {
          newState.errors.rateSystem = "This token pair is temporarily under maintenance"
        } else {
          newState.errors.rateSystem = "Kyber cannot handle your amount at the moment, please reduce your amount"
        }
      } else {
        newState.errors.rateSystem = ""
      }

      var slippageRate = slippagePrice == "0" ? converter.estimateSlippagerate(rateInit, 18) : converter.toT(slippagePrice, 18)
      var expectedRate = expectedPrice == "0" ? rateInit : expectedPrice

      newState.slippageRate = slippageRate
      newState.offeredRate = expectedRate
      newState.blockNo = blockNo

      if(type === constants.LIMIT_ORDER_CONFIG.updateRateType.selectToken){
        newState.triggerRate = converter.roundingNumber(converter.toT(expectedRate, 18)).replace(/,/g, "");
        newState.destAmount = converter.caculateDestAmount(newState.sourceAmount, expectedRate, 6)
      }

      newState.isSelectToken = false
      return newState
    }

    case "LIMIT_ORDER.UPDATE_RATE_PENDING": {
      const isManual = action.payload.isManual
      if (isManual) {
        newState.isSelectToken = true
      }
      return newState
    }
    case "LIMIT_ORDER.SET_IS_SELECT_TOKEN_BALANCE": {
      newState.isSelectTokenBalance = action.payload;
      return newState;
    }
    case "LIMIT_ORDER.FETCH_FEE": {
      newState.isFetchingFee = true
      return newState
    }
    case "LIMIT_ORDER.FETCH_FEE_COMPLETE":{
      const {fee, err} = action.payload
      newState.orderFee = fee
      newState.orderFeeErr = err
      newState.isFetchingFee = false
      return newState
    }

    case "LIMIT_ORDER.THROW_ERROR":{
      const { key, msg } = action.payload
      var errors = newState.errors
      errors[key] = msg
      newState.errors = errors
      return newState
    }
    case "LIMIT_ORDER.UPDATE_ORDER_PATH":{
      const {orderPath, currentPathIndex} = action.payload
      newState.orderPath = orderPath
      newState.currentPathIndex = currentPathIndex
      return newState
    }
    case "LIMIT_ORDER.RESET_ORDER_PATH":{
      newState.currentPathIndex = -1
      return newState
    }
    case "LIMIT_ORDER.FORWARD_ORDER_PATH":{
      newState.currentPathIndex += 1
      return newState
    }
    case "LIMIT_ORDER.ADD_LIST_ORDER": {
      const listOrder = action.payload;
      newState.listOrder = listOrder;
      return newState;
    }
    case "LIMIT_ORDER.ADD_NEW_ORDER":{
      const {order} = action.payload
      var listOrder = newState.listOrder
      listOrder.unshift(order)
      newState.listOrder = listOrder
      return newState
    }
    case "LIMIT_ORDER.REMOVE_ORDER": {
      const { order } = action.payload;
      const listOrder = JSON.parse(JSON.stringify(newState.listOrder));
      const target = listOrder.filter(item => item.id === order.id);
      if (target.length > 0) {
        listOrder.splice(listOrder.indexOf(target[0]), 1);
      }
      newState.listOrder = listOrder;
      return newState;
    }
    case "LIMIT_ORDER.UPDATE_ORDER": {
      const { order } = action.payload;
      const newListOrder = newState.listOrder.map(item => {
        if (item.id === order.id) {
          return order;
        }

        return item;
      });
      newState.listOrder = newListOrder;
      return newState;
    }

    case "GLOBAL.SET_GAS_PRICE_COMPLETE": {
      const { safeLowGas, standardGas, fastGas, defaultGas, selectedGas } = action.payload;

      const gasPriceSuggest = { ...newState.gasPriceSuggest };

      gasPriceSuggest.fastGas = Math.round(fastGas * 10) / 10;
      gasPriceSuggest.standardGas = Math.round(standardGas * 10) / 10;
      gasPriceSuggest.safeLowGas = Math.round(safeLowGas * 10) / 10;

      newState.gasPriceSuggest = { ...gasPriceSuggest };
      newState.gasPrice = Math.round(defaultGas * 10) / 10;

      newState.selectedGas = selectedGas;
      return newState;
    }
  }
  return state
}

export default limitOrder;
<|endoftext|>