<commit_before>/**
 * @fileOverview Interfaces the application with its current architecture.
 * This could be an extension, mobile app, or a hosted web server.
 *
 **/

/**
 * @namespace
 */
var privlyNetworkService = {

  /**
   * If this variable is assigned, it will be appended as a get parameter
   * on all requests, eg, `?auth_token=AUTH_TOKEN_HERE`. This should never
   * be referenced by anything but the auth token setters.
   */
  authToken: "",

  /**
   * Set the authentication token if a parameter is supplied, or the
   * current platform has a setter function. This may be called from the
   * context of mobile applications or browser extensions.
   */
  setAuthTokenString: function(authTokenString) {
    if (authTokenString !== undefined) {
      privlyNetworkService.authTokenString = "auth_token=" + authToken;
    } else if(privlyNetworkService.platformName() === "ANDROID") {
      privlyNetworkService.authToken = "auth_token=" +
                                              androidJsBridge.fetchAuthToken();
      console.log(androidJsBridge.fetchAuthToken());
      console.log(privlyNetworkService.authToken);
    }
  },

  /**
   * Adds the auth token to the current URL. If the auth token has not been
   * assigned, nothing is added. If the auth token is assigned, it is added
   * as a get parameter.
   *
   * @param string url the URL that may need an auth token added.
   *
   */
  getAuthenticatedUrl: function(url) {

    if(privlyNetworkService.authToken === "") {
      console.log("authToken is null");
      return url;
    }

    // if query parameters already exist on the URL
    if (url.indexOf("?") >= 0 && (url.indexOf("?") < url.indexOf("#") ||
      url.indexOf("#") === -1)) {
      return url.replace("?", "?" + privlyNetworkService.authToken + "&");

    // else if there is an anchor
    } else if(url.indexOf("#") >= 0) {
      return url.replace("#", "?" + privlyNetworkService.authToken + "#");
    } else {
      return url + "?" + privlyNetworkService.authToken;
    }
  },

  /**
   * Determines which platform the script is runing on. This helps determine
   * which request function should be used. The current values are "CHROME"
   * for the Google Chrome extension, and "HOSTED" for all other architectures.
   * HOSTED functions use standard same-origin AJAX requests.
   *
   * @return {string} the name of the platform.
   */
  platformName: function() {
    if (navigator.userAgent.indexOf("iPhone") >= 0 || navigator.userAgent.indexOf("iPad") >= 0) {
      return "IOS";
    } else if(typeof androidJsBridge !== "undefined") {
      return "ANDROID";
    }  else if (typeof chrome !== "undefined" && typeof chrome.extension !== "undefined") {
      return "CHROME";
    } else {
      return "HOSTED";
    }
  },

  /**
   * This function is specific to the privly content server available on GitHub.
   * It initializes a CSRF token, and checks whether the user is logged in.
   * Since the application is not necessarily tied to the privly content
   * server, the failure callback should not necessarily be interpreted as
   * a failure.
   *
   * @param setCSRF boolean indicates whether it should get the CSRF token.
   *
   * @param canPostCallback function the function to execute when
   * initialization is successful.
   *
   * @param loginCallback function the function to execute if the user is
   * not logged in.
   *
   * @param errorCallback function the function to execute if the remote
   * server is not available.
   */
  initPrivlyService: function(setCSRF, canPostCallback, loginCallback, errorCallback) {
    var csrfTokenAddress = privlyNetworkService.contentServerDomain() +
                           "/posts/user_account_data";
    console.log("setting Auth Token");
    privlyNetworkService.setAuthTokenString();
    csrfTokenAddress =  privlyNetworkService.getAuthenticatedUrl(csrfTokenAddress);
    console.log(csrfTokenAddress);
    if (setCSRF) {
      $.ajax({
        url: csrfTokenAddress,
        dataType: "json",
        success: function (json, textStatus, jqXHR) {
          $.ajaxSetup({
            beforeSend: function(xhr) {
              xhr.setRequestHeader('X-CSRF-Token', json.csrf);
              xhr.setRequestHeader('Accept', 'application/json');
          }});

          if(json.signedIn && json.canPost) {
            canPostCallback(json, textStatus, jqXHR);
          } else if(json.signedIn) {
            cantPostLoginCallback(json, textStatus, jqXHR);
          } else {
            loginCallback(json, textStatus, jqXHR);
          }
        },
        error: function (jqXHR, textStatus, errorThrown) {
          errorCallback(jqXHR, textStatus, errorThrown);
        }
      });
    } else {
      $.ajaxSetup({
        beforeSend: function(xhr) {
          xhr.setRequestHeader('Accept', 'application/json');
      }});
    }
  },

  /**
   * Gives the domain all requests are associated with.
   *
   * @return {string} The domain content is associated with.
   */
  contentServerDomain: function() {

    var protocolDomainPort = location.protocol +
                             '//'+location.hostname +
                             (location.port ? ':'+location.port: '');

    if (privlyNetworkService.platformName() === "HOSTED") {
      return protocolDomainPort;
    } else if (privlyNetworkService.platformName() === "CHROME") {
      return localStorage["posting_content_server_url"];
    } else if (privlyNetworkService.platformName() === "ANDROID") {
      return androidJsBridge.fetchDomainName();
    } else if (privlyNetworkService.platformName() === "IOS") {
      // Hery todo
      // Get content server from iOS.
      // May be a global variable like auth_token.
      // Sean note: I think it would be better to assign it in
      // local storage and reference the variable (if that is available)
    } else {
      return protocolDomainPort;
    }
  },

  /**
   * Make a same-origin get request for content.
   *
   * This request should always proceed a post request.
   *
   * @param {string} url The URL to make a cross domain request for.
   * @param {function} callback The function to execute after the response
   * returns.
   */
  sameOriginGetRequest: function(url, callback) {
    url = privlyNetworkService.getAuthenticatedUrl(url);
    $.ajax({
      url: url,
      dataType: "json",
      success: function (json, textStatus, jqXHR) {
        callback({json: json, textStatus: textStatus, jqXHR: jqXHR});
      },
      error: function (jqXHR, textStatus, errorThrown) {
        callback({json: {}, textStatus: textStatus, jqXHR: jqXHR});
      }
    });
  },

  /**
   * Make a same-origin post request to the specified server.
   *
   * Warning: Do not use this function without first checking the data returned
   * by a get request for conformance with the application's signature.
   * Basically, you need to see whether the data at the URL expects to interface
   * with your application. The easiest way to do this is to have a version
   * string for your application in the JSON stored on the content server.
   * This prevents some rather nasty cross-site-scripting attacks.
   *
   * @param {string} path The relative path to make a cross domain request for.
   * @param {function} callback The function to execute after the response
   * returns.
   * @param {object} data The data to be sent with the post request.
   */
  sameOriginPostRequest: function(path, callback, data) {

    var url = privlyNetworkService.contentServerDomain() + path;
    url = privlyNetworkService.getAuthenticatedUrl(url);
    $.ajax({
      url: url,
      cache: false,
      type: "POST",
      data: data,
      dataType: "json",
      success: function (json, textStatus, jqXHR) {
        callback({json: json, textStatus: textStatus, jqXHR: jqXHR});
      },
      error: function (jqXHR, textStatus, errorThrown) {
        callback({json: {}, textStatus: textStatus, jqXHR: jqXHR});
      }
    });
  }
}
<commit_msg>changed authTokenString to authToken<commit_after>/**
 * @fileOverview Interfaces the application with its current architecture.
 * This could be an extension, mobile app, or a hosted web server.
 *
 **/

/**
 * @namespace
 */
