<commit_before>//component/icon.js

var Icon = React.createClass({
  propTypes: {
    style: React.PropTypes.object,
    fixed: React.PropTypes.boolean,
  },
  render: function() {
    var className = 'icon ' + ('fixed' in this.props ? 'icon-fixed-width ' : '') + this.props.icon;
    return
  }
});

var Link = React.createClass({
  render: function() {
    console.log(this.props);
    var href = this.props.href ? this.props.href : 'javascript:;',
      icon = this.props.icon ? <Icon icon={this.props.icon} />  : '',
      className = (this.props.button ? 'button-block button-' + this.props.button : 'button-text') +
                  (this.props.hidden ? ' hidden' : '') + (this.props.disabled ? ' disabled' : '');
    return (

        {this.props.icon ? icon : '' }
        {this.props.label}

    );
  }
});

// Generic menu styles
var menuStyle = {
  border: '1px solid #aaa',
  padding: '2px',
  whiteSpace: 'nowrap',
};

var Menu = React.createClass({
  render: function() {
    return (

        {this.props.children}

    );
  }
});

var menuItemStyle = {
  display: 'block',
};
var MenuItem = React.createClass({
  propTypes: {
    href: React.PropTypes.string,
    label: React.PropTypes.string,
    icon: React.PropTypes.string,
    onClick: React.PropTypes.function,
  },
  getDefaultProps: function() {
    return {
      iconPosition: 'left',
    }
  },
  render: function() {
    var icon = (this.props.icon ? <Icon icon={this.props.icon} fixed /> : null);

    return (

        {this.props.iconPosition == 'left' ? icon : null}
        {this.props.label}
        {this.props.iconPosition == 'left' ? null : icon}

    );
  }
});

var creditAmountStyle = {
  color: '#216C2A',
  fontWeight: 'bold',
  fontSize: '0.8em'
}, estimateStyle = {
  marginLeft : '5px',
  color: '#aaa',
};

var CreditAmount = React.createClass({
  propTypes: {
    amount: React.PropTypes.number,
  },
  render: function() {
    var formattedAmount = lbry.formatCredits(this.props.amount);
    return (

        {formattedAmount}
        { this.props.isEstimate ? (est) : null }

    );
  }
});

var subPageLogoStyle = {
  maxWidth: '150px',
  display: 'block',
  marginTop: '36px',
};
var SubPageLogo = React.createClass({
  render: function() {
    return ;
  }
});<commit_msg>Add padding to main menu<commit_after>//component/icon.js

var Icon = React.createClass({
  propTypes: {
    style: React.PropTypes.object,
    fixed: React.PropTypes.boolean,
  },
  render: function() {
    var className = 'icon ' + ('fixed' in this.props ? 'icon-fixed-width ' : '') + this.props.icon;
    return
  }
});

var Link = React.createClass({
  render: function() {
    console.log(this.props);
    var href = this.props.href ? this.props.href : 'javascript:;',
      icon = this.props.icon ? <Icon icon={this.props.icon} />  : '',
      className = (this.props.button ? 'button-block button-' + this.props.button : 'button-text') +
                  (this.props.hidden ? ' hidden' : '') + (this.props.disabled ? ' disabled' : '');
    return (

        {this.props.icon ? icon : '' }
        {this.props.label}

    );
  }
});

// Generic menu styles
var menuStyle = {
  border: '1px solid #aaa',
  padding: '4px',
  whiteSpace: 'nowrap',
};

var Menu = React.createClass({
  render: function() {
    return (

        {this.props.children}

    );
  }
});

var menuItemStyle = {
  display: 'block',
};
var MenuItem = React.createClass({
  propTypes: {
    href: React.PropTypes.string,
    label: React.PropTypes.string,
    icon: React.PropTypes.string,
    onClick: React.PropTypes.function,
  },
  getDefaultProps: function() {
    return {
      iconPosition: 'left',
    }
  },
  render: function() {
    var icon = (this.props.icon ? <Icon icon={this.props.icon} fixed /> : null);

    return (

        {this.props.iconPosition == 'left' ? icon : null}
        {this.props.label}
        {this.props.iconPosition == 'left' ? null : icon}

    );
  }
});

var creditAmountStyle = {
  color: '#216C2A',
  fontWeight: 'bold',
  fontSize: '0.8em'
}, estimateStyle = {
  marginLeft : '5px',
  color: '#aaa',
};

var CreditAmount = React.createClass({
  propTypes: {
    amount: React.PropTypes.number,
  },
  render: function() {
    var formattedAmount = lbry.formatCredits(this.props.amount);
    return (

        {formattedAmount}
        { this.props.isEstimate ? (est) : null }

    );
  }
});

var subPageLogoStyle = {
  maxWidth: '150px',
  display: 'block',
  marginTop: '36px',
};
var SubPageLogo = React.createClass({
  render: function() {
    return ;
  }
});<|endoftext|>


<commit_before>var myModule = (function () {
	var NETSCAPE = 'Netscape',
		VISIBILITY_STRINGS = {
			NETSCAPE: {
				VISIBLE: 'show',
				HIDDEN: 'hide'
			},
			OTHER: {
				VISIBLE: 'visible',
				HIDDEN: 'hidden'
			}
		},

		browserName = navigator.appName,
		addScroll = false,
		positionX = 0,
		positionY = 0,

		hideTooltip,
		hideMenu1,
		showMenu1,
		hideMenu2,
		showMenu2;

	if (navigator.userAgent.indexOf('MSIE 5') >= 0 ||
			navigator.userAgent.indexOf('MSIE 6') >= 0) {
		addScroll = true;
	}

	if (browserName === NETSCAPE) {
		document.captureEvents(Event.MOUSEMOVE);
	} else {
		document.onmousemove = onMouseMove;
	}

	function onMouseMove(ev) {
		var TOOLTIP_NAME = 'ToolTip',
			offsetX = 5,
			toolTip;

		if (browserName === NETSCAPE) {
			positionX = ev.pageX - offsetX;
			positionY = ev.pageY;
		} else {
			positionX = event.x - offsetX;
			positionY = event.y;
		}

		toolTipVisibility = document.layers.ToolTip.visibility || document.all.ToolTip.style.visibility;
		if (toolTipVisibility === VISIBILITY_STRINGS.NETSCAPE.VISIBLE ||
				toolTipVisibility === VISIBILITY_STRINGS.OTHER.VISIBLE) {
			showTooltip(TOOLTIP_NAME);
		}
	}

	function showTooltip(layer) {
		var elementWidth = 120,
			windowWidth,
			offsetPositionX = 150,
			offsetLeft = 10,
			offsetTop = 15,
			layerLeft,
			layerTop;

		if (!layer) {
			return;
		}

		documentWidth = window.innerWidth || document.body.clientWidth;
		positionX = keepElementInVisibleSpace(positionX, elementWidth, documentWidth);

		if (browserName !== NETSCAPE && addScroll) {
			positionX = positionX + document.body.scrollLeft;
			positionY = positionY + document.body.scrollTop;
		}

		layerLeft = layer.left || layer.style.pixelLeft;
		layerTop = layer.top || layer.style.pixelTop;

		layerLeft = positionX + offsetLeft;
		layerTop = positionY + offsetTop;

		setLayerVisibility(layer, true);

		function keepElementInVisibleSpace(positionX, elementWidth, documentWidth) {
			if (!documentWidth || positionX + elementWidth > documentWidth) {
				positionX -= offsetPositionX;
			}

			return positionX;
		}
	}

	hideTooltip = function () {
		setLayerVisibility('ToolTip', false);
	};

	hideMenu1 = function () {
		setLayerVisibility('menu1', false);
	};

	showMenu1 = function () {
		setLayerVisibility('menu1', true);
	};

	hideMenu2 = function () {
		setLayerVisibility('menu2', false);
	};

	showMenu2 = function () {
		setLayerVisibility('menu2', true);
	};

	function setLayerVisibility(layer, isVisible) {
		var visibility;

		if (!layer || typeof isVisible !== 'boolean') {
			return;
		}

		if (browserName === NETSCAPE) {
			visibility = isVisible ?
				VISIBILITY_STRINGS.NETSCAPE.VISIBLE : VISIBILITY_STRINGS.NETSCAPE.HIDDEN;
			document.layers[layer].visibility = visibility;
		} else {
			visibility = isVisible ?
				VISIBILITY_STRINGS.OTHER.VISIBLE : VISIBILITY_STRINGS.OTHER.HIDDEN;
			document.all[layer].style.visibility = visibility;
		}
	}

	return {
		showTooltip: showTooltip,
		hideTooltip: hideTooltip,
		showMenu1: showMenu1,
		hideMenu1: hideMenu1,
		showMenu2: showMenu2,
		hideMenu2: hideMenu2
	};
} ());