var privlyNetworkService = {

  /**
   * If this variable is assigned, it will be appended as a get parameter
   * on all requests, eg, `?auth_token=AUTH_TOKEN_HERE`. This should never
   * be referenced by antrhing but the auth token setters.
   */
  authToken: "",

  /**
   * Set the authentication token if a parameter is supplied, or the
   * current platform has a setter function. This may be called from the
   * context of mobile applications or browser extensions.
   */
  setAuthTokenString: function(authTokenString) {
    if (authTokenString !== undefined) {
      privlyNetworkService.authToken = "auth_token=" + authTokenString;
    } else if(privlyNetworkService.platformName() === "ANDROID") {
      console.log("js bridge auth token" + androidJsBridge.fetchAuthToken());
      privlyNetworkService.authToken = "auth_token=" +
                                              androidJsBridge.fetchAuthToken();
    }
  },

  /**
   * Adds the auth token to the current URL. If the auth token has not been
   * assigned, nothing is added. If the auth token is assigned, it is added
   * as a get parameter.
   *
   * @param string url the URL that may need an auth token added.
   *
   */
  getAuthenticatedUrl: function(url) {
    privlyNetworkService.setAuthTokenString();
    if(privlyNetworkService.authToken === "") {
      return url;
    }

    // if query parameters already exist on the URL
    if (url.indexOf("?") >= 0 && (url.indexOf("?") < url.indexOf("#") ||
      url.indexOf("#") === -1)) {
      return url.replace("?", "?" + privlyNetworkService.authToken + "&");

    // else if there is an anchor
    } else if(url.indexOf("#") >= 0) {
      return url.replace("#", "?" + privlyNetworkService.authToken + "#");
    } else {
      return url + "?" + privlyNetworkService.authToken;
    }
  },

  /**
   * Determines which platform the script is runing on. This helps determine
   * which request function should be used. The current values are "CHROME"
   * for the Google Chrome extension, and "HOSTED" for all other architectures.
   * HOSTED functions use standard same-origin AJAX requests.
   *
   * @return {string} the name of the platform.
   */
  platformName: function() {
    if (navigator.userAgent.indexOf("iPhone") >= 0 || navigator.userAgent.indexOf("iPad") >= 0) {
      return "IOS";
    } else if(typeof androidJsBridge !== "undefined") {
      return "ANDROID";
    }  else if (typeof chrome !== "undefined" && typeof chrome.extension !== "undefined") {
      return "CHROME";
    } else {
      return "HOSTED";
    }
  },

  /**
   * This function is specific to the privly content server available on GitHub.
   * It initializes a CSRF token, and checks whether the user is logged in.
   * Since the application is not necessarily tied to the privly content
   * server, the failure callback should not necessarily be interpreted as
   * a failure.
   *
   * @param setCSRF boolean indicates whether it should get the CSRF token.
   *
   * @param canPostCallback function the function to execute when
   * initialization is successful.
   *
   * @param loginCallback function the function to execute if the user is
   * not logged in.
   *
   * @param errorCallback function the function to execute if the remote
   * server is not available.
   */
  initPrivlyService: function(setCSRF, canPostCallback, loginCallback, errorCallback) {
    var csrfTokenAddress = privlyNetworkService.contentServerDomain() +
                           "/posts/user_account_data";

    csrfTokenAddress =  privlyNetworkService.getAuthenticatedUrl(csrfTokenAddress);

    if (setCSRF) {
      $.ajax({
        url: csrfTokenAddress,
        dataType: "json",
        success: function (json, textStatus, jqXHR) {
          $.ajaxSetup({
            beforeSend: function(xhr) {
              xhr.setRequestHeader('X-CSRF-Token', json.csrf);
              xhr.setRequestHeader('Accept', 'application/json');
          }});

          if(json.signedIn && json.canPost) {
            canPostCallback(json, textStatus, jqXHR);
          } else if(json.signedIn) {
            cantPostLoginCallback(json, textStatus, jqXHR);
          } else {
            loginCallback(json, textStatus, jqXHR);
          }
        },
        error: function (jqXHR, textStatus, errorThrown) {
          errorCallback(jqXHR, textStatus, errorThrown);
        }
      });
    } else {
      $.ajaxSetup({
        beforeSend: function(xhr) {
          xhr.setRequestHeader('Accept', 'application/json');
      }});
    }
  },

  /**
   * Gives the domain all requests are associated with.
   *
   * @return {string} The domain content is associated with.
   */
  contentServerDomain: function() {

    var protocolDomainPort = location.protocol +
                             '//'+location.hostname +
                             (location.port ? ':'+location.port: '');

    if (privlyNetworkService.platformName() === "HOSTED") {
      return protocolDomainPort;
    } else if (privlyNetworkService.platformName() === "CHROME") {
      return localStorage["posting_content_server_url"];
    } else if (privlyNetworkService.platformName() === "ANDROID") {
      return androidJsBridge.fetchDomainName();
    } else if (privlyNetworkService.platformName() === "IOS") {
      // Hery todo
      // Get content server from iOS.
      // May be a global variable like auth_token.
      // Sean note: I think it would be better to assign it in
      // local storage and reference the variable (if that is available)
    } else {
      return protocolDomainPort;
    }
  },

  /**
   * Make a same-origin get request for content.
   *
   * This request should always proceed a post request.
   *
   * @param {string} url The URL to make a cross domain request for.
   * @param {function} callback The function to execute after the response
   * returns.
   */
  sameOriginGetRequest: function(url, callback) {
    url = privlyNetworkService.getAuthenticatedUrl(url);
    $.ajax({
      url: url,
      dataType: "json",
      success: function (json, textStatus, jqXHR) {
        callback({json: json, textStatus: textStatus, jqXHR: jqXHR});
      },
      error: function (jqXHR, textStatus, errorThrown) {
        callback({json: {}, textStatus: textStatus, jqXHR: jqXHR});
      }
    });
  },

  /**
   * Make a same-origin post request to the specified server.
   *
   * Warning: Do not use this function without first checking the data returned
   * by a get request for conformance with the application's signature.
   * Basically, you need to see whether the data at the URL expects to interface
   * with your application. The easiest way to do this is to have a version
   * string for your application in the JSON stored on the content server.
   * This prevents some rather nasty cross-site-scripting attacks.
   *
   * @param {string} path The relative path to make a cross domain request for.
   * @param {function} callback The function to execute after the response
   * returns.
   * @param {object} data The data to be sent with the post request.
   */
  sameOriginPostRequest: function(path, callback, data) {

    var url = privlyNetworkService.contentServerDomain() + path;
    url = privlyNetworkService.getAuthenticatedUrl(url);
    $.ajax({
      url: url,
      cache: false,
      type: "POST",
      data: data,
      dataType: "json",
      success: function (json, textStatus, jqXHR) {
        callback({json: json, textStatus: textStatus, jqXHR: jqXHR});
      },
      error: function (jqXHR, textStatus, errorThrown) {
        callback({json: {}, textStatus: textStatus, jqXHR: jqXHR});
      }
    });
  }
}
<|endoftext|>


<commit_before>(function() {
    'use strict';

    angular
        .module('app')
        .controller('browserActionCtrl', browserActionCtrl);

    browserActionCtrl.$inject = [
        '$scope',
        'RecommendationsTree'
    ];

    function browserActionCtrl($scope, RecommendationsTree) {
        $scope.loading = false;
        $scope.gotoNodeUrl = gotoNodeUrl;
        $scope.tree = {
            nodes: false,
            config: RecommendationsTree.getConfig()
        };

        activate();

        //////////

        function activate() {
            getRecomendationsTree();
        }

        function getRecomendationsTree() {
            $scope.loading = true;

            return RecommendationsTree.get().then(function(tree) {
                $scope.tree.nodes = tree;
                $scope.tree.config.version += 1;
                $scope.loading = false;
            }, function(err) {
                if (typeof(err) === 'string')
                    $scope.error = err;

                $scope.tree.nodes = [];
                $scope.loading = false;
            });
        }

        function gotoNodeUrl(evt, data) {
            var href = data.node.a_attr.href;

            chrome.extension.sendMessage({
                redirect: href
            });
        }
    }
})();<commit_msg>Added error when there problems with the API<commit_after>(function() {
    'use strict';

    angular
        .module('app')
        .controller('browserActionCtrl', browserActionCtrl);

    browserActionCtrl.$inject = [
        '$scope',
        'RecommendationsTree'
    ];

    function browserActionCtrl($scope, RecommendationsTree) {
        $scope.loading = false;
        $scope.gotoNodeUrl = gotoNodeUrl;
        $scope.tree = {
            nodes: false,
            config: RecommendationsTree.getConfig()
        };

        activate();

        //////////

        function activate() {
            getRecomendationsTree();
        }

        function getRecomendationsTree() {
            $scope.loading = true;

            return RecommendationsTree.get().then(function(tree) {
                $scope.tree.nodes = tree;
                $scope.tree.config.version += 1;
                $scope.loading = false;
            }, function(err) {
                $scope.error = 'Problem with the API';

                if (typeof(err) === 'string')
                    $scope.error = err;

                $scope.tree.nodes = [];
                $scope.loading = false;
            });
        }

        function gotoNodeUrl(evt, data) {
            var href = data.node.a_attr.href;

            chrome.extension.sendMessage({
                redirect: href
            });
        }
    }
})();<|endoftext|>


<commit_before>
'use strict';

export const SESSION_FINISH = 'SESSION_FINISH'; // session

export function actionFinishSession() {
  return {
    type: SESSION_FINISH
  };
}
<commit_msg>Added actionSaveSession().<commit_after>
'use strict';

export const SESSION_FINISH = 'SESSION_FINISH'; // session
export const SESSION_SAVE = 'SESSION_SAVE'; // session

export function actionSaveSession() {
  return {
    type: SESSION_SAVE
  };
}

export function actionFinishSession() {
  return {
    type: SESSION_FINISH
  };
}
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