<commit_msg>update<commit_after>var myModule = (function () {
	var NETSCAPE = 'Netscape',
		VISIBILITY_STRINGS = {
			NETSCAPE: {
				VISIBLE: 'show',
				HIDDEN: 'hide'
			},
			OTHER: {
				VISIBLE: 'visible',
				HIDDEN: 'hidden'
			}
		},

		browserName = navigator.appName,
		addScroll = false,
		positionX = 0,
		positionY = 0,

		hideTooltip,
		hideMenu1,
		showMenu1,
		hideMenu2,
		showMenu2;

	if (navigator.userAgent.indexOf('MSIE 5') >= 0 ||
			navigator.userAgent.indexOf('MSIE 6') >= 0) {
		addScroll = true;
	}

	if (browserName === NETSCAPE) {
		document.captureEvents(Event.MOUSEMOVE);
	} else {
		document.onmousemove = onMouseMove;
	}

	function onMouseMove(ev) {
		var TOOLTIP_NAME = 'ToolTip',
			offsetX = 5,
			toolTip;

		if (browserName === NETSCAPE) {
			positionX = ev.pageX - offsetX;
			positionY = ev.pageY;
		} else {
			positionX = event.x - offsetX;
			positionY = event.y;
		}

		toolTip = document.layers.ToolTip || document.all.ToolTip.style;
		if (toolTip.visibility === VISIBILITY_STRINGS.NETSCAPE.VISIBLE ||
				toolTip.visibility === VISIBILITY_STRINGS.OTHER.VISIBLE) {
			showTooltip(TOOLTIP_NAME);
		}
	}

	function showTooltip(layer) {
		var elementWidth = 120,
			windowWidth,
			offsetPositionX = 150,
			offsetLeft = 10,
			offsetTop = 15,
			layerLeft,
			layerTop;

		if (!layer) {
			return;
		}

		documentWidth = window.innerWidth || document.body.clientWidth;
		positionX = keepElementInVisibleSpace(positionX, elementWidth, documentWidth);

		if (browserName !== NETSCAPE && addScroll) {
			positionX = positionX + document.body.scrollLeft;
			positionY = positionY + document.body.scrollTop;
		}

		layerLeft = layer.left || layer.style.pixelLeft;
		layerTop = layer.top || layer.style.pixelTop;

		layerLeft = positionX + offsetLeft;
		layerTop = positionY + offsetTop;

		setLayerVisibility(layer, true);

		function keepElementInVisibleSpace(positionX, elementWidth, documentWidth) {
			if (!documentWidth || positionX + elementWidth > documentWidth) {
				positionX -= offsetPositionX;
			}

			return positionX;
		}
	}

	hideTooltip = function () {
		setLayerVisibility('ToolTip', false);
	};

	hideMenu1 = function () {
		setLayerVisibility('menu1', false);
	};

	showMenu1 = function () {
		setLayerVisibility('menu1', true);
	};

	hideMenu2 = function () {
		setLayerVisibility('menu2', false);
	};

	showMenu2 = function () {
		setLayerVisibility('menu2', true);
	};

	function setLayerVisibility(layer, isVisible) {
		var visibility;

		if (!layer || typeof isVisible !== 'boolean') {
			return;
		}

		if (browserName === NETSCAPE) {
			visibility = isVisible ?
				VISIBILITY_STRINGS.NETSCAPE.VISIBLE : VISIBILITY_STRINGS.NETSCAPE.HIDDEN;
			document.layers[layer].visibility = visibility;
		} else {
			visibility = isVisible ?
				VISIBILITY_STRINGS.OTHER.VISIBLE : VISIBILITY_STRINGS.OTHER.HIDDEN;
			document.all[layer].style.visibility = visibility;
		}
	}

	return {
		showTooltip: showTooltip,
		hideTooltip: hideTooltip,
		showMenu1: showMenu1,
		hideMenu1: hideMenu1,
		showMenu2: showMenu2,
		hideMenu2: hideMenu2
	};
} ());

<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * This view implements a vertically split display with a draggable divider.
 *
 *                  <<-- sizer -->>
 *
 *  +----------------------++----------------+
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |       leftView       ||   rightView    |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  +----------------------++----------------+
 *
 * @param {!View} leftView The widget to position on the left.
 * @param {!View} rightView The widget to position on the right.
 * @param {!DivView} sizerView The widget that will serve as draggable divider.
 * @constructor
 */
function ResizableVerticalSplitView(leftView, rightView, sizerView) {
  View.call(this);

  this.leftView_ = leftView;
  this.rightView_ = rightView;
  this.sizerView_ = sizerView;

  // Setup the "sizer" so it can be dragged left/right to reposition the
  // vertical split.
  sizerView.getNode().addEventListener(
      "mousedown", this.onDragSizerStart_.bind(this), true);
}

inherits(ResizableVerticalSplitView, View);

// Minimum width to size panels to, in pixels.
ResizableVerticalSplitView.MIN_PANEL_WIDTH = 50;

/**
 * Repositions all of the elements to fit the window.
 */
ResizableVerticalSplitView.prototype.setGeometry = function(
    left, top, width, height) {
  ResizableVerticalSplitView.superClass_.setGeometry.call(
      this, left, top, width, height);

  // If this is the first setGeometry(), initialize the split point at 50%.
  if (!this.rightSplit_)
    this.rightSplit_ = parseInt((width / 2).toFixed(0));

  // Calculate the horizontal split points.
  var rightboxWidth = this.rightSplit_;
  var sizerWidth = this.sizerView_.getWidth();
  var leftboxWidth = width - (rightboxWidth + sizerWidth);

  // Don't let the left pane get too small.
  if (leftboxWidth < ResizableVerticalSplitView.MIN_PANEL_WIDTH) {
    leftboxWidth = ResizableVerticalSplitView.MIN_PANEL_WIDTH;
    rightboxWidth = width - (sizerWidth + leftboxWidth);
  }

  // Position the boxes using calculated split points.
  this.leftView_.setGeometry(left, top, leftboxWidth, height);
  this.sizerView_.setGeometry(this.leftView_.getRight(), top,
                              sizerWidth, height);
  this.rightView_.setGeometry(this.sizerView_.getRight(), top,
                              rightboxWidth, height);
};

ResizableVerticalSplitView.prototype.show = function(isVisible) {
  ResizableVerticalSplitView.superClass_.show.call(this, isVisible);
  this.leftView_.show(isVisible);
  this.sizerView_.show(isVisible);
  this.rightView_.show(isVisible);
};

/**
 * Called once we have clicked into the sizer. Starts capturing the mouse
 * position to implement dragging.
 */
ResizableVerticalSplitView.prototype.onDragSizerStart_ = function(event) {
  this.sizerMouseMoveListener_ = this.onDragSizer.bind(this);
  this.sizerMouseUpListener_ = this.onDragSizerEnd.bind(this);

  window.addEventListener("mousemove", this.sizerMouseMoveListener_, true);
  window.addEventListener("mouseup", this.sizerMouseUpListener_, true);

  event.preventDefault();
};

/**
 * Called when the mouse has moved after dragging started.
 */
ResizableVerticalSplitView.prototype.onDragSizer = function(event) {
  // Convert from page coordinates, to view coordinates.
  this.rightSplit_ = this.getWidth() - (event.pageX - this.getLeft());

  // Avoid shrinking the right box too much.
  this.rightSplit_ = Math.max(
      this.rightSplit_, ResizableVerticalSplitView.MIN_PANEL_WIDTH);

  // Force a layout with the new |rightSplit_|.
  this.setGeometry(
      this.getLeft(), this.getTop(), this.getWidth(), this.getHeight());
};

/**
 * Called once the mouse has been released, and the dragging is over.
 */
ResizableVerticalSplitView.prototype.onDragSizerEnd = function(event) {
  window.removeEventListener("mousemove", this.sizerMouseMoveListener_, true);
  window.removeEventListener("mouseup", this.sizerMouseUpListener_, true);

  this.sizerMouseMoveListener_ = null;
  this.sizerMouseUpListener_ = null;

  event.preventDefault();
};
<commit_msg>Minor aesthetic change on new net internals page: when you resize the window and it is in the requests view, grow the right box (details view) to fit the new space, rather than growing the left box.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * This view implements a vertically split display with a draggable divider.
 *
 *                  <<-- sizer -->>
 *
 *  +----------------------++----------------+
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |       leftView       ||   rightView    |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  |                      ||                |
 *  +----------------------++----------------+
 *
 * @param {!View} leftView The widget to position on the left.
 * @param {!View} rightView The widget to position on the right.
 * @param {!DivView} sizerView The widget that will serve as draggable divider.
 * @constructor
 */
function ResizableVerticalSplitView(leftView, rightView, sizerView) {
  View.call(this);

  this.leftView_ = leftView;
  this.rightView_ = rightView;
  this.sizerView_ = sizerView;

  // Setup the "sizer" so it can be dragged left/right to reposition the
  // vertical split.
  sizerView.getNode().addEventListener(
      "mousedown", this.onDragSizerStart_.bind(this), true);
}

inherits(ResizableVerticalSplitView, View);

// Minimum width to size panels to, in pixels.
ResizableVerticalSplitView.MIN_PANEL_WIDTH = 50;

/**
 * Repositions all of the elements to fit the window.
 */
ResizableVerticalSplitView.prototype.setGeometry = function(
    left, top, width, height) {
  ResizableVerticalSplitView.superClass_.setGeometry.call(
      this, left, top, width, height);

  // If this is the first setGeometry(), initialize the split point at 50%.
  if (!this.leftSplit_)
    this.leftSplit_ = parseInt((width / 2).toFixed(0));

  // Calculate the horizontal split points.
  var leftboxWidth = this.leftSplit_;
  var sizerWidth = this.sizerView_.getWidth();
  var rightboxWidth = width - (leftboxWidth + sizerWidth);

  // Don't let the right pane get too small.
  if (rightboxWidth < ResizableVerticalSplitView.MIN_PANEL_WIDTH) {
    rightboxWidth = ResizableVerticalSplitView.MIN_PANEL_WIDTH;
    leftboxWidth = width - (sizerWidth + rightboxWidth);
  }

  // Position the boxes using calculated split points.
  this.leftView_.setGeometry(left, top, leftboxWidth, height);
  this.sizerView_.setGeometry(this.leftView_.getRight(), top,
                              sizerWidth, height);
  this.rightView_.setGeometry(this.sizerView_.getRight(), top,
                              rightboxWidth, height);
};

ResizableVerticalSplitView.prototype.show = function(isVisible) {
  ResizableVerticalSplitView.superClass_.show.call(this, isVisible);
  this.leftView_.show(isVisible);
  this.sizerView_.show(isVisible);
  this.rightView_.show(isVisible);
};

/**
 * Called once we have clicked into the sizer. Starts capturing the mouse
 * position to implement dragging.
 */
ResizableVerticalSplitView.prototype.onDragSizerStart_ = function(event) {
  this.sizerMouseMoveListener_ = this.onDragSizer.bind(this);
  this.sizerMouseUpListener_ = this.onDragSizerEnd.bind(this);

  window.addEventListener("mousemove", this.sizerMouseMoveListener_, true);
  window.addEventListener("mouseup", this.sizerMouseUpListener_, true);

  event.preventDefault();
};

/**
 * Called when the mouse has moved after dragging started.
 */
ResizableVerticalSplitView.prototype.onDragSizer = function(event) {
  // Convert from page coordinates, to view coordinates.
  this.leftSplit_ = (event.pageX - this.getLeft());

  // Avoid shrinking the left box too much.
  this.leftSplit_ = Math.max(
      this.leftSplit_, ResizableVerticalSplitView.MIN_PANEL_WIDTH);
  // Avoid shrinking the right box too much.
  this.leftSplit_ = Math.min(
      this.leftSplit_,
      this.getWidth() - ResizableVerticalSplitView.MIN_PANEL_WIDTH);

  // Force a layout with the new |leftSplit_|.
  this.setGeometry(
      this.getLeft(), this.getTop(), this.getWidth(), this.getHeight());
};

/**
 * Called once the mouse has been released, and the dragging is over.
 */
ResizableVerticalSplitView.prototype.onDragSizerEnd = function(event) {
  window.removeEventListener("mousemove", this.sizerMouseMoveListener_, true);
  window.removeEventListener("mouseup", this.sizerMouseUpListener_, true);

  this.sizerMouseMoveListener_ = null;
  this.sizerMouseUpListener_ = null;

  event.preventDefault();
};
<|endoftext|>


<commit_before>/**
 * @ngdoc function
 * @name sraApp.controller:MainCtrl
 * @description
 * # MainCtrl
 * Controller of the sraApp
 */
(function() {
    var app = angular.module('mainModule', []);

    app.controller('CustomersController', ['$scope', '$location','$rootScope',
        function ($scope, $location, $rootScope) {
            var sessionIdentifier = sessionStorage.getItem('sessionIdentifier');

            if (sessionIdentifier == '' ||
                sessionIdentifier == null ||
                typeof sessionIdentifier == 'undefined'){

                $rootScope.isUserLoggedIn = false;
                $rootScope.userName = '';
                sessionStorage.setItem('userFullName', '');
                sessionStorage.setItem('sessionIdentifier', '');

                $location.path("/");
            }

            $rootScope.isUserLoggedIn = true;
            $rootScope.userName = sessionStorage.getItem('userFullName');
        }]);

})();<commit_msg>Created a service to fetch the list of customers<commit_after>/**
 * @ngdoc function
 * @name sraApp.controller:MainCtrl
 * @description
 * # MainCtrl
 * Controller of the sraApp
 */