// Provides control sap.m.Token.
sap.ui.define([
	'./library',
	'sap/ui/core/Control',
	'sap/ui/core/library',
	'sap/ui/core/Icon',
	'./TokenRenderer',
	'sap/ui/core/InvisibleText',
	'sap/ui/events/KeyCodes',
	'sap/ui/core/theming/Parameters',
	'sap/ui/core/Core'
],
	function(
		library,
		Control,
		coreLibrary,
		Icon,
		TokenRenderer,
		InvisibleText,
		KeyCodes,
		Parameters,
		Core
	) {
	"use strict";

	// shortcut for sap.ui.core.TextDirection
	var TextDirection = coreLibrary.TextDirection;

	/**
	 * Constructor for a new Token.
	 *
	 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
	 * @param {object} [mSettings] Initial settings for the new control
	 *
	 * @class
	 *
Overview

	 * Tokens are small items of information (similar to tags) that mainly serve to visualize previously selected items.
	 * Tokens are manipulated by a {@link sap.m.Tokenizer Tokenizer}.
	 *
Structure

	 * The tokens store single text items or sometimes key-value pairs, such as "John Miller (ID1234567)".
	 * Each token also contains a delete icon, which is invisible if the token is in edit mode.
	 *
	 *
Usage

	 *
When to use:

	 * Tokens can only be used with the Tokenizer as a container.
	 *
	 * @extends sap.ui.core.Control
	 * @author SAP SE
	 * @version ${version}
	 *
	 * @constructor
	 * @public
	 * @alias sap.m.Token
	 * @see {@link fiori:https://experience.sap.com/fiori-design-web/token/ Token}
	 * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
	 */
	var Token = Control.extend("sap.m.Token", /** @lends sap.m.Token.prototype */ { metadata : {

		library : "sap.m",
		properties : {

			/**
			 * Indicates the current selection status of the token.
			 */
			selected : {type : "boolean", group : "Misc", defaultValue : false},

			/**
			 * Key of the token.
			 */
			key : {type : "string", group : "Misc", defaultValue : ""},

			/**
			 * Displayed text of the token.
			 */
			text : {type : "string", group : "Misc", defaultValue : ""},

			/**
			 * Indicates the editable status of the token. If it is set to true, token displays a delete icon.
			 */
			editable : {type : "boolean", group : "Misc", defaultValue : true},

			/**
			 * This property specifies the text directionality with enumerated options. By default, the control inherits text direction from the DOM.
			 * @since 1.28.0
			 */
			textDirection : {type : "sap.ui.core.TextDirection", group : "Appearance", defaultValue : TextDirection.Inherit},

			/**
			 * Indicates the editable status of the token's parent (Tokenizer). If it is set to true, the ARIA attributes of the token are updated accordingly.
			 */
			editableParent : {type : "boolean", group : "Behavior", defaultValue : true, visibility: "hidden"},

			/**
			 * Indicates if the token's text should be truncated.
			 */
			truncated : {type : "boolean", group : "Appearance", defaultValue : false, visibility: "hidden"},

			/**
			 * Indicates the position of a token. Used for aria attributes.
			 * @private
			 */
			posinset : { type: "int", visibility: "hidden" },

			/**
			 * Indicates the count of the token. Used for aria attributes.
			 * @private
			 */
			setsize : { type: "int", visibility: "hidden" }
		},
		aggregations : {

			/**
			 * The delete icon.
			 */
			deleteIcon : {type : "sap.ui.core.Icon", multiple : false, visibility : "hidden"}
		},
		associations : {

			/**
			 * Association to controls / IDs which describe this control (see WAI-ARIA attribute aria-describedby).
			 */
			ariaDescribedBy: {type: "sap.ui.core.Control", multiple: true, singularName: "ariaDescribedBy"},

			/**
			 * Association to controls / IDs which label this control (see WAI-ARIA attribute aria-labelledby).
			 */
			ariaLabelledBy: {type: "sap.ui.core.Control", multiple: true, singularName: "ariaLabelledBy"}
		},
		events : {

			/**
			 * This event is fired if the user clicks the token's delete icon.
			 */
			"delete" : {
				enableEventBubbling: true
			},

			/**
			 * This event is fired when the user clicks on the token.
			 */
			press : {},

			/**
			 * This event is fired when the token gets selected.
			 */
			select : {},

			/**
			 * This event is fired when the token gets deselected.
			 */
			deselect : {}
		}
	}});

	Token.prototype.init = function() {
		var oDeleteIcon = new Icon({
				id : this.getId() + "-icon",
				src : "sap-icon://decline",
				noTabStop: true,
				press : this._fireDeleteToken.bind(this)
			});

		oDeleteIcon.addStyleClass("sapMTokenIcon");
		oDeleteIcon.setUseIconTooltip(false);
		this.setAggregation("deleteIcon", oDeleteIcon);
	};

	/**
	 * Handles the touch start event on the token.
	 *
	 * @param {jQuery.Event} oEvent The event object.
	 * @private
	 */
	Token.prototype.ontouchstart = function(oEvent) {
		if (oEvent.target.id === this.getId() + "-icon") {
			// prevent default or else the icon may get focused
			oEvent.preventDefault();
		}
	};

	/**
	 * Helper function for synchronizing the tooltip of the token.
	 * @private
	 * @param {sap.m.Token} oControl The control instance to get the tooltip for
	 * @param {boolean} bEditable The editable value
	 * @return {string} The tooltip text
	 */
	Token.prototype._getTooltip = function (oControl, bEditable) {
		var sTooltip = oControl.getTooltip_AsString(),
			sDeletableTooltip = Core.getLibraryResourceBundle("sap.m").getText("TOKEN_ARIA_DELETABLE");

		if (bEditable && !sTooltip) {
			return sDeletableTooltip;
		}

		return sTooltip;
	};

	/**
	 * Function is called when token is pressed to select/deselect token.
	 * @private
	 * @param {jQuery.Event} oEvent The event object
	 */
	Token.prototype._onTokenPress = function(oEvent) {
		var bSelected = this.getSelected(),
			bCtrlKey = oEvent.ctrlKey || oEvent.metaKey,
			bNewSelectedValue = true;

		if (bCtrlKey || (oEvent.which === KeyCodes.SPACE)) {
			bNewSelectedValue = !bSelected;
		}

		this.setSelected(bNewSelectedValue);

		this.firePress();

		if (bSelected !== bNewSelectedValue) {
			if (bNewSelectedValue) {
				this.fireSelect();
			} else {
				this.fireDeselect();
			}
		}

		if (this.getSelected()) {
			this.focus();
		}
	};

	/**
	 * Event handler called when control is on tap
	 *
	 * @param {jQuery.Event} oEvent The event object
	 * @private
	 */
	Token.prototype.ontap = function (oEvent) {
		var oDeleteIcon = this.getAggregation("deleteIcon");

		if (oDeleteIcon && oEvent.target.id === oDeleteIcon.getId()) {
			oEvent.setMark("tokenDeletePress", true);
			return;
		}

		oEvent.setMark("tokenTap", this);

		this._onTokenPress(oEvent);
	};

	Token.prototype._fireDeleteToken = function (oEvent, bKey, bBackspace) {
		if (this.getEditable() && this.getProperty("editableParent")) {
			this.fireDelete({
				token: this,
				byKeyboard: bKey,
				backspace: bBackspace
			});
		}

		oEvent.preventDefault();
	};

	/**
	 * Function is called on keyboard space, select/deselect token
	 * @private
	 * @param {jQuery.Event} oEvent The event object
	 */
	Token.prototype.onsapspace = function(oEvent) {

		this._onTokenPress(oEvent);
		// stop browsers default behavior
		if (oEvent) {
			oEvent.preventDefault();
			oEvent.stopPropagation();
		}
	};

	/**
	 * Handle the key down event for Ctrl+ space
	 *
	 * @param {jQuery.Event} oEvent The event object
	 * @private
	 */
	Token.prototype.onkeydown = function(oEvent) {

		if ((oEvent.ctrlKey || oEvent.metaKey) && oEvent.which === KeyCodes.SPACE) {
			//metaKey for MAC command
			this.onsapspace(oEvent);
			oEvent.preventDefault();
		}
	};

	/**
	 * Returns the value of Token's truncated property.
	 *
	 * @returns {boolean} true if the Token is truncated.
	 * @private
	 * @ui5-restricted sap.m.Tokenizer
	 */
	Token.prototype.getTruncated = function () {
		return this.getProperty("truncated");
	};

	/**
	 * Sets the Token's truncated property.
	 *
	 * @param {boolean} bValue The new property value.
	 * @param {boolean} bSkipInvalidation true if control invalidation should not happen.
	 * @returns {this} this reference for method chaining.
	 * @private
	 * @ui5-restricted sap.m.Tokenizer
	 */
	Token.prototype.setTruncated = function (bValue) {
		return this.setProperty("truncated", bValue);
	};

	return Token;

});
<commit_msg>[INTERNAL] sap.m.Token: sap.ui.core.theming.Parameters is defined but never used<commit_after>/*!
 * ${copyright}
 */

// Provides control sap.m.Token.
sap.ui.define([
	'./library',
	'sap/ui/core/Control',
	'sap/ui/core/library',
	'sap/ui/core/Icon',
	'./TokenRenderer',
	'sap/ui/core/InvisibleText',
	'sap/ui/events/KeyCodes',
	'sap/ui/core/Core'
],
	function(
		library,
		Control,
		coreLibrary,
		Icon,
		TokenRenderer,
		InvisibleText,
		KeyCodes,
		Core
	) {
	"use strict";

	// shortcut for sap.ui.core.TextDirection
	var TextDirection = coreLibrary.TextDirection;

	/**
	 * Constructor for a new Token.
	 *
	 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
	 * @param {object} [mSettings] Initial settings for the new control
	 *
	 * @class
	 *
Overview

	 * Tokens are small items of information (similar to tags) that mainly serve to visualize previously selected items.
	 * Tokens are manipulated by a {@link sap.m.Tokenizer Tokenizer}.
	 *
Structure

	 * The tokens store single text items or sometimes key-value pairs, such as "John Miller (ID1234567)".
	 * Each token also contains a delete icon, which is invisible if the token is in edit mode.
	 *
	 *
Usage

	 *
When to use:

	 * Tokens can only be used with the Tokenizer as a container.
	 *
	 * @extends sap.ui.core.Control
	 * @author SAP SE
	 * @version ${version}
	 *
	 * @constructor
	 * @public
	 * @alias sap.m.Token
	 * @see {@link fiori:https://experience.sap.com/fiori-design-web/token/ Token}
	 * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
	 */
	var Token = Control.extend("sap.m.Token", /** @lends sap.m.Token.prototype */ { metadata : {

		library : "sap.m",
		properties : {

			/**
			 * Indicates the current selection status of the token.
			 */
			selected : {type : "boolean", group : "Misc", defaultValue : false},

			/**
			 * Key of the token.
			 */
			key : {type : "string", group : "Misc", defaultValue : ""},

			/**
			 * Displayed text of the token.
			 */
			text : {type : "string", group : "Misc", defaultValue : ""},

			/**
			 * Indicates the editable status of the token. If it is set to true, token displays a delete icon.
			 */
			editable : {type : "boolean", group : "Misc", defaultValue : true},

			/**
			 * This property specifies the text directionality with enumerated options. By default, the control inherits text direction from the DOM.
			 * @since 1.28.0
			 */
			textDirection : {type : "sap.ui.core.TextDirection", group : "Appearance", defaultValue : TextDirection.Inherit},

			/**
			 * Indicates the editable status of the token's parent (Tokenizer). If it is set to true, the ARIA attributes of the token are updated accordingly.
			 */
			editableParent : {type : "boolean", group : "Behavior", defaultValue : true, visibility: "hidden"},

			/**
			 * Indicates if the token's text should be truncated.
			 */
			truncated : {type : "boolean", group : "Appearance", defaultValue : false, visibility: "hidden"},

			/**
			 * Indicates the position of a token. Used for aria attributes.
			 * @private
			 */
			posinset : { type: "int", visibility: "hidden" },

			/**
			 * Indicates the count of the token. Used for aria attributes.
			 * @private
			 */
			setsize : { type: "int", visibility: "hidden" }
		},
		aggregations : {

			/**
			 * The delete icon.
			 */
			deleteIcon : {type : "sap.ui.core.Icon", multiple : false, visibility : "hidden"}
		},
		associations : {

			/**
			 * Association to controls / IDs which describe this control (see WAI-ARIA attribute aria-describedby).
			 */
			ariaDescribedBy: {type: "sap.ui.core.Control", multiple: true, singularName: "ariaDescribedBy"},

			/**
			 * Association to controls / IDs which label this control (see WAI-ARIA attribute aria-labelledby).
			 */
			ariaLabelledBy: {type: "sap.ui.core.Control", multiple: true, singularName: "ariaLabelledBy"}
		},
		events : {

			/**
			 * This event is fired if the user clicks the token's delete icon.
			 */
			"delete" : {
				enableEventBubbling: true
			},

			/**
			 * This event is fired when the user clicks on the token.
			 */
			press : {},

			/**
			 * This event is fired when the token gets selected.
			 */
			select : {},

			/**
			 * This event is fired when the token gets deselected.
			 */
			deselect : {}
		}
	}});

	Token.prototype.init = function() {
		var oDeleteIcon = new Icon({
				id : this.getId() + "-icon",
				src : "sap-icon://decline",
				noTabStop: true,
				press : this._fireDeleteToken.bind(this)
			});

		oDeleteIcon.addStyleClass("sapMTokenIcon");
		oDeleteIcon.setUseIconTooltip(false);
		this.setAggregation("deleteIcon", oDeleteIcon);
	};

	/**
	 * Handles the touch start event on the token.
	 *
	 * @param {jQuery.Event} oEvent The event object.
	 * @private
	 */
	Token.prototype.ontouchstart = function(oEvent) {
		if (oEvent.target.id === this.getId() + "-icon") {
			// prevent default or else the icon may get focused
			oEvent.preventDefault();
		}
	};

	/**
	 * Helper function for synchronizing the tooltip of the token.
	 * @private
	 * @param {sap.m.Token} oControl The control instance to get the tooltip for
	 * @param {boolean} bEditable The editable value
	 * @return {string} The tooltip text
	 */
	Token.prototype._getTooltip = function (oControl, bEditable) {
		var sTooltip = oControl.getTooltip_AsString(),
			sDeletableTooltip = Core.getLibraryResourceBundle("sap.m").getText("TOKEN_ARIA_DELETABLE");

		if (bEditable && !sTooltip) {
			return sDeletableTooltip;
		}

		return sTooltip;
	};

	/**
	 * Function is called when token is pressed to select/deselect token.
	 * @private
	 * @param {jQuery.Event} oEvent The event object
	 */
	Token.prototype._onTokenPress = function(oEvent) {
		var bSelected = this.getSelected(),
			bCtrlKey = oEvent.ctrlKey || oEvent.metaKey,
			bNewSelectedValue = true;

		if (bCtrlKey || (oEvent.which === KeyCodes.SPACE)) {
			bNewSelectedValue = !bSelected;
		}

		this.setSelected(bNewSelectedValue);

		this.firePress();

		if (bSelected !== bNewSelectedValue) {
			if (bNewSelectedValue) {
				this.fireSelect();
			} else {
				this.fireDeselect();
			}
		}

		if (this.getSelected()) {
			this.focus();
		}
	};

	/**
	 * Event handler called when control is on tap
	 *
	 * @param {jQuery.Event} oEvent The event object
	 * @private
	 */
	Token.prototype.ontap = function (oEvent) {
		var oDeleteIcon = this.getAggregation("deleteIcon");

		if (oDeleteIcon && oEvent.target.id === oDeleteIcon.getId()) {
			oEvent.setMark("tokenDeletePress", true);
			return;
		}

		oEvent.setMark("tokenTap", this);

		this._onTokenPress(oEvent);
	};

	Token.prototype._fireDeleteToken = function (oEvent, bKey, bBackspace) {
		if (this.getEditable() && this.getProperty("editableParent")) {
			this.fireDelete({
				token: this,
				byKeyboard: bKey,
				backspace: bBackspace
			});
		}

		oEvent.preventDefault();
	};

	/**
	 * Function is called on keyboard space, select/deselect token
	 * @private
	 * @param {jQuery.Event} oEvent The event object
	 */
	Token.prototype.onsapspace = function(oEvent) {

		this._onTokenPress(oEvent);
		// stop browsers default behavior
		if (oEvent) {
			oEvent.preventDefault();
			oEvent.stopPropagation();
		}
	};

	/**
	 * Handle the key down event for Ctrl+ space
	 *
	 * @param {jQuery.Event} oEvent The event object
	 * @private
	 */
	Token.prototype.onkeydown = function(oEvent) {

		if ((oEvent.ctrlKey || oEvent.metaKey) && oEvent.which === KeyCodes.SPACE) {
			//metaKey for MAC command
			this.onsapspace(oEvent);
			oEvent.preventDefault();
		}
	};

	/**
	 * Returns the value of Token's truncated property.
	 *
	 * @returns {boolean} true if the Token is truncated.
	 * @private
	 * @ui5-restricted sap.m.Tokenizer
	 */
	Token.prototype.getTruncated = function () {
		return this.getProperty("truncated");
	};

	/**
	 * Sets the Token's truncated property.
	 *
	 * @param {boolean} bValue The new property value.
	 * @param {boolean} bSkipInvalidation true if control invalidation should not happen.
	 * @returns {this} this reference for method chaining.
	 * @private
	 * @ui5-restricted sap.m.Tokenizer
	 */
	Token.prototype.setTruncated = function (bValue) {
		return this.setProperty("truncated", bValue);
	};

	return Token;

});
<|endoftext|>