(function() {
    var app = angular.module('mainModule', []);

    app.controller('CustomersController', ['$scope', '$location','$rootScope',
        function ($scope, $location, $rootScope) {
            var sessionIdentifier = sessionStorage.getItem('sessionIdentifier');

            if (sessionIdentifier == '' ||
                sessionIdentifier == null ||
                typeof sessionIdentifier == 'undefined'){

                $rootScope.isUserLoggedIn = false;
                $rootScope.userName = '';
                sessionStorage.setItem('userFullName', '');
                sessionStorage.setItem('sessionIdentifier', '');

                $location.path("/");
            }

            $rootScope.isUserLoggedIn = true;
            $rootScope.userName = sessionStorage.getItem('userFullName');
        }]);

    app.service('CustomerListService', ['$http', '$q', function ($http, $q) {
        this.callCustomerListService = function (requestData) {
            var deferred = $q.defer();
            var json = JSON.stringify(requestData);
            $http.post('customer/list', json)
                .success(function (data, status, headers, config) {
                    deferred.resolve(data);
                })
                .error(function () {
                    deferred.reject("An error occurred while fetching items");
                });
            return deferred.promise;
        }
    }]);

})();<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('options', function() {
  const OptionsPage = options.OptionsPage;

  // The GUID of the loaded credit card.
  var guid_;

  // The CC number of the profile, used to check for changes to the input field.
  var storedCCNumber_;

  // Set to true if the user has edited the CC number field. When saving the
  // CC profile after editing, the stored CC number is saved if the input field
  // has not been modified.
  var hasEditedNumber_;

  /**
   * AutoFillEditCreditCardOverlay class
   * Encapsulated handling of the 'Add Page' overlay page.
   * @class
   */
  function AutoFillEditCreditCardOverlay() {
    OptionsPage.call(this, 'autoFillEditCreditCardOverlay',
                     templateData.autoFillEditCreditCardTitle,
                     'autoFillEditCreditCardOverlay');
  }

  cr.addSingletonGetter(AutoFillEditCreditCardOverlay);

  AutoFillEditCreditCardOverlay.prototype = {
    __proto__: OptionsPage.prototype,

    /**
     * Initializes the page.
     */
    initializePage: function() {
      OptionsPage.prototype.initializePage.call(this);

      var self = this;
      $('autoFillEditCreditCardCancelButton').onclick = function(event) {
        self.dismissOverlay_();
      }
      $('autoFillEditCreditCardApplyButton').onclick = function(event) {
        self.saveCreditCard_();
        self.dismissOverlay_();
      }
      $('creditCardNumber').onkeydown = this.onKeyDown_.bind(this);

      self.guid_ = '';
      self.storedCCNumber_ = '';
      self.hasEditedNumber_ = false;
      self.clearInputFields_();
      self.connectInputEvents_();
      self.setDefaultSelectOptions_();
    },

    /**
     * Handles the keydown event.
     * @private
     */
    onKeyDown_: function(event) {
      // If the user hasn't edited the text yet, delete it all on edit.
      if (!this.hasEditedNumber_ &&
          $('creditCardNumber').value != this.storedCCNumber_) {
        this.hasEditedNumber_ = true;
        $('creditCardNumber').value = '';
      }
    },

    /**
     * Clears any uncommitted input, and dismisses the overlay.
     * @private
     */
    dismissOverlay_: function() {
      this.clearInputFields_();
      this.guid_ = '';
      this.storedCCNumber_ = '';
      this.hasEditedNumber_ = false;
      OptionsPage.clearOverlays();
    },

    /**
     * Aggregates the values in the input fields into an array and sends the
     * array to the AutoFill handler.
     * @private
     */
    saveCreditCard_: function() {
      var creditCard = new Array(5);
      creditCard[0] = this.guid_;
      creditCard[1] = $('nameOnCard').value;
      creditCard[3] = $('expirationMonth').value;
      creditCard[4] = $('expirationYear').value;

      if (this.hasEditedNumber_)
        creditCard[2] = $('creditCardNumber').value;
      else
        creditCard[2] = this.storedCCNumber_;

      chrome.send('setCreditCard', creditCard);
    },

    /**
     * Connects each input field to the inputFieldChanged_() method that enables
     * or disables the 'Ok' button based on whether all the fields are empty or
     * not.
     * @private
     */
    connectInputEvents_: function() {
      var self = this;
      $('nameOnCard').oninput = $('creditCardNumber').oninput =
          $('expirationMonth').onchange = $('expirationYear').onchange =
              function(event) {
                self.inputFieldChanged_();
              }
    },

    /**
     * Checks the values of each of the input fields and disables the 'Ok'
     * button if all of the fields are empty.
     * @private
     */
    inputFieldChanged_: function() {
      var disabled = !$('nameOnCard').value && !$('creditCardNumber').value;
      $('autoFillEditCreditCardApplyButton').disabled = disabled;
    },

    /**
     * Sets the default values of the options in the 'Expiration date' select
     * controls.
     * @private
     */
    setDefaultSelectOptions_: function() {
      // Set the 'Expiration month' default options.
      var expirationMonth = $('expirationMonth');
      expirationMonth.options.length = 0;
      for (var i = 1; i <= 12; ++i) {
        var text;
        if (i < 10)
          text = '0' + i;
        else
          text = i;

        var option = document.createElement('option');
        option.text = text;
        option.value = text;
        expirationMonth.add(option, null);
      }

      // Set the 'Expiration year' default options.
      var expirationYear = $('expirationYear');
      expirationYear.options.length = 0;

      var date = new Date();
      var year = parseInt(date.getFullYear());
      for (var i = 0; i < 10; ++i) {
        var text = year + i;
        var option = document.createElement('option');
        option.text = text;
        option.value = text;
        expirationYear.add(option, null);
      }
    },

    /**
     * Clears the value of each input field.
     * @private
     */
    clearInputFields_: function() {
      $('nameOnCard').value = '';
      $('creditCardNumber').value = '';
      $('expirationMonth').selectedIndex = 0;
      $('expirationYear').selectedIndex = 0;
    },

    /**
     * Sets the value of each input field according to |creditCard|
     * @private
     */
    setInputFields_: function(creditCard) {
      $('nameOnCard').value = creditCard['nameOnCard'];
      $('creditCardNumber').value = creditCard['obfuscatedCardNumber'];

      // The options for the year select control may be out-dated at this point,
      // e.g. the user opened the options page before midnight on New Year's Eve
      // and then loaded a credit card profile to edit in the new year, so
      // reload the select options just to be safe.
      this.setDefaultSelectOptions_();

      var idx = parseInt(creditCard['expirationMonth'], 10);
      $('expirationMonth').selectedIndex = idx - 1;

      expYear = creditCard['expirationYear'];
      var date = new Date();
      var year = parseInt(date.getFullYear());
      for (var i = 0; i < 10; ++i) {
        var text = year + i;
        if (expYear == String(text))
          $('expirationYear').selectedIndex = i;
      }
    },

    /**
     * Loads the credit card data from |creditCard|, sets the input fields based
     * on this data and stores the GUID of the credit card.
     * @private
     */
    loadCreditCard_: function(creditCard) {
      this.setInputFields_(creditCard);
      this.inputFieldChanged_();
      this.guid_ = creditCard['guid'];
      this.storedCCNumber_ = creditCard['creditCardNumber'];
    },
  };

  AutoFillEditCreditCardOverlay.clearInputFields = function(title) {
    AutoFillEditCreditCardOverlay.getInstance().clearInputFields_();
  };

  AutoFillEditCreditCardOverlay.loadCreditCard = function(creditCard) {
    AutoFillEditCreditCardOverlay.getInstance().loadCreditCard_(creditCard);
  };

  AutoFillEditCreditCardOverlay.setTitle = function(title) {
    $('autoFillCreditCardTitle').textContent = title;
  };

  // Export
  return {
    AutoFillEditCreditCardOverlay: AutoFillEditCreditCardOverlay
  };

});
<commit_msg>DOMUI: Handle textInput in addition to keydown for the AF cc input field.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('options', function() {
  const OptionsPage = options.OptionsPage;

  // The GUID of the loaded credit card.
  var guid_;

  // The CC number of the profile, used to check for changes to the input field.
  var storedCCNumber_;

  // Set to true if the user has edited the CC number field. When saving the
  // CC profile after editing, the stored CC number is saved if the input field
  // has not been modified.
  var hasEditedNumber_;

  /**
   * AutoFillEditCreditCardOverlay class
   * Encapsulated handling of the 'Add Page' overlay page.
   * @class
   */
  function AutoFillEditCreditCardOverlay() {
    OptionsPage.call(this, 'autoFillEditCreditCardOverlay',
                     templateData.autoFillEditCreditCardTitle,
                     'autoFillEditCreditCardOverlay');
  }

  cr.addSingletonGetter(AutoFillEditCreditCardOverlay);

  AutoFillEditCreditCardOverlay.prototype = {
    __proto__: OptionsPage.prototype,

    /**
     * Initializes the page.
     */
    initializePage: function() {
      OptionsPage.prototype.initializePage.call(this);

      var self = this;
      $('autoFillEditCreditCardCancelButton').onclick = function(event) {
        self.dismissOverlay_();
      }
      $('autoFillEditCreditCardApplyButton').onclick = function(event) {
        self.saveCreditCard_();
        self.dismissOverlay_();
      }
      $('creditCardNumber').onkeydown = this.onTextInput_.bind(this);
      $('creditCardNumber').addEventListener('textInput',
                                             this.onTextInput_.bind(this));

      self.guid_ = '';
      self.storedCCNumber_ = '';
      self.hasEditedNumber_ = false;
      self.clearInputFields_();
      self.connectInputEvents_();
      self.setDefaultSelectOptions_();
    },

    /**
     * Handles the textInput and keydown events.
     * @private
     */
    onTextInput_: function(event) {
      // For some reason, the textInput event doesn't consider
      // backspace/deletion an input event, so we have to handle those here.
      // 8 - backspace
      // 46 - delete
      if (event.type == 'keydown' && event.keyCode != '8' &&
          event.keyCode != '46')
        return;

      // If the user hasn't edited the text yet, delete it all on edit.
      if (!this.hasEditedNumber_ &&
          $('creditCardNumber').value != this.storedCCNumber_) {
        this.hasEditedNumber_ = true;
        $('creditCardNumber').value = '';
      }
    },

    /**
     * Clears any uncommitted input, and dismisses the overlay.
     * @private
     */
    dismissOverlay_: function() {
      this.clearInputFields_();
      this.guid_ = '';
      this.storedCCNumber_ = '';
      this.hasEditedNumber_ = false;
      OptionsPage.clearOverlays();
    },

    /**
     * Aggregates the values in the input fields into an array and sends the
     * array to the AutoFill handler.
     * @private
     */
    saveCreditCard_: function() {
      var creditCard = new Array(5);
      creditCard[0] = this.guid_;
      creditCard[1] = $('nameOnCard').value;
      creditCard[3] = $('expirationMonth').value;
      creditCard[4] = $('expirationYear').value;

      if (this.hasEditedNumber_)
        creditCard[2] = $('creditCardNumber').value;
      else
        creditCard[2] = this.storedCCNumber_;

      chrome.send('setCreditCard', creditCard);
    },

    /**
     * Connects each input field to the inputFieldChanged_() method that enables
     * or disables the 'Ok' button based on whether all the fields are empty or
     * not.
     * @private
     */
    connectInputEvents_: function() {
      var self = this;
      $('nameOnCard').oninput = $('creditCardNumber').oninput =
          $('expirationMonth').onchange = $('expirationYear').onchange =
              function(event) {
                self.inputFieldChanged_();
              }
    },

    /**
     * Checks the values of each of the input fields and disables the 'Ok'
     * button if all of the fields are empty.
     * @private
     */
    inputFieldChanged_: function() {
      var disabled = !$('nameOnCard').value && !$('creditCardNumber').value;
      $('autoFillEditCreditCardApplyButton').disabled = disabled;
    },

    /**
     * Sets the default values of the options in the 'Expiration date' select
     * controls.
     * @private
     */
    setDefaultSelectOptions_: function() {
      // Set the 'Expiration month' default options.
      var expirationMonth = $('expirationMonth');
      expirationMonth.options.length = 0;
      for (var i = 1; i <= 12; ++i) {
        var text;
        if (i < 10)
          text = '0' + i;
        else
          text = i;

        var option = document.createElement('option');
        option.text = text;
        option.value = text;
        expirationMonth.add(option, null);
      }

      // Set the 'Expiration year' default options.
      var expirationYear = $('expirationYear');
      expirationYear.options.length = 0;

      var date = new Date();
      var year = parseInt(date.getFullYear());
      for (var i = 0; i < 10; ++i) {
        var text = year + i;
        var option = document.createElement('option');
        option.text = text;
        option.value = text;
        expirationYear.add(option, null);
      }
    },

    /**
     * Clears the value of each input field.
     * @private
     */
    clearInputFields_: function() {
      $('nameOnCard').value = '';
      $('creditCardNumber').value = '';
      $('expirationMonth').selectedIndex = 0;
      $('expirationYear').selectedIndex = 0;
    },

    /**
     * Sets the value of each input field according to |creditCard|
     * @private
     */
    setInputFields_: function(creditCard) {
      $('nameOnCard').value = creditCard['nameOnCard'];
      $('creditCardNumber').value = creditCard['obfuscatedCardNumber'];

      // The options for the year select control may be out-dated at this point,
      // e.g. the user opened the options page before midnight on New Year's Eve
      // and then loaded a credit card profile to edit in the new year, so
      // reload the select options just to be safe.
      this.setDefaultSelectOptions_();

      var idx = parseInt(creditCard['expirationMonth'], 10);
      $('expirationMonth').selectedIndex = idx - 1;

      expYear = creditCard['expirationYear'];
      var date = new Date();
      var year = parseInt(date.getFullYear());
      for (var i = 0; i < 10; ++i) {
        var text = year + i;
        if (expYear == String(text))
          $('expirationYear').selectedIndex = i;
      }
    },

    /**
     * Loads the credit card data from |creditCard|, sets the input fields based
     * on this data and stores the GUID of the credit card.
     * @private
     */
    loadCreditCard_: function(creditCard) {
      this.setInputFields_(creditCard);
      this.inputFieldChanged_();
      this.guid_ = creditCard['guid'];
      this.storedCCNumber_ = creditCard['creditCardNumber'];
    },
  };

  AutoFillEditCreditCardOverlay.clearInputFields = function(title) {
    AutoFillEditCreditCardOverlay.getInstance().clearInputFields_();
  };

  AutoFillEditCreditCardOverlay.loadCreditCard = function(creditCard) {
    AutoFillEditCreditCardOverlay.getInstance().loadCreditCard_(creditCard);
  };

  AutoFillEditCreditCardOverlay.setTitle = function(title) {
    $('autoFillCreditCardTitle').textContent = title;
  };

  // Export
  return {
    AutoFillEditCreditCardOverlay: AutoFillEditCreditCardOverlay
  };

});
<|endoftext|>


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