<commit_before>'use strict';
var token = require( './token' );

const type_ids = {
    conj : 107,
    disj : 109,
    term : 113,
    tagp : 127
};

// base, abstract expression
class expr {

    constructor ( tokens ) {
        if ( !( tokens instanceof Array ) && typeof tokens !== 'array' )
            throw new Error( "expr accepts only an array of lexer tokens" );
        this._tokens = tokens;
    }

    getType () {
        return this._type;
    }

    getTokens () {
        return this._tokens;
    }

    isRecursive () {
        return false;
    }

};

// abstract recursive expression
class rcrs extends expr {

    constructor ( subexprs ) {
        if ( !( subexprs instanceof Array ) && typeof subexprs !== 'array' )
            throw new Error( "recursive expressions accept only an array of expr's" );
        super([]);
        this._children = subexprs;
    }

    getTokens () {
        return this._children.reduce( ( acc, child ) => {
            return acc.concat( child.getTokens() );
        }, [] );
    }

    getChildren () {
        return this._children;
    }

    isRecursive () {
        return true;
    }

    isNegated () {
        return false;
    }

    toString ( outtermost ) {
        let inner = this._children.reduce( ( acc, child ) => {
            return acc.concat([ child.toString() ]);
        }, [] ).join( this._infix );
        return outtermost ? '( ' + inner + ' )' : inner;
    }

};

class conj extends rcrs {

    constructor ( subexprs ) {
        super( subexprs );
        this._type = type_ids.conj;
        this._infix = ' ';
    }

    negate () {
        return new disj( this._children.map ( ( child ) => {
            return child.negate();
        } ) );
    }

};

class disj extends rcrs {

    constructor ( subexprs ) {
        super( subexprs );
        this._type = type_ids.disj;
        this._infix = ' OR ';
    }

    negate () {
        return new conj( this._children.map ( ( child ) => {
            return child.negate()
        } ) );
    }

};

class term extends expr {

    constructor ( trm ) {
        if ( !( trm instanceof token.trm ) )
            throw new Error( "'term' expression accepts only a token 'trm'" );
        super([ trm ]);
        this._type = type_ids.term;
        this._negated = false;
    }

    getId () {
        return this._tokens[ 0 ].getText();
    }

    negate () {
        let ng = new term( this.getTokens()[ 0 ] );
        ng._negated = true;
        return ng;
    }

    isNegated () {
        return this._negated;
    }

    toString () {
        return ( this._negated ? '-' : '' ) + `"${this.getId()}"`;
    }

};

class tagp extends expr {

    constructor ( tokens ) {
        if (
            ( !( tokens instanceof Array ) && typeof tokens !== 'array' ) ||
            tokens.length !== 3 ||
            !( tokens[ 0 ] instanceof token.trm ) ||
            !( tokens[ 1 ] instanceof token.tag ) ||
            !( tokens[ 2 ] instanceof token.trm )
        ) {
            throw new Error( "tagp accepts only a term-tag-term triplet" );
        }
        super( tokens );
        this._type = type_ids.tagp;
        this._negated = false;
    }

    getAttr () {
        return this.getTokens()[ 0 ].getText();
    }

    getVal () {
        return this.getTokens()[ 2 ].getText();
    }

    negate () {
        let ng = new tagp( this._tokens );
        ng._negated = true;
        return ng;
    }

    isNegated () {
        return this._negated;
    }

    toString () {
        let neg = this._negated ? '-' : '';
        return neg + `'${this.getAttr()}':'${this.getVal()}'`;
    }

};

module.exports = {
    expr : expr,
    rcrs : rcrs,
    conj : conj,
    disj : disj,
    term : term,
    tagp : tagp,
    type_ids : type_ids
};

<commit_msg>tagp cleanup<commit_after>'use strict';
var token = require( './token' );

const type_ids = {
    conj : 107,
    disj : 109,
    term : 113,
    tagp : 127
};

// base, abstract expression
class expr {

    constructor ( tokens ) {
        if ( !( tokens instanceof Array ) && typeof tokens !== 'array' )
            throw new Error( "expr accepts only an array of lexer tokens" );
        this._tokens = tokens;
    }

    getType () {
        return this._type;
    }

    getTokens () {
        return this._tokens;
    }

    isRecursive () {
        return false;
    }

};

// abstract recursive expression
class rcrs extends expr {

    constructor ( subexprs ) {
        if ( !( subexprs instanceof Array ) && typeof subexprs !== 'array' )
            throw new Error( "recursive expressions accept only an array of expr's" );
        super([]);
        this._children = subexprs;
    }

    getTokens () {
        return this._children.reduce( ( acc, child ) => {
            return acc.concat( child.getTokens() );
        }, [] );
    }

    getChildren () {
        return this._children;
    }

    isRecursive () {
        return true;
    }

    isNegated () {
        return false;
    }

    toString ( outtermost ) {
        let inner = this._children.reduce( ( acc, child ) => {
            return acc.concat([ child.toString() ]);
        }, [] ).join( this._infix );
        return outtermost ? '( ' + inner + ' )' : inner;
    }

};

class conj extends rcrs {

    constructor ( subexprs ) {
        super( subexprs );
        this._type = type_ids.conj;
        this._infix = ' ';
    }

    negate () {
        return new disj( this._children.map ( ( child ) => {
            return child.negate();
        } ) );
    }

};

class disj extends rcrs {

    constructor ( subexprs ) {
        super( subexprs );
        this._type = type_ids.disj;
        this._infix = ' OR ';
    }

    negate () {
        return new conj( this._children.map ( ( child ) => {
            return child.negate()
        } ) );
    }

};

class term extends expr {

    constructor ( trm ) {
        if ( !( trm instanceof token.trm ) )
            throw new Error( "'term' expression accepts only a token 'trm'" );
        super([ trm ]);
        this._type = type_ids.term;
        this._negated = false;
    }

    getId () {
        return this._tokens[ 0 ].getText();
    }

    negate () {
        let ng = new term( this.getTokens()[ 0 ] );
        ng._negated = true;
        return ng;
    }

    isNegated () {
        return this._negated;
    }

    toString () {
        return ( this._negated ? '-' : '' ) + `"${this.getId()}"`;
    }

};

class tagp extends expr {

    constructor ( tokens ) {
        if ( !( tokens instanceof Array ) && typeof tokens !== 'array' )
            throw new Error( "expr.tagp accepts only an Array" );
        if ( tokens.length !== 3 )
            throw new Error( "expr.tagp accepts only a term-tag-term triplet" );

        let attr = tokens[ 0 ];
        let tag  = tokens[ 1 ];
        let val  = tokens[ 2 ];

        if (
            !( attr instanceof token.trm ) ||
            !( tag  instanceof token.tag ) ||
            !( val  instanceof token.trm )
        ) {
            throw new Error( "expr.tagp accepts only a term-tag-term triplet" );
        }

        super( tokens );

        this._type = type_ids.tagp;
        this._negated = false;
    }

    getAttr () {
        return this.getTokens()[ 0 ].getText();
    }

    getVal () {
        return this.getTokens()[ 2 ].getText();
    }

    negate () {
        let ng = new tagp( this._tokens );
        ng._negated = true;
        return ng;
    }

    isNegated () {
        return this._negated;
    }

    toString () {
        let neg = this._negated ? '-' : '';
        return neg + `'${this.getAttr()}':'${this.getVal()}'`;
    }

};

module.exports = {
    expr : expr,
    rcrs : rcrs,
    conj : conj,
    disj : disj,
    term : term,
    tagp : tagp,
    type_ids : type_ids
};

<|endoftext|>