/**
 * Creates an object that has the same API as an Array, but also supports notifications and PhET-iO
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

import arrayRemove from '../../phet-core/js/arrayRemove.js';
import merge from '../../phet-core/js/merge.js';
import PhetioObject from '../../tandem/js/PhetioObject.js';
import Tandem from '../../tandem/js/Tandem.js';
import IOType from '../../tandem/js/types/IOType.js';
import axon from './axon.js';
import AxonArray from './AxonArray.js';
import Emitter from './Emitter.js';
import NumberProperty from './NumberProperty.js';
import ValidatorDef from './ValidatorDef.js';

/**
 * Black box testing is less efficient but more concise and easy to verify correctness.  Used for the rarer methods.
 * @param {Object[]} shallowCopy
 * @param {arrayProxy[]} arrayProxy
 */
const reportDifference = ( shallowCopy, arrayProxy ) => {

  const before = shallowCopy;
  const after = arrayProxy.targetArray.slice();

  for ( let i = 0; i < before.length; i++ ) {
    const beforeElement = before[ i ];
    const afterIndex = after.indexOf( beforeElement );
    if ( afterIndex >= 0 ) {
      before.splice( i, 1 );
      after.splice( afterIndex, 1 );
      i--;
    }
  }
  before.forEach( element => arrayProxy.elementRemovedEmitter.emit( element ) );
  after.forEach( element => arrayProxy.elementAddedEmitter.emit( element ) );
};

// Methods shared by all arrayProxy instances
const methods = {

  /******************************************
   * Overridden Array methods
   *******************************************/

  // @public
  pop() {
    const initialLength = this.targetArray.length;
    const returnValue = Array.prototype.pop.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    initialLength > 0 && this.elementRemovedEmitter.emit( returnValue );
    return returnValue;
  },

  // @public
  shift() {
    const initialLength = this.targetArray.length;
    const returnValue = Array.prototype.shift.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    initialLength > 0 && this.elementRemovedEmitter.emit( returnValue );
    return returnValue;
  },
  push() {
    const returnValue = Array.prototype.push.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    for ( let i = 0; i < arguments.length; i++ ) {
      this.elementAddedEmitter.emit( arguments[ i ] );
    }
    return returnValue;
  },

  // @public
  unshift() {
    const returnValue = Array.prototype.unshift.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    for ( let i = 0; i < arguments.length; i++ ) {
      this.elementAddedEmitter.emit( arguments[ i ] );
    }
    return returnValue;
  },

  // @public
  splice() {
    const returnValue = Array.prototype.splice.apply( this.targetArray, arguments );

    // Set length first so it will be correct in elementListener callbacks
    this.lengthProperty.value = this.length;
    const deletedElements = returnValue;
    for ( let i = 2; i < arguments.length; i++ ) {
      this.elementAddedEmitter.emit( arguments[ i ] );
    }
    deletedElements.forEach( deletedElement => this.elementRemovedEmitter.emit( deletedElement ) );
    return returnValue;
  },

  // @public
  copyWithin() {
    const before = this.targetArray.slice();
    const returnValue = Array.prototype.copyWithin.apply( this.targetArray, arguments );
    reportDifference( before, this );
    return returnValue;
  },

  // @public
  fill() {
    const before = this.targetArray.slice();
    const returnValue = Array.prototype.fill.apply( this.targetArray, arguments );
    reportDifference( before, this );
    return returnValue;
  },

  /******************************************
   * For compatibility with ObservableArray
   * NOTE: consider deleting after migration
   *******************************************/

  // @public
  get: function( index ) {return this[ index ];},

  // @public
  addItemAddedListener: function( listener ) { this.elementAddedEmitter.addListener( listener ); },

  // @public
  removeItemAddedListener: function( listener ) { this.elementAddedEmitter.removeListener( listener ); },

  // @public
  addItemRemovedListener: function( listener ) { this.elementRemovedEmitter.addListener( listener ); },

  // @public
  removeItemRemovedListener: function( listener ) { this.elementRemovedEmitter.removeListener( listener ); },

  // @public
  add: function( element ) { this.push( element );},

  // @public
  addAll: function( elements ) { this.push( ...elements );},

  // @public
  remove: function( element ) { this.includes( element ) && arrayRemove( this, element );},

  // @public
  removeAll: function( elements ) {
    elements.forEach( element => this.includes( element ) && arrayRemove( this, element ) );
  },

  // @public
  clear: function() {
    while ( this.length > 0 ) {
      this.pop();
    }
  },

  // @public
  count: function( predicate ) {
    let count = 0;
    for ( let i = 0; i < this.length; i++ ) {
      if ( predicate( this[ i ] ) ) {
        count++;
      }
    }
    return count;
  },

  // @public
  find: function( predicate, fromIndex ) {
    assert && ( fromIndex !== undefined ) && assert( typeof fromIndex === 'number', 'fromIndex must be numeric, if provided' );
    assert && ( typeof fromIndex === 'number' ) && assert( fromIndex >= 0 && fromIndex < this.length,
      `fromIndex out of bounds: ${fromIndex}` );
    return _.find( this, predicate, fromIndex );
  },

  // @public
  getArrayCopy: function() {return this.slice();},

  // @public
  shuffle: function( random ) {
    assert && assert( random, 'random must be supplied' );

    // preserve the same _array reference in case any clients got a reference to it with getArray()
    const shuffled = random.shuffle( this );
    this.length = 0;
    Array.prototype.push.apply( this, shuffled );
  },

  // TODO: This seems important to eliminate
  // @public
  getArray: function() {return this;},

  /******************************************
   * PhET-iO
   *******************************************/

  // @public
  toStateObject: function() {
    return { array: this.map( item => this.phetioElementType.toStateObject( item ) ) };
  },

  // @public
  applyState: function( stateObject ) {
    this.length = 0;
    const elements = stateObject.array.map( paramStateObject => this.phetioElementType.fromStateObject( paramStateObject ) );
    this.push( ...elements );
  },

  // @public
  dispose: function() {
    this.elementAddedEmitter.dispose();
    this.elementRemovedEmitter.dispose();
    this.lengthProperty.dispose();
    this.axonArrayPhetioObject && this.axonArrayPhetioObject.dispose();
  }
};

/**
 * @param {Object} [options]
 * @returns {[]}
 */
const createArrayProxy = options => {

  if ( options && options.hasOwnProperty( 'length' ) ) {
    assert && assert( !options.hasOwnProperty( 'elements' ), 'options.elements and options.length are mutually exclusive' );
  }

  // If the options supplied the phetioElementType, it is passed through as a phetioType to the Emitter parameter
  // const isPhetioElementTypeProvided = options && options.hasOwnProperty( 'phetioElementType' );

  options = merge( {

    // Also supports phetioType or validator options.  If both are supplied, only the phetioType is respected

    length: 0,
    elements: [],
    tandem: Tandem.OPTIONAL
  }, options );

  let emitterParameterOptions = null;
  if ( options.phetioType ) {

    assert && assert( options.phetioType.typeName.startsWith( 'ArrayProxyIO' ) );
    emitterParameterOptions = { name: 'value', phetioType: options.phetioType.parameterTypes[ 0 ] };
  }
  else if ( ValidatorDef.isValidValidator( options ) ) {
    const validator = _.pick( options, ValidatorDef.VALIDATOR_KEYS );
    emitterParameterOptions = merge( { name: 'value' }, validator );
  }
  else {
    emitterParameterOptions = merge( { name: 'value' }, { isValidValue: _.stubTrue } );
  }

  // notifies when an element has been added
  const elementAddedEmitter = new Emitter( {
    tandem: options.tandem.createTandem( 'elementAddedEmitter' ),
    parameters: [ emitterParameterOptions ]
  } );

  // notifies when an element has been removed
  const elementRemovedEmitter = new Emitter( {
    tandem: options.tandem.createTandem( 'elementRemovedEmitter' ),
    parameters: [ emitterParameterOptions ]
  } );

  // observe this, but don't set it. Updated when Array modifiers are called (except array.length=...)
  const lengthProperty = new NumberProperty( 0, {
    numberType: 'Integer',
    tandem: options.tandem.createTandem( 'lengthProperty' ),
    phetioReadOnly: true
  } );

  const targetArray = [];

  const arrayProxy = new Proxy( targetArray, {
    get: function( target, key, receiver ) {
      if ( methods.hasOwnProperty( key ) ) {
        return methods[ key ];
      }
      else {
        return Reflect.get( target, key, receiver );
      }
    },
    set: function( array, key, newValue ) {
      const oldValue = array[ key ];
      // console.log( `Changing ${key} (type===${typeof key}), from ${oldValue} to ${newValue}` );

      let removedElements = null;
      // See which items are removed
      if ( key === 'length' ) {
        removedElements = array.slice( newValue );
      }

      const returnValue = Reflect.set( array, key, newValue );
      const parsed = parseInt( key, 10 );
      if ( !isNaN( parsed ) ) {
        if ( oldValue !== undefined ) {
          elementRemovedEmitter.emit( array[ key ] );
        }
        if ( newValue !== undefined ) {
          elementAddedEmitter.emit( newValue );
        }
      }
      else if ( key === 'length' ) {
        lengthProperty.value = newValue;

        removedElements.forEach( element => elementRemovedEmitter.emit( element ) );
      }
      return returnValue;
    },
    deleteProperty: function( array, key ) {
      // console.log( `deleteProperty ${key}, ${typeof key}` );
      const parsed = parseInt( key, 10 );

      let removed;
      if ( !isNaN( parsed ) ) {
        removed = array[ key ];
      }
      const returnValue = Reflect.deleteProperty( array, key );
      if ( removed !== undefined ) {
        elementRemovedEmitter.emit( removed );
      }

      return returnValue;
    }
  } );

  // @private - Make it possible to use the targetArray in the overridden methods above.
  arrayProxy.targetArray = targetArray;

  // @public (listen only)
  arrayProxy.elementAddedEmitter = elementAddedEmitter;
  arrayProxy.elementRemovedEmitter = elementRemovedEmitter;
  arrayProxy.lengthProperty = lengthProperty;

  if ( options.length >= 0 ) {
    arrayProxy.length = options.length;
  }
  if ( options.elements.length > 0 ) {
    Array.prototype.push.apply( arrayProxy, options.elements );
  }

  // TODO: Move to "prototype" above or drop support
  arrayProxy.reset = () => {
    arrayProxy.length = 0;
    if ( options.length >= 0 ) {
      arrayProxy.length = options.length;
    }
    if ( options.elements.length > 0 ) {
      Array.prototype.push.apply( arrayProxy, options.elements );
    }
  };

  /******************************************
   * PhET-iO support
   *******************************************/
  if ( options.tandem.supplied ) {
    arrayProxy.phetioElementType = options.phetioType.parameterTypes[ 0 ];

    // @private - for managing state in phet-io
    // Use the same tandem and phetioState options so it can "masquerade" as the real object.  When PhetioObject is a mixin this can be changed.
    arrayProxy.axonArrayPhetioObject = new ArrayProxyPhetioObject( arrayProxy, options );
  }

  return arrayProxy;
};

/**
 * Manages state save/load.  ArrayProxy uses Proxy and hence cannot be instrumented as a PhetioObject.  This type
 * provides that functionality.
 */
class ArrayProxyPhetioObject extends PhetioObject {

  /**
   * @param {Object} arrayProxy
   * @param {Object} [options] - same as the options to the parent AxonArray
   */
  constructor( arrayProxy, options ) {

    options = merge( {
      phetioType: ArrayProxyIO
    }, options );

    super( options );

    // @private
    this.arrayProxy = arrayProxy;
  }
}

// @public (read-only) (ArrayProxyIO)
AxonArray.ArrayProxyPhetioObject = ArrayProxyPhetioObject;

// {Map.<cacheKey:function(new:ArrayProxyIO), function(new:ArrayProxyIO)>} - Cache each parameterized ArrayProxyIO
// based on the parameter type, so that it is only created once.
const cache = new Map();

/**
 * ArrayProxyIO is the IO Type for AxonArray. It delegates most of its implementation to AxonArray.
 * Instead of being a parametric type, it leverages the phetioElementType on AxonArray.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
const ArrayProxyIO = parameterType => {
  if ( !cache.has( parameterType ) ) {
    cache.set( parameterType, new IOType( `ArrayProxyIO<${parameterType.typeName}>`, {
      parameterTypes: [ parameterType ],
      valueType: ArrayProxyPhetioObject,
      toStateObject: axonArrayPhetioObject => axonArrayPhetioObject.arrayProxy.toStateObject(),
      applyState: ( axonArrayPhetioObject, state ) => axonArrayPhetioObject.arrayProxy.applyState( state )
    } ) );
  }
  return cache.get( parameterType );
};

// @public
createArrayProxy.ArrayProxyIO = ArrayProxyIO;

axon.register( 'createArrayProxy', createArrayProxy );
export default createArrayProxy;<commit_msg>delete spurious @author annotation, https://github.com/phetsims/axon/issues/330<commit_after>// Copyright 2020, University of Colorado Boulder

/**
 * Creates an object that has the same API as an Array, but also supports notifications and PhET-iO
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

import arrayRemove from '../../phet-core/js/arrayRemove.js';
import merge from '../../phet-core/js/merge.js';
import PhetioObject from '../../tandem/js/PhetioObject.js';
import Tandem from '../../tandem/js/Tandem.js';
import IOType from '../../tandem/js/types/IOType.js';
import axon from './axon.js';
import AxonArray from './AxonArray.js';
import Emitter from './Emitter.js';
import NumberProperty from './NumberProperty.js';
import ValidatorDef from './ValidatorDef.js';

/**
 * Black box testing is less efficient but more concise and easy to verify correctness.  Used for the rarer methods.
 * @param {Object[]} shallowCopy
 * @param {arrayProxy[]} arrayProxy
 */
const reportDifference = ( shallowCopy, arrayProxy ) => {

  const before = shallowCopy;
  const after = arrayProxy.targetArray.slice();

  for ( let i = 0; i < before.length; i++ ) {
    const beforeElement = before[ i ];
    const afterIndex = after.indexOf( beforeElement );
    if ( afterIndex >= 0 ) {
      before.splice( i, 1 );
      after.splice( afterIndex, 1 );
      i--;
    }
  }
  before.forEach( element => arrayProxy.elementRemovedEmitter.emit( element ) );
  after.forEach( element => arrayProxy.elementAddedEmitter.emit( element ) );
};

// Methods shared by all arrayProxy instances
const methods = {

  /******************************************
   * Overridden Array methods
   *******************************************/

  // @public
  pop() {
    const initialLength = this.targetArray.length;
    const returnValue = Array.prototype.pop.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    initialLength > 0 && this.elementRemovedEmitter.emit( returnValue );
    return returnValue;
  },

  // @public
  shift() {
    const initialLength = this.targetArray.length;
    const returnValue = Array.prototype.shift.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    initialLength > 0 && this.elementRemovedEmitter.emit( returnValue );
    return returnValue;
  },
  push() {
    const returnValue = Array.prototype.push.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    for ( let i = 0; i < arguments.length; i++ ) {
      this.elementAddedEmitter.emit( arguments[ i ] );
    }
    return returnValue;
  },

  // @public
  unshift() {
    const returnValue = Array.prototype.unshift.apply( this.targetArray, arguments );
    this.lengthProperty.value = this.length;
    for ( let i = 0; i < arguments.length; i++ ) {
      this.elementAddedEmitter.emit( arguments[ i ] );
    }
    return returnValue;
  },

  // @public
  splice() {
    const returnValue = Array.prototype.splice.apply( this.targetArray, arguments );

    // Set length first so it will be correct in elementListener callbacks
    this.lengthProperty.value = this.length;
    const deletedElements = returnValue;
    for ( let i = 2; i < arguments.length; i++ ) {
      this.elementAddedEmitter.emit( arguments[ i ] );
    }
    deletedElements.forEach( deletedElement => this.elementRemovedEmitter.emit( deletedElement ) );
    return returnValue;
  },

  // @public
  copyWithin() {
    const before = this.targetArray.slice();
    const returnValue = Array.prototype.copyWithin.apply( this.targetArray, arguments );
    reportDifference( before, this );
    return returnValue;
  },

  // @public
  fill() {
    const before = this.targetArray.slice();
    const returnValue = Array.prototype.fill.apply( this.targetArray, arguments );
    reportDifference( before, this );
    return returnValue;
  },

  /******************************************
   * For compatibility with ObservableArray
   * NOTE: consider deleting after migration
   *******************************************/

  // @public
  get: function( index ) {return this[ index ];},

  // @public
  addItemAddedListener: function( listener ) { this.elementAddedEmitter.addListener( listener ); },

  // @public
  removeItemAddedListener: function( listener ) { this.elementAddedEmitter.removeListener( listener ); },

  // @public
  addItemRemovedListener: function( listener ) { this.elementRemovedEmitter.addListener( listener ); },

  // @public
  removeItemRemovedListener: function( listener ) { this.elementRemovedEmitter.removeListener( listener ); },

  // @public
  add: function( element ) { this.push( element );},

  // @public
  addAll: function( elements ) { this.push( ...elements );},

  // @public
  remove: function( element ) { this.includes( element ) && arrayRemove( this, element );},

  // @public
  removeAll: function( elements ) {
    elements.forEach( element => this.includes( element ) && arrayRemove( this, element ) );
  },

  // @public
  clear: function() {
    while ( this.length > 0 ) {
      this.pop();
    }
  },

  // @public
  count: function( predicate ) {
    let count = 0;
    for ( let i = 0; i < this.length; i++ ) {
      if ( predicate( this[ i ] ) ) {
        count++;
      }
    }
    return count;
  },

  // @public
  find: function( predicate, fromIndex ) {
    assert && ( fromIndex !== undefined ) && assert( typeof fromIndex === 'number', 'fromIndex must be numeric, if provided' );
    assert && ( typeof fromIndex === 'number' ) && assert( fromIndex >= 0 && fromIndex < this.length,
      `fromIndex out of bounds: ${fromIndex}` );
    return _.find( this, predicate, fromIndex );
  },

  // @public
  getArrayCopy: function() {return this.slice();},

  // @public
  shuffle: function( random ) {
    assert && assert( random, 'random must be supplied' );

    // preserve the same _array reference in case any clients got a reference to it with getArray()
    const shuffled = random.shuffle( this );
    this.length = 0;
    Array.prototype.push.apply( this, shuffled );
  },

  // TODO: This seems important to eliminate
  // @public
  getArray: function() {return this;},

  /******************************************
   * PhET-iO
   *******************************************/

  // @public
  toStateObject: function() {
    return { array: this.map( item => this.phetioElementType.toStateObject( item ) ) };
  },

  // @public
  applyState: function( stateObject ) {
    this.length = 0;
    const elements = stateObject.array.map( paramStateObject => this.phetioElementType.fromStateObject( paramStateObject ) );
    this.push( ...elements );
  },

  // @public
  dispose: function() {
    this.elementAddedEmitter.dispose();
    this.elementRemovedEmitter.dispose();
    this.lengthProperty.dispose();
    this.axonArrayPhetioObject && this.axonArrayPhetioObject.dispose();
  }
};