<commit_before>(function() {
	'use strict';

	/**
	 * Reckon settings
	 * @type {Object}
	 */
	var reckonSettings = {
		stringPrototype: true,
		delimStart: '{{',
		delimEnd: '}}'
	};

	/**
	 * Create an instance
	 * @param {Object} params Configuration and data
	 */
	var Reckon = function(params) {
		return new ReckonInstance(params);
	}

	/**
	 * The actual Reckon implementation
	 * @param {Object} params Configuration and data
	 */
	var ReckonInstance = function(params) {
		/**
		 * Reckon version
		 * @type {String}
		 */
		this.version = '0.1.0';

		/**
		 * Define delimiter that marks beginning of interpolation
		 * @type {String}
		 */
		var delimStart = reckonSettings.delimStart;

		/**
		 * Define delimiter that marks end of interpolation
		 * @type {String}
		 */
		var delimEnd = reckonSettings.delimEnd;

		/**
		 * Get the text from params
		 * @type {String}
		 */
		this.text = params.text;

		/**
		 * Get the scope from the params and ensure its an array, if not, make it one
		 * @type {Array}
		 */
		this.scopes = [].concat(params.scope);

		/**
		 * Compile the initial input
		 */
		this.compile();
	};

	/**
	 * The reckon prototype
	 * @type {Object}
	 */
	ReckonInstance.prototype = Reckon.fn = {

		/**
		 * The function responsible for interpolation
		 * @return {Reckon} return self object for chaining
		 */
		compile: function() {
			/**
			 * The required regexp computed using delims in settings
			 * @type {RegExp}
			 */
			var re = new RegExp(['/{%(.+?)%}|', this.delimStart, '(.+?)', this.delimEnd, '/g'].join(''));

			/**
			 * Reference the instance
			 */
			 var rInstance = this;

			 /**
			  * Save the raw text
			  * @type {String}
			  */
			 rInstance.raw = rInstance.text;

			/**
			 * Compute and assign to compiledText property of the same object
			 * @param  {String} _  Matched string
			 * @param  {String} $1 Content of first match group
			 * @param  {String} $2 Content of second match group
			 * @return {String}    Interpolated string
			 */
			rInstance.text = rInstance.text.replace(/{%(.+?)%}|{{(.+?)}}/g, function(_, $1, $2) {
				var computed;

				/**
				 * If escaped value is found, interpolation will not happen
				 */
				if ($1) {
					computed = $1;
				}

				/**
				 * Matched content, let's interpolate
				 */
				if ($2) {

					/**
					 * Break out scope variables out of scope's box and evaluate the expr expression
					 */
					var scopeBox = function(expr, localScope) {
						var variables = '';

						/**
						 * If scope is a window object, no need to scopebox it
						 */
						if (typeof window !== "undefined" ? localScope !== window : true) {
							for(var i in localScope) {
								variables += 'var ' + i + ' = localScope.' + i + '; ';
							}
						}
						var unbox = '(function() { ' + variables + ' try { return eval(expr);} catch(e) {} })()';
						return eval(unbox);
					};

					if (rInstance.scopes.length) {
						var numScopes = rInstance.scopes.length;
						for (var i=0;i<numScopes;i++) {
							/**
							 * Current Scope
							 * @type {String}
							 */
							var scope = rInstance.scopes[i];

							/**
							 * Get the computation
							 * @type {Any}
							 */
							computed = scopeBox($2, scope);

							/**
							 * If the computed property is a function, execute it in the context of the current scope
							 * @type {Unknown}
							 */
							if (typeof computed === "function") {
								computed = computed.call(scope);
							}

							/**
							 * If the computed property is an object, get the string
							 */
							if (typeof computed === "object") {
								computed = computed.toString();
							}

							/**
							 * Found what we were looking for, now break the loop
							 */
							if (computed !== undefined)
								break;
						}
					} else {
						/**
						 * If no scope is passed, let us assume the global scope
						 * @type {Any}
						 */
						computed = scopeBox($2, window);
					}

					/**
					 * If no computations were found, we return the raw matched value back
					 * @type {String}
					 */
					computed = computed !== undefined ? computed : _;
				}

				/**
				 * Final computed value returned
				 */
				return computed;
			});

			/**
			 * Return self for chaining
			 */
			return rInstance;
		},
		toString: function() {
			return this.text;
		}
	}
	if (typeof window !== "undefined") {
		if (!window.reckon) {
			window.reckon = Reckon;
			window.reckonSettings = reckonSettings;

			/**
			 * Add method to string's prototype for easier access
			 */
			if (reckonSettings.stringPrototype === true) {

				/**
				 * Reckon added to string's prototype
				 * @param  {Array} scopes list of scope objects
				 * @return {String}       the reckoned string
				 */
				String.prototype.reckon = function(scopes) {
					return window.reckon({text: this, scope: scopes}).toString();
				}
			}
		}
	} else {
		module.exports = Reckon;
	}

})();<commit_msg>Using the delims from settings<commit_after>	(function() {
	'use strict';

	/**
	 * Reckon settings
	 * @type {Object}
	 */
	var reckonSettings = {
		stringPrototype: true,
		delimStart: '{{',
		delimEnd: '}}'
	};

	/**
	 * Create an instance
	 * @param {Object} params Configuration and data
	 */
	var Reckon = function(params) {
		return new ReckonInstance(params);
	}

	/**
	 * The actual Reckon implementation
	 * @param {Object} params Configuration and data
	 */
	var ReckonInstance = function(params) {
		/**
		 * Reckon version
		 * @type {String}
		 */
		this.version = '0.1.0';

		/**
		 * Define delimiter that marks beginning of interpolation
		 * @type {String}
		 */
		this.delimStart = reckonSettings.delimStart;

		/**
		 * Define delimiter that marks end of interpolation
		 * @type {String}
		 */
		this.delimEnd = reckonSettings.delimEnd;

		/**
		 * Get the text from params
		 * @type {String}
		 */
		this.text = params.text;

		/**
		 * Get the scope from the params and ensure its an array, if not, make it one
		 * @type {Array}
		 */
		this.scopes = [].concat(params.scope);

		/**
		 * Compile the initial input
		 */
		this.compile();
	};

	/**
	 * The reckon prototype
	 * @type {Object}
	 */
	ReckonInstance.prototype = Reckon.fn = {

		/**
		 * The function responsible for interpolation
		 * @return {Reckon} return self object for chaining
		 */
		compile: function() {
			/**
			 * The required regexp computed using delims in settings
			 * @type {RegExp}
			 */
			var re = new RegExp(['{%(.+?)%}|', this.delimStart, '(.+?)', this.delimEnd].join(''), 'g');

			/**
			 * Reference the instance
			 */
			 var rInstance = this;

			 /**
			  * Save the raw text
			  * @type {String}
			  */
			 rInstance.raw = rInstance.text;

			/**
			 * Compute and assign to compiledText property of the same object
			 * @param  {String} _  Matched string
			 * @param  {String} $1 Content of first match group
			 * @param  {String} $2 Content of second match group
			 * @return {String}    Interpolated string
			 */
			rInstance.text = rInstance.text.replace(re, function(_, $1, $2) {
				var computed;

				/**
				 * If escaped value is found, interpolation will not happen
				 */
				if ($1) {
					computed = $1;
				}

				/**
				 * Matched content, let's interpolate
				 */
				if ($2) {

					/**
					 * Break out scope variables out of scope's box and evaluate the expr expression
					 */
					var scopeBox = function(expr, localScope) {
						var variables = '';

						/**
						 * If scope is a window object, no need to scopebox it
						 */
						if (typeof window !== "undefined" ? localScope !== window : true) {
							for(var i in localScope) {
								variables += 'var ' + i + ' = localScope.' + i + '; ';
							}
						}
						var unbox = '(function() { ' + variables + ' try { return eval(expr);} catch(e) {} })()';
						return eval(unbox);
					};

					if (rInstance.scopes.length) {
						var numScopes = rInstance.scopes.length;
						for (var i=0;i<numScopes;i++) {
							/**
							 * Current Scope
							 * @type {String}
							 */
							var scope = rInstance.scopes[i];

							/**
							 * Get the computation
							 * @type {Any}
							 */
							computed = scopeBox($2, scope);

							/**
							 * If the computed property is a function, execute it in the context of the current scope
							 * @type {Unknown}
							 */
							if (typeof computed === "function") {
								computed = computed.call(scope);
							}

							/**
							 * If the computed property is an object, get the string
							 */
							if (typeof computed === "object") {
								computed = computed.toString();
							}

							/**
							 * Found what we were looking for, now break the loop
							 */
							if (computed !== undefined)
								break;
						}
					} else {
						/**
						 * If no scope is passed, let us assume the global scope
						 * @type {Any}
						 */
						computed = scopeBox($2, window);
					}

					/**
					 * If no computations were found, we return the raw matched value back
					 * @type {String}
					 */
					computed = computed !== undefined ? computed : _;
				}

				/**
				 * Final computed value returned
				 */
				return computed;
			});

			/**
			 * Return self for chaining
			 */
			return rInstance;
		},
		toString: function() {
			return this.text;
		}
	}
	if (typeof window !== "undefined") {
		if (!window.reckon) {
			window.reckon = Reckon;
			window.reckonSettings = reckonSettings;

			/**
			 * Add method to string's prototype for easier access
			 */
			if (reckonSettings.stringPrototype === true) {

				/**
				 * Reckon added to string's prototype
				 * @param  {Array} scopes list of scope objects
				 * @return {String}       the reckoned string
				 */
				String.prototype.reckon = function(scopes) {
					return window.reckon({text: this, scope: scopes}).toString();
				}
			}
		}
	} else {
		module.exports = Reckon;
	}

})();<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import { propTypes } from './react-props-decorators.js'; //tnrtodo: update this once the actual npm module updates its dependencies
var Combokeys = require("combokeys");
var combokeys;
var bindGlobalPlugin = require('combokeys/plugins/global-bind');

var RowView = require('./RowView');
var MapView = require('./MapView');
var BottomStatusBar = require('./BottomStatusBar');

var Clipboard = require('./Clipboard');
import {Decorator as Cerebral} from 'cerebral-react';