/**
 * @param {Object} [options]
 * @returns {[]}
 */
const createArrayProxy = options => {

  if ( options && options.hasOwnProperty( 'length' ) ) {
    assert && assert( !options.hasOwnProperty( 'elements' ), 'options.elements and options.length are mutually exclusive' );
  }

  // If the options supplied the phetioElementType, it is passed through as a phetioType to the Emitter parameter
  // const isPhetioElementTypeProvided = options && options.hasOwnProperty( 'phetioElementType' );

  options = merge( {

    // Also supports phetioType or validator options.  If both are supplied, only the phetioType is respected

    length: 0,
    elements: [],
    tandem: Tandem.OPTIONAL
  }, options );

  let emitterParameterOptions = null;
  if ( options.phetioType ) {

    assert && assert( options.phetioType.typeName.startsWith( 'ArrayProxyIO' ) );
    emitterParameterOptions = { name: 'value', phetioType: options.phetioType.parameterTypes[ 0 ] };
  }
  else if ( ValidatorDef.isValidValidator( options ) ) {
    const validator = _.pick( options, ValidatorDef.VALIDATOR_KEYS );
    emitterParameterOptions = merge( { name: 'value' }, validator );
  }
  else {
    emitterParameterOptions = merge( { name: 'value' }, { isValidValue: _.stubTrue } );
  }

  // notifies when an element has been added
  const elementAddedEmitter = new Emitter( {
    tandem: options.tandem.createTandem( 'elementAddedEmitter' ),
    parameters: [ emitterParameterOptions ]
  } );

  // notifies when an element has been removed
  const elementRemovedEmitter = new Emitter( {
    tandem: options.tandem.createTandem( 'elementRemovedEmitter' ),
    parameters: [ emitterParameterOptions ]
  } );

  // observe this, but don't set it. Updated when Array modifiers are called (except array.length=...)
  const lengthProperty = new NumberProperty( 0, {
    numberType: 'Integer',
    tandem: options.tandem.createTandem( 'lengthProperty' ),
    phetioReadOnly: true
  } );

  const targetArray = [];

  const arrayProxy = new Proxy( targetArray, {
    get: function( target, key, receiver ) {
      if ( methods.hasOwnProperty( key ) ) {
        return methods[ key ];
      }
      else {
        return Reflect.get( target, key, receiver );
      }
    },
    set: function( array, key, newValue ) {
      const oldValue = array[ key ];
      // console.log( `Changing ${key} (type===${typeof key}), from ${oldValue} to ${newValue}` );

      let removedElements = null;
      // See which items are removed
      if ( key === 'length' ) {
        removedElements = array.slice( newValue );
      }

      const returnValue = Reflect.set( array, key, newValue );
      const parsed = parseInt( key, 10 );
      if ( !isNaN( parsed ) ) {
        if ( oldValue !== undefined ) {
          elementRemovedEmitter.emit( array[ key ] );
        }
        if ( newValue !== undefined ) {
          elementAddedEmitter.emit( newValue );
        }
      }
      else if ( key === 'length' ) {
        lengthProperty.value = newValue;

        removedElements.forEach( element => elementRemovedEmitter.emit( element ) );
      }
      return returnValue;
    },
    deleteProperty: function( array, key ) {
      // console.log( `deleteProperty ${key}, ${typeof key}` );
      const parsed = parseInt( key, 10 );

      let removed;
      if ( !isNaN( parsed ) ) {
        removed = array[ key ];
      }
      const returnValue = Reflect.deleteProperty( array, key );
      if ( removed !== undefined ) {
        elementRemovedEmitter.emit( removed );
      }

      return returnValue;
    }
  } );

  // @private - Make it possible to use the targetArray in the overridden methods above.
  arrayProxy.targetArray = targetArray;

  // @public (listen only)
  arrayProxy.elementAddedEmitter = elementAddedEmitter;
  arrayProxy.elementRemovedEmitter = elementRemovedEmitter;
  arrayProxy.lengthProperty = lengthProperty;

  if ( options.length >= 0 ) {
    arrayProxy.length = options.length;
  }
  if ( options.elements.length > 0 ) {
    Array.prototype.push.apply( arrayProxy, options.elements );
  }

  // TODO: Move to "prototype" above or drop support
  arrayProxy.reset = () => {
    arrayProxy.length = 0;
    if ( options.length >= 0 ) {
      arrayProxy.length = options.length;
    }
    if ( options.elements.length > 0 ) {
      Array.prototype.push.apply( arrayProxy, options.elements );
    }
  };

  /******************************************
   * PhET-iO support
   *******************************************/
  if ( options.tandem.supplied ) {
    arrayProxy.phetioElementType = options.phetioType.parameterTypes[ 0 ];

    // @private - for managing state in phet-io
    // Use the same tandem and phetioState options so it can "masquerade" as the real object.  When PhetioObject is a mixin this can be changed.
    arrayProxy.axonArrayPhetioObject = new ArrayProxyPhetioObject( arrayProxy, options );
  }

  return arrayProxy;
};

/**
 * Manages state save/load.  ArrayProxy uses Proxy and hence cannot be instrumented as a PhetioObject.  This type
 * provides that functionality.
 */
class ArrayProxyPhetioObject extends PhetioObject {

  /**
   * @param {Object} arrayProxy
   * @param {Object} [options] - same as the options to the parent AxonArray
   */
  constructor( arrayProxy, options ) {

    options = merge( {
      phetioType: ArrayProxyIO
    }, options );

    super( options );

    // @private
    this.arrayProxy = arrayProxy;
  }
}

// @public (read-only) (ArrayProxyIO)
AxonArray.ArrayProxyPhetioObject = ArrayProxyPhetioObject;

// {Map.<cacheKey:function(new:ArrayProxyIO), function(new:ArrayProxyIO)>} - Cache each parameterized ArrayProxyIO
// based on the parameter type, so that it is only created once.
const cache = new Map();

/**
 * ArrayProxyIO is the IO Type for AxonArray. It delegates most of its implementation to AxonArray.
 * Instead of being a parametric type, it leverages the phetioElementType on AxonArray.
 */
const ArrayProxyIO = parameterType => {
  if ( !cache.has( parameterType ) ) {
    cache.set( parameterType, new IOType( `ArrayProxyIO<${parameterType.typeName}>`, {
      parameterTypes: [ parameterType ],
      valueType: ArrayProxyPhetioObject,
      toStateObject: axonArrayPhetioObject => axonArrayPhetioObject.arrayProxy.toStateObject(),
      applyState: ( axonArrayPhetioObject, state ) => axonArrayPhetioObject.arrayProxy.applyState( state )
    } ) );
  }
  return cache.get( parameterType );
};

// @public
createArrayProxy.ArrayProxyIO = ArrayProxyIO;

axon.register( 'createArrayProxy', createArrayProxy );
export default createArrayProxy;<|endoftext|>