@Cerebral({
    sequenceLength: ['sequenceLength'],
    bpsPerRow: ['bpsPerRow'],
    totalRows: ['totalRows'],
    newRandomRowToJumpTo: ['newRandomRowToJumpTo'],
    selectedSequenceString: ['selectedSequenceString'],
    caretPosition: ['caretPosition'],
    sequenceData: ['sequenceData'],
    selectionLayer: ['selectionLayer'],
    clipboardData: ['clipboardData'],
})
@propTypes({
    sequenceLength: PropTypes.number.isRequired,
    bpsPerRow: PropTypes.number.isRequired,
    totalRows: PropTypes.number.isRequired,
    newRandomRowToJumpTo: PropTypes.object,
    selectedSequenceString: PropTypes.string.isRequired,
    caretPosition: PropTypes.number.isRequired,
    sequenceData: PropTypes.object.isRequired,
    selectionLayer: PropTypes.object.isRequired,
    clipboardData: PropTypes.object.isRequired,
})
class SequenceEditor extends React.Component {
    componentDidMount() {
        var {
            sequenceDataInserted,
            backspacePressed,
            selectAll,
        } = this.props.signals;
        var self = this;
        combokeys = new Combokeys(document.documentElement);
        // combokeys = new Combokeys(React.findDOMNode(this.refs.sequenceEditor));
        bindGlobalPlugin(combokeys);

        //bind a bunch of keyboard shortcuts we're interested in catching
        //we're using the "mousetrap" library (available thru npm: https://www.npmjs.com/package/br-mousetrap)
        //documentation: https://craig.is/killing/mice
        combokeys.bind(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ], function(event) { // Handle shortcut
            sequenceDataInserted({newSequenceData: {sequence: String.fromCharCode(event.charCode)}});
        });
        combokeys.bind(['left','shift+left'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretLeftOne'});
        });
        combokeys.bind(['right','shift+right'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretRightOne'});
        });
        combokeys.bind(['up','shift+up'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretUpARow'});
        });
        combokeys.bindGlobal(['down','shift+down'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretDownARow'});
        });
        combokeys.bindGlobal(['mod+right','mod+shift+right'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+left','mod+shift+left'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+up','mod+shift+up'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+down','mod+shift+down'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bind('backspace', function(event) { // Handle shortcut
            backspacePressed();
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal('command+a', function(event) { // Handle shortcut
            selectAll();
            event.stopPropagation();
        });
    }

    handlePaste(event) {
        var {
            pasteSequenceString,
        } = this.props.signals;
        event.clipboardData.items[0].getAsString(function(string) {
            pasteSequenceString(string);
        });
    }

    handleCopy() {
        var {
            copySelection,
        } = this.props.signals;
        copySelection();
        // this.props.selectedSequenceString
    }

    componentWillUnmount() {

        // Remove any Mousetrap bindings before unmounting.detach()
        combokeys.detach()
    }

    handleEditorClick(updatedCaretPos, event) {
        //if cursor position is different than the original position, reset the position and clear the selection
        // console.log('onclick!!');
        // var bp = this.getNearestCursorPositionToMouseEvent(event);
        if (this.editorBeingDragged) {
            //do nothing because the click was triggered by a drag event
        } else {
            this.props.signals.editorClicked({
                shiftHeld: event.shiftKey,
                type: 'editorClick',
                updatedCaretPos: updatedCaretPos
            })
        }

    }

    handleEditorDrag(caretPosition) {
        var {
            setCaretPosition,
            setSelectionLayer
        } = this.props.signals;
        //note this method relies on variables that are set in the handleEditorDragStart method!
        this.editorBeingDragged = true;
        if (caretPosition === this.fixedCaretPositionOnEditorDragStart) {
            setCaretPosition(caretPosition);
            setSelectionLayer(false);
        } else {
            var newSelectionLayer;
            if (this.fixedCaretPositionOnEditorDragStartType === 'start') {
                newSelectionLayer = {
                    start: this.fixedCaretPositionOnEditorDragStart,
                    end: caretPosition - 1,
                    cursorAtEnd: true,
                };
            } else if (this.fixedCaretPositionOnEditorDragStartType === 'end') {
                newSelectionLayer = {
                    start: caretPosition,
                    end: this.fixedCaretPositionOnEditorDragStart - 1,
                    cursorAtEnd: false,
                };
            } else {
                if (caretPosition > this.fixedCaretPositionOnEditorDragStart) {
                    newSelectionLayer = {
                        start: this.fixedCaretPositionOnEditorDragStart,
                        end: caretPosition - 1,
                        cursorAtEnd: true,
                    };
                } else {
                    newSelectionLayer = {
                        start: caretPosition,
                        end: this.fixedCaretPositionOnEditorDragStart - 1,
                        cursorAtEnd: false,
                    };
                }
            }
            setSelectionLayer({selectionLayer: newSelectionLayer});
        }
    }

    handleEditorDragStart(caretPosition, event) {
        var {selectionLayer} = this.props;
        // var caretPosition = this.getNearestCursorPositionToMouseEvent(event);
        if (event.target.className === "cursor" && selectionLayer.selected) {
            // this.circularSelectionOnEditorDragStart = (selectionLayer.start > selectionLayer.end);
            if (selectionLayer.start === caretPosition) {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.end + 1;
                this.fixedCaretPositionOnEditorDragStartType = 'end';

                //plus one because the cursor position will be 1 more than the selectionLayer.end
                //imagine selection from
                //0 1 2  <--possible cursor positions
                // A T G
                //if A is selected, selection.start = 0, selection.end = 0
                //so the caretPosition for the end of the selection is 1!
                //which is selection.end+1
            } else {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.start;
                this.fixedCaretPositionOnEditorDragStartType = 'start';
            }
        } else {
            // this.circularSelectionOnEditorDragStart = false;
            this.fixedCaretPositionOnEditorDragStart = caretPosition;
            this.fixedCaretPositionOnEditorDragStartType = 'caret';
        }
    }

    handleEditorDragStop(event, ui) {
        var self = this;
        if (this.editorBeingDragged) { //check to make sure dragging actually occurred
            setTimeout(function() {
                //we use setTimeout to put the call to change editorBeingDragged to false
                //on the bottom of the event stack, thus the click event that is fired because of the drag
                //will be able to check if editorBeingDragged and not trigger if it is
                self.editorBeingDragged = false;
            }, 0);
        } else {
            self.editorBeingDragged = false;
        }
    }

    render() {
        var {
            selectedSequenceString,
            signals: {
                toggleAnnotationDisplay
            }
        } = this.props;
        var annotationList = ['features', 'parts', 'translations', 'orfs', 'cutsites'];
        var toggleButtons = annotationList.map(function(annotationType, index){
            // console.log(">>> " + annotationType + " " + index);
            return (<button key={index} onClick={function () {
                toggleAnnotationDisplay(String(annotationType));
            }}>
            toggle {annotationType}
        </button>)
        });

        return (
            <div ref="sequenceEditor"
                style={{float:"right"}}>

                {toggleButtons}

                <Clipboard
                    value={selectedSequenceString}
                    onCopy={this.handleCopy.bind(this)}
                    onPaste={this.handlePaste.bind(this)}/>

               <RowView
                   handleEditorDrag={this.handleEditorDrag.bind(this)}
                   handleEditorDragStart={this.handleEditorDragStart.bind(this)}
                   handleEditorDragStop={this.handleEditorDragStop.bind(this)}
                   handleEditorClick={this.handleEditorClick.bind(this)}
               />

               <BottomStatusBar/>
            </div>
      );
    }
}

module.exports = SequenceEditor;
<commit_msg>create toolbar and icon menu<commit_after>import React, {PropTypes} from 'react';
import { propTypes } from './react-props-decorators.js'; //tnrtodo: update this once the actual npm module updates its dependencies
var Combokeys = require("combokeys");
var combokeys;
var bindGlobalPlugin = require('combokeys/plugins/global-bind');

var RowView = require('./RowView');
var MapView = require('./MapView');
var BottomStatusBar = require('./BottomStatusBar');

var Clipboard = require('./Clipboard');
import {Decorator as Cerebral} from 'cerebral-react';

/* Toolbar */
import { Toolbar, ToolbarGroup } from 'material-ui';
import { IconMenu } from 'material-ui';
import { IconButton } from 'material-ui';
import SettingsIcon from 'material-ui/lib/svg-icons/action/settings';
import MenuItem from 'material-ui/lib/menus/menu-item';

@Cerebral({
    sequenceLength: ['sequenceLength'],
    bpsPerRow: ['bpsPerRow'],
    totalRows: ['totalRows'],
    newRandomRowToJumpTo: ['newRandomRowToJumpTo'],
    selectedSequenceString: ['selectedSequenceString'],
    caretPosition: ['caretPosition'],
    sequenceData: ['sequenceData'],
    selectionLayer: ['selectionLayer'],
    clipboardData: ['clipboardData'],
})
@propTypes({
    sequenceLength: PropTypes.number.isRequired,
    bpsPerRow: PropTypes.number.isRequired,
    totalRows: PropTypes.number.isRequired,
    newRandomRowToJumpTo: PropTypes.object,
    selectedSequenceString: PropTypes.string.isRequired,
    caretPosition: PropTypes.number.isRequired,
    sequenceData: PropTypes.object.isRequired,
    selectionLayer: PropTypes.object.isRequired,
    clipboardData: PropTypes.object.isRequired,
})
class SequenceEditor extends React.Component {
    componentDidMount() {
        var {
            sequenceDataInserted,
            backspacePressed,
            selectAll,
        } = this.props.signals;
        var self = this;
        combokeys = new Combokeys(document.documentElement);
        // combokeys = new Combokeys(React.findDOMNode(this.refs.sequenceEditor));
        bindGlobalPlugin(combokeys);

        //bind a bunch of keyboard shortcuts we're interested in catching
        //we're using the "mousetrap" library (available thru npm: https://www.npmjs.com/package/br-mousetrap)
        //documentation: https://craig.is/killing/mice
        combokeys.bind(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ], function(event) { // Handle shortcut
            sequenceDataInserted({newSequenceData: {sequence: String.fromCharCode(event.charCode)}});
        });
        combokeys.bind(['left','shift+left'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretLeftOne'});
        });
        combokeys.bind(['right','shift+right'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretRightOne'});
        });
        combokeys.bind(['up','shift+up'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretUpARow'});
        });
        combokeys.bindGlobal(['down','shift+down'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretDownARow'});
        });
        combokeys.bindGlobal(['mod+right','mod+shift+right'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+left','mod+shift+left'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+up','mod+shift+up'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+down','mod+shift+down'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bind('backspace', function(event) { // Handle shortcut
            backspacePressed();
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal('command+a', function(event) { // Handle shortcut
            selectAll();
            event.stopPropagation();
        });
    }

    handlePaste(event) {
        var {
            pasteSequenceString,
        } = this.props.signals;
        event.clipboardData.items[0].getAsString(function(string) {
            pasteSequenceString(string);
        });
    }

    handleCopy() {
        var {
            copySelection,
        } = this.props.signals;
        copySelection();
        // this.props.selectedSequenceString
    }

    componentWillUnmount() {

        // Remove any Mousetrap bindings before unmounting.detach()
        combokeys.detach()
    }

    handleEditorClick(updatedCaretPos, event) {
        //if cursor position is different than the original position, reset the position and clear the selection
        // console.log('onclick!!');
        // var bp = this.getNearestCursorPositionToMouseEvent(event);
        if (this.editorBeingDragged) {
            //do nothing because the click was triggered by a drag event
        } else {
            this.props.signals.editorClicked({
                shiftHeld: event.shiftKey,
                type: 'editorClick',
                updatedCaretPos: updatedCaretPos
            })
        }

    }

    handleEditorDrag(caretPosition) {
        var {
            setCaretPosition,
            setSelectionLayer
        } = this.props.signals;
        //note this method relies on variables that are set in the handleEditorDragStart method!
        this.editorBeingDragged = true;
        if (caretPosition === this.fixedCaretPositionOnEditorDragStart) {
            setCaretPosition(caretPosition);
            setSelectionLayer(false);
        } else {
            var newSelectionLayer;
            if (this.fixedCaretPositionOnEditorDragStartType === 'start') {
                newSelectionLayer = {
                    start: this.fixedCaretPositionOnEditorDragStart,
                    end: caretPosition - 1,
                    cursorAtEnd: true,
                };
            } else if (this.fixedCaretPositionOnEditorDragStartType === 'end') {
                newSelectionLayer = {
                    start: caretPosition,
                    end: this.fixedCaretPositionOnEditorDragStart - 1,
                    cursorAtEnd: false,
                };
            } else {
                if (caretPosition > this.fixedCaretPositionOnEditorDragStart) {
                    newSelectionLayer = {
                        start: this.fixedCaretPositionOnEditorDragStart,
                        end: caretPosition - 1,
                        cursorAtEnd: true,
                    };
                } else {
                    newSelectionLayer = {
                        start: caretPosition,
                        end: this.fixedCaretPositionOnEditorDragStart - 1,
                        cursorAtEnd: false,
                    };
                }
            }
            setSelectionLayer({selectionLayer: newSelectionLayer});
        }
    }

    handleEditorDragStart(caretPosition, event) {
        var {selectionLayer} = this.props;
        // var caretPosition = this.getNearestCursorPositionToMouseEvent(event);
        if (event.target.className === "cursor" && selectionLayer.selected) {
            // this.circularSelectionOnEditorDragStart = (selectionLayer.start > selectionLayer.end);
            if (selectionLayer.start === caretPosition) {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.end + 1;
                this.fixedCaretPositionOnEditorDragStartType = 'end';

                //plus one because the cursor position will be 1 more than the selectionLayer.end
                //imagine selection from
                //0 1 2  <--possible cursor positions
                // A T G
                //if A is selected, selection.start = 0, selection.end = 0
                //so the caretPosition for the end of the selection is 1!
                //which is selection.end+1
            } else {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.start;
                this.fixedCaretPositionOnEditorDragStartType = 'start';
            }
        } else {
            // this.circularSelectionOnEditorDragStart = false;
            this.fixedCaretPositionOnEditorDragStart = caretPosition;
            this.fixedCaretPositionOnEditorDragStartType = 'caret';
        }
    }

    handleEditorDragStop(event, ui) {
        var self = this;
        if (this.editorBeingDragged) { //check to make sure dragging actually occurred
            setTimeout(function() {
                //we use setTimeout to put the call to change editorBeingDragged to false
                //on the bottom of the event stack, thus the click event that is fired because of the drag
                //will be able to check if editorBeingDragged and not trigger if it is
                self.editorBeingDragged = false;
            }, 0);
        } else {
            self.editorBeingDragged = false;
        }
    }

    render() {
        var {
            selectedSequenceString,
            signals: {
                toggleAnnotationDisplay
            }
        } = this.props;
        var annotationList = ['features', 'parts', 'translations', 'orfs', 'cutsites'];
        var toggleButtons = annotationList.map(function(annotationType, index){
            return (<button key={index} onClick={function () {
                toggleAnnotationDisplay(String(annotationType));
            }}>
            toggle {annotationType}
        </button>)
        });

        var iconButtonElement = (
            <IconButton tooltip="Settings">
                <SettingsIcon />
            </IconButton>
        );

        return (
            <div ref="sequenceEditor">
                <Toolbar>
                    <ToolbarGroup key={0}>
                        <IconMenu iconButtonElement={iconButtonElement}>
                            <MenuItem primaryText="Annotations" />
                            <MenuItem primaryText="ORFs" />
                        </IconMenu>
                    </ToolbarGroup>
                </Toolbar>

                {toggleButtons}

                <Clipboard
                    value={selectedSequenceString}
                    onCopy={this.handleCopy.bind(this)}
                    onPaste={this.handlePaste.bind(this)}/>

                <RowView
                    handleEditorDrag={this.handleEditorDrag.bind(this)}
                    handleEditorDragStart={this.handleEditorDragStart.bind(this)}
                    handleEditorDragStop={this.handleEditorDragStop.bind(this)}
                    handleEditorClick={this.handleEditorClick.bind(this)}
                />

                <BottomStatusBar/>
            </div>
        );
    }
}

module.exports = SequenceEditor;
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import { propTypes } from './react-props-decorators.js'; //tnrtodo: update this once the actual npm module updates its dependencies
var Combokeys = require("combokeys");
var combokeys;
var bindGlobalPlugin = require('combokeys/plugins/global-bind');

var RowView = require('./RowView');
var MapView = require('./MapView');
var BottomStatusBar = require('./BottomStatusBar');

var Clipboard = require('./Clipboard');
import {Decorator as Cerebral} from 'cerebral-react';

/* Toolbar */
import { Toolbar, ToolbarGroup } from 'material-ui';
import { IconMenu } from 'material-ui';
import { IconButton } from 'material-ui';
import SettingsIcon from 'material-ui/lib/svg-icons/action/settings';
import MenuItem from 'material-ui/lib/menus/menu-item';

@Cerebral({
    sequenceLength: ['sequenceLength'],
    bpsPerRow: ['bpsPerRow'],
    totalRows: ['totalRows'],
    newRandomRowToJumpTo: ['newRandomRowToJumpTo'],
    selectedSequenceString: ['selectedSequenceString'],
    caretPosition: ['caretPosition'],
    sequenceData: ['sequenceData'],
    selectionLayer: ['selectionLayer'],
    clipboardData: ['clipboardData'],
})
@propTypes({
    sequenceLength: PropTypes.number.isRequired,
    bpsPerRow: PropTypes.number.isRequired,
    totalRows: PropTypes.number.isRequired,
    newRandomRowToJumpTo: PropTypes.object,
    selectedSequenceString: PropTypes.string.isRequired,
    caretPosition: PropTypes.number.isRequired,
    sequenceData: PropTypes.object.isRequired,
    selectionLayer: PropTypes.object.isRequired,
    clipboardData: PropTypes.object.isRequired,
})
class SequenceEditor extends React.Component {
    componentDidMount() {
        var {
            sequenceDataInserted,
            backspacePressed,
            selectAll,
        } = this.props.signals;
        var self = this;
        combokeys = new Combokeys(document.documentElement);
        // combokeys = new Combokeys(React.findDOMNode(this.refs.sequenceEditor));
        bindGlobalPlugin(combokeys);

        //bind a bunch of keyboard shortcuts we're interested in catching
        //we're using the "mousetrap" library (available thru npm: https://www.npmjs.com/package/br-mousetrap)
        //documentation: https://craig.is/killing/mice
        combokeys.bind(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ], function(event) { // Handle shortcut
            sequenceDataInserted({newSequenceData: {sequence: String.fromCharCode(event.charCode)}});
        });
        combokeys.bind(['left','shift+left'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretLeftOne'});
        });
        combokeys.bind(['right','shift+right'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretRightOne'});
        });
        combokeys.bind(['up','shift+up'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretUpARow'});
        });
        combokeys.bindGlobal(['down','shift+down'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretDownARow'});
        });
        combokeys.bindGlobal(['mod+right','mod+shift+right'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+left','mod+shift+left'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+up','mod+shift+up'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+down','mod+shift+down'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bind('backspace', function(event) { // Handle shortcut
            backspacePressed();
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal('command+a', function(event) { // Handle shortcut
            selectAll();
            event.stopPropagation();
        });
    }

    handlePaste(event) {
        var {
            pasteSequenceString,
        } = this.props.signals;
        event.clipboardData.items[0].getAsString(function(string) {
            pasteSequenceString(string);
        });
    }

    handleCopy() {
        var {
            copySelection,
        } = this.props.signals;
        copySelection();
        // this.props.selectedSequenceString
    }

    componentWillUnmount() {

        // Remove any Mousetrap bindings before unmounting.detach()
        combokeys.detach()
    }

    handleEditorClick(updatedCaretPos, event) {
        //if cursor position is different than the original position, reset the position and clear the selection
        // console.log('onclick!!');
        // var bp = this.getNearestCursorPositionToMouseEvent(event);
        if (this.editorBeingDragged) {
            //do nothing because the click was triggered by a drag event
        } else {
            this.props.signals.editorClicked({
                shiftHeld: event.shiftKey,
                type: 'editorClick',
                updatedCaretPos: updatedCaretPos
            })
        }

    }

    handleEditorDrag(caretPosition) {
        var {
            setCaretPosition,
            setSelectionLayer
        } = this.props.signals;
        //note this method relies on variables that are set in the handleEditorDragStart method!
        this.editorBeingDragged = true;
        if (caretPosition === this.fixedCaretPositionOnEditorDragStart) {
            setCaretPosition(caretPosition);
            setSelectionLayer(false);
        } else {
            var newSelectionLayer;
            if (this.fixedCaretPositionOnEditorDragStartType === 'start') {
                newSelectionLayer = {
                    start: this.fixedCaretPositionOnEditorDragStart,
                    end: caretPosition - 1,
                    cursorAtEnd: true,
                };
            } else if (this.fixedCaretPositionOnEditorDragStartType === 'end') {
                newSelectionLayer = {
                    start: caretPosition,
                    end: this.fixedCaretPositionOnEditorDragStart - 1,
                    cursorAtEnd: false,
                };
            } else {
                if (caretPosition > this.fixedCaretPositionOnEditorDragStart) {
                    newSelectionLayer = {
                        start: this.fixedCaretPositionOnEditorDragStart,
                        end: caretPosition - 1,
                        cursorAtEnd: true,
                    };
                } else {
                    newSelectionLayer = {
                        start: caretPosition,
                        end: this.fixedCaretPositionOnEditorDragStart - 1,
                        cursorAtEnd: false,
                    };
                }
            }
            setSelectionLayer({selectionLayer: newSelectionLayer});
        }
    }

    handleEditorDragStart(caretPosition, event) {
        var {selectionLayer} = this.props;
        // var caretPosition = this.getNearestCursorPositionToMouseEvent(event);
        if (event.target.className === "cursor" && selectionLayer.selected) {
            // this.circularSelectionOnEditorDragStart = (selectionLayer.start > selectionLayer.end);
            if (selectionLayer.start === caretPosition) {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.end + 1;
                this.fixedCaretPositionOnEditorDragStartType = 'end';

                //plus one because the cursor position will be 1 more than the selectionLayer.end
                //imagine selection from
                //0 1 2  <--possible cursor positions
                // A T G
                //if A is selected, selection.start = 0, selection.end = 0
                //so the caretPosition for the end of the selection is 1!
                //which is selection.end+1
            } else {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.start;
                this.fixedCaretPositionOnEditorDragStartType = 'start';
            }
        } else {
            // this.circularSelectionOnEditorDragStart = false;
            this.fixedCaretPositionOnEditorDragStart = caretPosition;
            this.fixedCaretPositionOnEditorDragStartType = 'caret';
        }
    }

    handleEditorDragStop(event, ui) {
        var self = this;
        if (this.editorBeingDragged) { //check to make sure dragging actually occurred
            setTimeout(function() {
                //we use setTimeout to put the call to change editorBeingDragged to false
                //on the bottom of the event stack, thus the click event that is fired because of the drag
                //will be able to check if editorBeingDragged and not trigger if it is
                self.editorBeingDragged = false;
            }, 0);
        } else {
            self.editorBeingDragged = false;
        }
    }

    render() {
        var {
            selectedSequenceString,
            signals: {
                toggleAnnotationDisplay
            }
        } = this.props;
        var annotationList = ['features', 'parts', 'translations', 'orfs', 'cutsites'];
        var toggleButtons = annotationList.map(function(annotationType, index){
            return (<button key={index} onClick={function () {
                toggleAnnotationDisplay(String(annotationType));
            }}>
            toggle {annotationType}
        </button>)
        });

        var iconButtonElement = (
            <IconButton tooltip="Settings">
                <SettingsIcon />
            </IconButton>
        );

        return (
            <div ref="sequenceEditor">
                <Toolbar>
                    <ToolbarGroup key={0}>
                        <IconMenu iconButtonElement={iconButtonElement} openDirection="bottom-right">
                            <MenuItem primaryText="Annotations" />
                            <MenuItem primaryText="ORFs" />
                        </IconMenu>
                    </ToolbarGroup>
                </Toolbar>

                {toggleButtons}

                <Clipboard
                    value={selectedSequenceString}
                    onCopy={this.handleCopy.bind(this)}
                    onPaste={this.handlePaste.bind(this)}/>

                <RowView
                    handleEditorDrag={this.handleEditorDrag.bind(this)}
                    handleEditorDragStart={this.handleEditorDragStart.bind(this)}
                    handleEditorDragStop={this.handleEditorDragStop.bind(this)}
                    handleEditorClick={this.handleEditorClick.bind(this)}
                />

                <BottomStatusBar/>
            </div>
        );
    }
}

module.exports = SequenceEditor;
<commit_msg>swap buttons for menu items<commit_after>import React, {PropTypes} from 'react';
import { propTypes } from './react-props-decorators.js'; //tnrtodo: update this once the actual npm module updates its dependencies
var Combokeys = require("combokeys");
var combokeys;
var bindGlobalPlugin = require('combokeys/plugins/global-bind');

var RowView = require('./RowView');
var MapView = require('./MapView');
var BottomStatusBar = require('./BottomStatusBar');

var Clipboard = require('./Clipboard');
import {Decorator as Cerebral} from 'cerebral-react';

/* Toolbar */
import { Toolbar, ToolbarGroup } from 'material-ui';
import { IconMenu } from 'material-ui';
import { IconButton } from 'material-ui';
import SettingsIcon from 'material-ui/lib/svg-icons/action/settings';
import MenuItem from 'material-ui/lib/menus/menu-item';

@Cerebral({
    sequenceLength: ['sequenceLength'],
    bpsPerRow: ['bpsPerRow'],
    totalRows: ['totalRows'],
    newRandomRowToJumpTo: ['newRandomRowToJumpTo'],
    selectedSequenceString: ['selectedSequenceString'],
    caretPosition: ['caretPosition'],
    sequenceData: ['sequenceData'],
    selectionLayer: ['selectionLayer'],
    clipboardData: ['clipboardData'],
})
@propTypes({
    sequenceLength: PropTypes.number.isRequired,
    bpsPerRow: PropTypes.number.isRequired,
    totalRows: PropTypes.number.isRequired,
    newRandomRowToJumpTo: PropTypes.object,
    selectedSequenceString: PropTypes.string.isRequired,
    caretPosition: PropTypes.number.isRequired,
    sequenceData: PropTypes.object.isRequired,
    selectionLayer: PropTypes.object.isRequired,
    clipboardData: PropTypes.object.isRequired,
})
class SequenceEditor extends React.Component {
    componentDidMount() {
        var {
            sequenceDataInserted,
            backspacePressed,
            selectAll,
        } = this.props.signals;
        var self = this;
        combokeys = new Combokeys(document.documentElement);
        // combokeys = new Combokeys(React.findDOMNode(this.refs.sequenceEditor));
        bindGlobalPlugin(combokeys);

        //bind a bunch of keyboard shortcuts we're interested in catching
        //we're using the "mousetrap" library (available thru npm: https://www.npmjs.com/package/br-mousetrap)
        //documentation: https://craig.is/killing/mice
        combokeys.bind(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ], function(event) { // Handle shortcut
            sequenceDataInserted({newSequenceData: {sequence: String.fromCharCode(event.charCode)}});
        });
        combokeys.bind(['left','shift+left'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretLeftOne'});
        });
        combokeys.bind(['right','shift+right'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretRightOne'});
        });
        combokeys.bind(['up','shift+up'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretUpARow'});
        });
        combokeys.bindGlobal(['down','shift+down'] , function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretDownARow'});
        });
        combokeys.bindGlobal(['mod+right','mod+shift+right'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+left','mod+shift+left'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfRow'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+up','mod+shift+up'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToStartOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal(['mod+down','mod+shift+down'], function(event) { // Handle shortcut
            self.props.signals.caretMoved({shiftHeld: event.shiftKey, type: 'moveCaretToEndOfSequence'});
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bind('backspace', function(event) { // Handle shortcut
            backspacePressed();
            event.stopPropagation();
            event.preventDefault();
        });
        combokeys.bindGlobal('command+a', function(event) { // Handle shortcut
            selectAll();
            event.stopPropagation();
        });
    }

    handlePaste(event) {
        var {
            pasteSequenceString,
        } = this.props.signals;
        event.clipboardData.items[0].getAsString(function(string) {
            pasteSequenceString(string);
        });
    }

    handleCopy() {
        var {
            copySelection,
        } = this.props.signals;
        copySelection();
        // this.props.selectedSequenceString
    }

    componentWillUnmount() {

        // Remove any Mousetrap bindings before unmounting.detach()
        combokeys.detach()
    }

    handleEditorClick(updatedCaretPos, event) {
        //if cursor position is different than the original position, reset the position and clear the selection
        // console.log('onclick!!');
        // var bp = this.getNearestCursorPositionToMouseEvent(event);
        if (this.editorBeingDragged) {
            //do nothing because the click was triggered by a drag event
        } else {
            this.props.signals.editorClicked({
                shiftHeld: event.shiftKey,
                type: 'editorClick',
                updatedCaretPos: updatedCaretPos
            })
        }

    }

    handleEditorDrag(caretPosition) {
        var {
            setCaretPosition,
            setSelectionLayer
        } = this.props.signals;
        //note this method relies on variables that are set in the handleEditorDragStart method!
        this.editorBeingDragged = true;
        if (caretPosition === this.fixedCaretPositionOnEditorDragStart) {
            setCaretPosition(caretPosition);
            setSelectionLayer(false);
        } else {
            var newSelectionLayer;
            if (this.fixedCaretPositionOnEditorDragStartType === 'start') {
                newSelectionLayer = {
                    start: this.fixedCaretPositionOnEditorDragStart,
                    end: caretPosition - 1,
                    cursorAtEnd: true,
                };
            } else if (this.fixedCaretPositionOnEditorDragStartType === 'end') {
                newSelectionLayer = {
                    start: caretPosition,
                    end: this.fixedCaretPositionOnEditorDragStart - 1,
                    cursorAtEnd: false,
                };
            } else {
                if (caretPosition > this.fixedCaretPositionOnEditorDragStart) {
                    newSelectionLayer = {
                        start: this.fixedCaretPositionOnEditorDragStart,
                        end: caretPosition - 1,
                        cursorAtEnd: true,
                    };
                } else {
                    newSelectionLayer = {
                        start: caretPosition,
                        end: this.fixedCaretPositionOnEditorDragStart - 1,
                        cursorAtEnd: false,
                    };
                }
            }
            setSelectionLayer({selectionLayer: newSelectionLayer});
        }
    }

    handleEditorDragStart(caretPosition, event) {
        var {selectionLayer} = this.props;
        // var caretPosition = this.getNearestCursorPositionToMouseEvent(event);
        if (event.target.className === "cursor" && selectionLayer.selected) {
            // this.circularSelectionOnEditorDragStart = (selectionLayer.start > selectionLayer.end);
            if (selectionLayer.start === caretPosition) {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.end + 1;
                this.fixedCaretPositionOnEditorDragStartType = 'end';

                //plus one because the cursor position will be 1 more than the selectionLayer.end
                //imagine selection from
                //0 1 2  <--possible cursor positions
                // A T G
                //if A is selected, selection.start = 0, selection.end = 0
                //so the caretPosition for the end of the selection is 1!
                //which is selection.end+1
            } else {
                this.fixedCaretPositionOnEditorDragStart = selectionLayer.start;
                this.fixedCaretPositionOnEditorDragStartType = 'start';
            }
        } else {
            // this.circularSelectionOnEditorDragStart = false;
            this.fixedCaretPositionOnEditorDragStart = caretPosition;
            this.fixedCaretPositionOnEditorDragStartType = 'caret';
        }
    }

    handleEditorDragStop(event, ui) {
        var self = this;
        if (this.editorBeingDragged) { //check to make sure dragging actually occurred
            setTimeout(function() {
                //we use setTimeout to put the call to change editorBeingDragged to false
                //on the bottom of the event stack, thus the click event that is fired because of the drag
                //will be able to check if editorBeingDragged and not trigger if it is
                self.editorBeingDragged = false;
            }, 0);
        } else {
            self.editorBeingDragged = false;
        }
    }

    render() {
        var {
            selectedSequenceString,
            signals: {
                toggleAnnotationDisplay
            }
        } = this.props;
        var annotationList = ['features', 'parts', 'translations', 'orfs', 'cutsites'];
        var toggleMenuItems = annotationList.map(function(annotationType, index){
            return (
                <MenuItem key={index} primaryText={annotationType} onClick={function () {
                    toggleAnnotationDisplay(String(annotationType));
                }} />
            );
        });

        var iconButtonElement = (
            <IconButton tooltip="Settings">
                <SettingsIcon />
            </IconButton>
        );

        return (
            <div ref="sequenceEditor">
                <Toolbar>
                    <ToolbarGroup key={0}>
                        <IconMenu iconButtonElement={iconButtonElement} openDirection="bottom-right">
                            {toggleMenuItems}
                        </IconMenu>
                    </ToolbarGroup>
                </Toolbar>

                <Clipboard
                    value={selectedSequenceString}
                    onCopy={this.handleCopy.bind(this)}
                    onPaste={this.handlePaste.bind(this)}/>

                <RowView
                    handleEditorDrag={this.handleEditorDrag.bind(this)}
                    handleEditorDragStart={this.handleEditorDragStart.bind(this)}
                    handleEditorDragStop={this.handleEditorDragStop.bind(this)}
                    handleEditorClick={this.handleEditorClick.bind(this)}
                />

                <BottomStatusBar/>
            </div>
        );
    }
}

module.exports = SequenceEditor;
<|endoftext|>