<commit_before>(function(global, document) {

  //  Declare constructor
  //  Returns an instance object.
  function DataDoc(parameters, options) {

    //ok, so we're setting the new
    return new DataDoc.dd.init( parameters.video_id, parameters.fullscreen_id, parameters.embed_id, options || null );

  };

  //  Declare a shortcut (DataDoc.dd) to and a definition of
  //  the new prototype for our DataDoc constructor
  DataDoc.dd = DataDoc.prototype = {

    init : function( entity, fullscreen_container_id, embed_div, options ) {

      this.video = Popcorn("#"+entity);
      this.pauseBtnID = "#pause_button";
      this.overlay = document.getElementById("overlays");

      this["video"] = this.video; //expose the video property so it can be accessed by external scripts

      var hasCSS = null;
      if(options){
        hasCSS = options.control_style;
      }

      customcontrols(this["video"].video, {"fullscreen_container":fullscreen_container_id, "embed_container":embed_div, "control_style":hasCSS});

      this["customcontrols"] = true;

      return this;
    },

    addSalt : function(jsonObj){
      var allSalt = jsonObj;
      if (!(jsonObj instanceof Array)){
        allSalt = new Array(jsonObj);
      }
      for(var p=0; p<allSalt.length; p++){
        this.video.salt(allSalt[p]);
      }
    },

    addCaramel : function(jsonObj){
      var allSugar = jsonObj;
      if (!(jsonObj instanceof Array)){
        allSugar = new Array(jsonObj);
      }
      for(var p=0; p<allSugar.length; p++){
        allSugar[p].pause_button_id = this.pauseBtnID;
        this.video.caramel(allSugar[p]);
      }
    },

    addOverlays : function(overlayType, jsonObj){

      if(overlayType == "labels"){
        //let's start by handling MVP text
        this.addFlexText(jsonObj.flex_text, this.overlay, this);
      }
      if(overlayType == "charts"){
        //and first generation Data Docs charts
        this.addChalkboardCharts(jsonObj.graph_list);
      }

    },

    buildFlex : function(textString, theStyle){
      var text_el = document.createElement("div");
      text_el.style.position = "relative";
      text_el.style.display = "block";

      if(theStyle.font == "MVP_Hand"){
        text_el.appendChild(returnHandDrawn(textString, theStyle.size));
      }else{

        text_el.style.fontFamily = new String(theStyle.font);

        text_el.style.fontSize = theStyle.size+'px';
        text_el.innerHTML = textString;
      }

      return(text_el);
    },

    addFlexText : function(theJSON){

      //create the default "styles" - so far we want to support the "MVP_Hand" font, and webfonts
      var theFont = theJSON.stylesheet.fontFamily;

      var theSize = theJSON.stylesheet.fontSize;

      var numBlocks = theJSON.text_blocks;

      //get each text block in the list
      for(var i=0; i<numBlocks.length; i++){

        var blockData = numBlocks[i];

        //every block is a single parent div
        var blockDiv = document.createElement("div");
        blockDiv.id = numBlocks[i].divID ? numBlocks[i].divID : "flex_text_"+i;
        blockDiv.style.position = "absolute";
        blockDiv.style.top = numBlocks[i].top_left[0]+"px";
        blockDiv.style.left = numBlocks[i].top_left[1]+"px";

        //add block div to page
        this.overlay.appendChild(blockDiv);

        //call salt plugin
        this.addSalt({
          "start":numBlocks[i].start_end[0],
          "end":numBlocks[i].start_end[1],
          "target":blockDiv.id
        });

        var text_elems = blockData.text_list;
        //get each element in the text block
        for(var j=0; j<text_elems.length; j++){

          //create the style object for this
          var styleObj = {};
          styleObj.font = text_elems[j].fontFamily ? text_elems[j].fontFamily : theFont;
          styleObj.size = text_elems[j].fontSize ? text_elems[j].fontSize : theSize;

          blockDiv.appendChild(this.buildFlex(text_elems[j].text, styleObj));

        }

      }

    },

    addChalkboardCharts : function(data){

      for (var p=0; p<data.chart_list.length; p++){

        var chartInfo = data.chart_list[p];

        var chartDiv = document.createElement("div");
        chartDiv.id = chartInfo.meta.divID ? chartInfo.meta.divID : "chart_"+p;
        chartDiv.style.position = 'absolute';
        chartDiv.style.width = Number(chartInfo.meta.width_height[0])+'px';
        chartDiv.style.height = Number(chartInfo.meta.width_height[1])+'px';
        chartDiv.style.top = Number(chartInfo.meta.top_left[0])+'px';
        chartDiv.style.left = Number(chartInfo.meta.top_left[0])+'px';

        this.overlay.appendChild(chartDiv);

        //call salt plugin on each of these chart divs immediately; let data fill in later
        this.addSalt({
          start:Number(chartInfo.meta.start_end[0]),
          end:Number(chartInfo.meta.start_end[1]),
          target:chartDiv.id
        });

        //takes the metadata about the chart and the target div, and populates it with the appropriate chart!
        loadFREDChart(chartInfo.chart_info, chartDiv, 'gif');

      }

    }

  };//end prototype declaration

  //  Extend constructor prototype to instance prototype
  //  Allows chaining methods to instances
 DataDoc.dd.init.prototype = DataDoc.dd;

  //  Exposes DataDocs to global context
  global.DataDoc = DataDoc;

})(window, window.document);
<commit_msg>Added support for 'textAlign' parameter in addFlexText method, with default of left.<commit_after>(function(global, document) {

  //  Declare constructor
  //  Returns an instance object.
  function DataDoc(parameters, options) {

    //ok, so we're setting the new
    return new DataDoc.dd.init( parameters.video_id, parameters.fullscreen_id, parameters.embed_id, options || null );

  };

  //  Declare a shortcut (DataDoc.dd) to and a definition of
  //  the new prototype for our DataDoc constructor
  DataDoc.dd = DataDoc.prototype = {

    init : function( entity, fullscreen_container_id, embed_div, options ) {

      this.video = Popcorn("#"+entity);
      this.pauseBtnID = "#pause_button";
      this.overlay = document.getElementById("overlays");

      this["video"] = this.video; //expose the video property so it can be accessed by external scripts

      var hasCSS = null;
      if(options){
        hasCSS = options.control_style;
      }

      customcontrols(this["video"].video, {"fullscreen_container":fullscreen_container_id, "embed_container":embed_div, "control_style":hasCSS});

      this["customcontrols"] = true;

      return this;
    },

    addSalt : function(jsonObj){
      var allSalt = jsonObj;
      if (!(jsonObj instanceof Array)){
        allSalt = new Array(jsonObj);
      }
      for(var p=0; p<allSalt.length; p++){
        this.video.salt(allSalt[p]);
      }
    },

    addCaramel : function(jsonObj){
      var allSugar = jsonObj;
      if (!(jsonObj instanceof Array)){
        allSugar = new Array(jsonObj);
      }
      for(var p=0; p<allSugar.length; p++){
        allSugar[p].pause_button_id = this.pauseBtnID;
        this.video.caramel(allSugar[p]);
      }
    },

    addOverlays : function(overlayType, jsonObj){

      if(overlayType == "labels"){
        //let's start by handling MVP text
        this.addFlexText(jsonObj.flex_text, this.overlay, this);
      }
      if(overlayType == "charts"){
        //and first generation Data Docs charts
        this.addChalkboardCharts(jsonObj.graph_list);
      }

    },

    buildFlex : function(textString, theStyle){
      var text_el = document.createElement("div");
      text_el.style.position = "relative";
      text_el.style.display = "block";

      if(theStyle.font == "MVP_Hand"){
        text_el.appendChild(returnHandDrawn(textString, theStyle.size));
      }else{

        text_el.style.fontFamily = new String(theStyle.font);
        text_el.style.textAlign = theStyle.align;
        text_el.style.fontSize = theStyle.size+'px';
        text_el.innerHTML = textString;
      }

      return(text_el);
    },

    addFlexText : function(theJSON){

      //create the default "styles" - so far we want to support the "MVP_Hand" font, and webfonts
      var theFont = theJSON.stylesheet.fontFamily;

      var theSize = theJSON.stylesheet.fontSize;

      var theAlign = theJSON.stylesheet.textAlign ? theJSON.stylesheet.textAlign : "left";

      var numBlocks = theJSON.text_blocks;

      //get each text block in the list
      for(var i=0; i<numBlocks.length; i++){

        var blockData = numBlocks[i];

        //every block is a single parent div
        var blockDiv = document.createElement("div");
        blockDiv.id = numBlocks[i].divID ? numBlocks[i].divID : "flex_text_"+i;
        blockDiv.style.position = "absolute";
        blockDiv.style.top = numBlocks[i].top_left[0]+"px";
        blockDiv.style.left = numBlocks[i].top_left[1]+"px";

        //add block div to page
        this.overlay.appendChild(blockDiv);

        //call salt plugin
        this.addSalt({
          "start":numBlocks[i].start_end[0],
          "end":numBlocks[i].start_end[1],
          "target":blockDiv.id
        });

        var text_elems = blockData.text_list;
        //get each element in the text block
        for(var j=0; j<text_elems.length; j++){

          //create the style object for this
          var styleObj = {};
          styleObj.font = text_elems[j].fontFamily ? text_elems[j].fontFamily : theFont;
          styleObj.size = text_elems[j].fontSize ? text_elems[j].fontSize : theSize;
          styleObj.align = text_elems[j].textAlign ? text_elems[j].textAlign: theAlign;

          blockDiv.appendChild(this.buildFlex(text_elems[j].text, styleObj));

        }

      }

    },

    addChalkboardCharts : function(data){

      for (var p=0; p<data.chart_list.length; p++){

        var chartInfo = data.chart_list[p];

        var chartDiv = document.createElement("div");
        chartDiv.id = chartInfo.meta.divID ? chartInfo.meta.divID : "chart_"+p;
        chartDiv.style.position = 'absolute';
        chartDiv.style.width = Number(chartInfo.meta.width_height[0])+'px';
        chartDiv.style.height = Number(chartInfo.meta.width_height[1])+'px';
        chartDiv.style.top = Number(chartInfo.meta.top_left[0])+'px';
        chartDiv.style.left = Number(chartInfo.meta.top_left[0])+'px';

        this.overlay.appendChild(chartDiv);

        //call salt plugin on each of these chart divs immediately; let data fill in later
        this.addSalt({
          start:Number(chartInfo.meta.start_end[0]),
          end:Number(chartInfo.meta.start_end[1]),
          target:chartDiv.id
        });

        //takes the metadata about the chart and the target div, and populates it with the appropriate chart!
        loadFREDChart(chartInfo.chart_info, chartDiv, 'gif');

      }

    }

  };//end prototype declaration

  //  Extend constructor prototype to instance prototype
  //  Allows chaining methods to instances
 DataDoc.dd.init.prototype = DataDoc.dd;

  //  Exposes DataDocs to global context
  global.DataDoc = DataDoc;

})(window, window.document);
<|endoftext|>


<commit_before>$(document).ready(function() {
    $.ajax({
        type: 'GET',
        crossDomain: true,
        contentType: "application/json; charset=utf-8",
        url: "https://protected-bayou-34428.herokuapp.com/health?echo=21",
        dataType: "jsonp"
        success: function(data){
              $('.bearing').append(data.bearing);
              $('.size').append(data.size);
            }
        }
        error : function(data){
                    console.log(data);
                   }
    })
});
<commit_msg>testing<commit_after>$(document).ready(function() {
    $.ajax({
        type: 'GET',
        crossDomain: true,
        contentType: "application/json; charset=utf-8",
        url: "https://protected-bayou-34428.herokuapp.com/health?echo=21",
        dataType: "jsonp",
        success: function(data){
              $('.bearing').append(data.bearing);
              $('.size').append(data.size);
            }
        }
        error : function(data){
                    console.log(data);
                   }
    })
});
<|endoftext|>