<commit_before>// Copyright 2012 Software Freedom Conservancy. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Defines utilities for exchanging messages between the
 * sandboxed SafariDriver injected script and its corresponding content page.
 */

goog.provide('safaridriver.inject.page');

goog.require('bot.Error');
goog.require('bot.ErrorCode');
goog.require('bot.dom');
goog.require('goog.array');
goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.dom.classes');
goog.require('safaridriver.console');
goog.require('safaridriver.message');
goog.require('webdriver.CommandName');
goog.require('webdriver.error');
goog.require('webdriver.promise');

/**
 * @define {boolean} Whether this script is being used by the extension or has
 *     been injected into the web page.
 */
safaridriver.inject.page.EXTENSION = true;

/**
 * Class name assigned to the SCRIPT element used to insert the compiled form of
 * this script into the web page.
 * @type {string}
 * @const
 * @private
 */
safaridriver.inject.page.SCRIPT_CLASS_NAME_ = 'safari-driver';

/**
 * @type {!goog.debug.Logger}
 * @const
 * @private
 */
safaridriver.inject.page.LOG_ = goog.debug.Logger.getLogger(
    'safaridriver.inject.page.' +
        (safaridriver.inject.page.EXTENSION ? 'extension' : 'webpage'));

/**
 * Initializes this script. When included in the SafariDriver extension, this
 * appends a SCRIPT element to the DOM that loads the page script. When
 * included as the page script, this sends a LOADED message to the injected
 * script, informing it that the page script has been successfully loaded.
 */
safaridriver.inject.page.init = function() {
  var script;
  if (safaridriver.inject.page.EXTENSION) {
    safaridriver.inject.page.LOG_.info('Initializing for extension');

    script = document.createElement('script');
    script.className = safaridriver.inject.page.SCRIPT_CLASS_NAME_;
    script.type = 'text/javascript';
    script.src = safari.extension.baseURI + 'page.js';
    document.documentElement.appendChild(script);
  } else {
    safaridriver.console.init();
    safaridriver.inject.page.LOG_.info('Initializing for page');

    window.addEventListener('message', safaridriver.inject.page.onMessage_,
        true);

    var message = new safaridriver.message.Message(
        safaridriver.message.Type.LOADED);
    safaridriver.inject.page.LOG_.info('Sending ' + message);
    message.send(window);

    script = document.querySelector(
        'script.' + safaridriver.inject.page.SCRIPT_CLASS_NAME_ +
            ':last-child');
    // If we find the script running this script, remove it.
    if (script) {
      goog.dom.removeNode(script);
    }
  }
};

if (!safaridriver.inject.page.EXTENSION) {
  goog.exportSymbol('init', safaridriver.inject.page.init);
}

/**
 * Responds to messages from the injected script.
 * @param {Event} e The message event.
 * @private
 */
safaridriver.inject.page.onMessage_ = function(e) {
  try {
    var message = safaridriver.message.Message.fromEvent(
        (/** @type {!MessageEvent} */e));
  } catch (ex) {
    // Silently ignore parse failure; message may not have origininated from the
    // injected script.
    return;
  }

  // Ignore messages that are from this context. How would we receive our own
  // messages?  Simple - when we post a message to the page, in addition to
  // going to the page, it will be posted back on our own window.
  if (message.getOrigin() === safaridriver.message.ORIGIN) {
    return;
  }

  safaridriver.inject.page.LOG_.info('onMessage: ' + JSON.stringify(message));
  switch (message.getType()) {
    case safaridriver.message.Type.COMMAND:
      safaridriver.inject.page.onCommand_(
          (/** @type {!safaridriver.message.CommandMessage} */message));
      return;

    default:
      return;  // Ignore unknown message type.
  }
};

/**
 * Handles command messages from the injected script.
 * @param {!safaridriver.message.CommandMessage} message The command message.
 * @throws {Error} If the command is not supported by this script.
 * @private
 */
safaridriver.inject.page.onCommand_ = function(message) {
  var command = message.getCommand();
  switch (command.getName()) {
    case webdriver.CommandName.EXECUTE_ASYNC_SCRIPT:
      safaridriver.inject.page.executeAsyncScript_(command);
      break;

    case webdriver.CommandName.EXECUTE_SCRIPT:
      safaridriver.inject.page.executeScript_(command);
      return;

    default:
      throw Error('Unknown command: ' + command.getName());
  }
};

/**
 * Locates an element by XPath.
 * @param {string} xpath The XPath expression.
 * @return {Element} The located element or {@code null}.
 * @private
 */
safaridriver.inject.page.getElementByXpath_ = function(xpath) {
  return document.evaluate(xpath, document, null,
      XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue;
};

/**
 * Computes the canonical XPath locator for an element.
 * @param {!Element} element The element to compute an XPath expression for.
 * @return {string} The element's XPath locator.
 * @private
 */
safaridriver.inject.page.getElementXPath_ = function(element) {
  var path = '';
  for(; element; element = bot.dom.getParentElement(element)) {
    var index = 1;
    for (var sibling = element.previousSibling; sibling;
        sibling = sibling.previousSibling) {
      if (sibling.nodeType == goog.dom.NodeType.ELEMENT &&
          sibling.tagName == element.tagName) {
        index++;
      }
    }
    var tmp = '/' + element.tagName;
    if (index > 1) {
      tmp += '[' + index + ']';
    }
    path = tmp + path;
  }
  return path;
};

/**
 * Key used in an object literal to indicate it is the encoded representation of
 * a DOM element. The corresponding property's value will be a CSS selector for
 * the encoded elmeent.
 * @type {string}
 * @const
 * @private
 */
safaridriver.inject.page.ENCODED_ELEMENT_KEY_ = 'WebElement';

/**
 * Encodes a value so it may be included in a message exchanged between the
 * document and sandboxed injected script. Any DOM element references will
 * be replaced with an object literal whose sole key is t
 * @param {*} value The value to encode.
 * @return {*} The encoded value.
 * @throws {Error} If the value is cannot be encoded (e.g. it is a function, or
 *     an array or object with a cyclical reference).
 */
safaridriver.inject.page.encodeValue = function(value) {
  var type = goog.typeOf(value);
  switch (type) {
    case 'boolean':
    case 'number':
    case 'string':
      return value;

    case 'null':
    case 'undefined':
      return null;

    case 'array':
      return goog.array.map((/** @type {!Array} */value),
          safaridriver.inject.page.encodeValue);

    case 'object':
      if (bot.dom.isElement(value)) {
        // We don't permit elements belong to another document because we
        // wouldn't be able to find them again on the other side when the
        // element was decoded.
        if (value.ownerDocument !== document) {
          throw Error('The element does not belong to this document: ' +
              safaridriver.inject.page.getElementCssSelector_(value));
        }

        var encoded = {};
        encoded[safaridriver.inject.page.ENCODED_ELEMENT_KEY_] =
            safaridriver.inject.page.getElementCssSelector_(value);
        return encoded;
      }
      return goog.object.map((/** @type {!Object} */value),
          safaridriver.inject.page.encodeValue);

    case 'function':
    default:
      throw Error('Invalid value type: ' + type + ' => ' + value);
  }
};

/**
 * Decodes a value. Any object literals whose sole key is
 * {@link safaridriver.inject.page.ENCODED_ELEMENT_KEY_} will be considered an
 * encoded reference to a DOM element. The corresponding value for this key will
 * be used as a CSS selector to locate the element.
 * @param {*} value The value to decode.
 * @return {*} The decoded value.
 * @throws {bot.Error} If an encoded DOM element cannot be located on the page.
 * @throws {Error} If the value is an invalid type, or an array or object with
 *     cyclical references.
 */
safaridriver.inject.page.decodeValue = function(value) {
  var type = goog.typeOf(value);
  switch (type) {
    case 'boolean':
    case 'number':
    case 'string':
      return value;

    case 'null':
    case 'undefined':
      return null;

    case 'array':
      return goog.array.map((/** @type {!Array} */value),
          safaridriver.inject.page.decodeValue);

    case 'object':
      var obj = (/** @type {!Object} */value);
      var keys = Object.keys(obj);
      if (keys.length == 1 &&
          keys[0] === safaridriver.inject.page.ENCODED_ELEMENT_KEY_) {
        var xpath = value[safaridriver.inject.page.ENCODED_ELEMENT_KEY_];
        var element = safaridriver.inject.page.getElementByXpath_(xpath);
        if (!element) {
          throw new bot.Error(bot.ErrorCode.STALE_ELEMENT_REFERENCE,
              'Unable to locate encoded element: ' + xpath);
        }
        return element;
      }
      return goog.object.map(obj, safaridriver.inject.page.decodeValue);

    case 'function':
    default:
      throw Error('Invalid value type: ' + type + ' => ' + value);
  }
};

/**
 * Handles an executeScript command.
 * @param {!safaridriver.Command} command The command to execute.
 * @private
 */
safaridriver.inject.page.executeScript_ = function(command) {
  var response;
  try {
    // TODO: clean-up bot.inject.executeScript so it doesn't pull in so many
    // extra dependencies.
    var fn = new Function(command.getParameter('script'));

    var args = command.getParameter('args');
    args = (/** @type {!Array} */safaridriver.inject.page.decodeValue(args));

    response = fn.apply(window, args);
    console.log('script response', response);
    response = safaridriver.inject.page.encodeValue(response);
    response = {
      'status': bot.ErrorCode.SUCCESS,
      'value': response
    };
  } catch (ex) {
    response = webdriver.error.createResponse(ex);
  }
  var responseMessage = new safaridriver.message.ResponseMessage(
      command.getId(), response);
  safaridriver.inject.page.LOG_.info(
      'Sending executeScript response: ' + responseMessage);
  responseMessage.send(window);
};

/**
 * Handles an executeAsyncScript command.
 * @param {!safaridriver.Command} command The command to execute.
 * @private
 */
safaridriver.inject.page.executeAsyncScript_ = function(command) {
  var response = new webdriver.promise.Deferred();
  // When the response is resolved, we want to wrap it up in a message and
  // send it back to the injected script. This does all that.
  response.
      then(function(value) {
        value = safaridriver.inject.page.encodeValue(value);
        return {
          'status': bot.ErrorCode.SUCCESS,
          'value': value
        };
      }).
      then(null, webdriver.error.createResponse).
      then(function(response) {
        var responseMessage = new safaridriver.message.ResponseMessage(
            command.getId(), response);
        safaridriver.inject.page.LOG_.info(
            'Sending executeAsyncScript response: ' + responseMessage);
        responseMessage.send(window);
      });

  try {
    var script = (/** @type {string} */command.getParameter('script'));
    var scriptFn = new Function(script);

    var args = command.getParameter('args');
    args = (/** @type {!Array} */safaridriver.inject.page.decodeValue(args));
    // The last argument for an async script is the callback that triggers the
    // response.
    args.push(function(value) {
      window.clearTimeout(timeoutId);
      if (response.isPending()) {
        response.resolve(value);
      }
    });

    var startTime = goog.now();
    scriptFn.apply(window, args);

    // Register our timeout *after* the function has been invoked. This will
    // ensure we don't timeout on a function that invokes its callback after a
    // 0-based timeout:
    // var scriptFn = function(callback) {
    //   setTimeout(callback, 0);
    // };
    var timeout = (/** @type {number} */command.getParameter('timeout'));
    var timeoutId = window.setTimeout(function() {
      if (response.isPending()) {
        response.reject(new bot.Error(bot.ErrorCode.SCRIPT_TIMEOUT,
            'Timed out waiting for an asynchronous script result after ' +
                (goog.now() - startTime) +  ' ms'));
      }
    }, Math.max(0, timeout));
  } catch (ex) {
    if (response.isPending()) {
      response.reject(ex);
    }
  }
};
<commit_msg>JasonLeyba: It would help if I ran the tests before committing code...<commit_after>// Copyright 2012 Software Freedom Conservancy. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Defines utilities for exchanging messages between the
 * sandboxed SafariDriver injected script and its corresponding content page.
 */

goog.provide('safaridriver.inject.page');

goog.require('bot.Error');
goog.require('bot.ErrorCode');
goog.require('bot.dom');
goog.require('goog.array');
goog.require('goog.debug.Logger');
goog.require('goog.dom');
goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.dom.classes');
goog.require('safaridriver.console');
goog.require('safaridriver.message');
goog.require('webdriver.CommandName');
goog.require('webdriver.error');
goog.require('webdriver.promise');

/**
 * @define {boolean} Whether this script is being used by the extension or has
 *     been injected into the web page.
 */
safaridriver.inject.page.EXTENSION = true;

/**
 * Class name assigned to the SCRIPT element used to insert the compiled form of
 * this script into the web page.
 * @type {string}
 * @const
 * @private
 */
safaridriver.inject.page.SCRIPT_CLASS_NAME_ = 'safari-driver';

/**
 * @type {!goog.debug.Logger}
 * @const
 * @private
 */
safaridriver.inject.page.LOG_ = goog.debug.Logger.getLogger(
    'safaridriver.inject.page.' +
        (safaridriver.inject.page.EXTENSION ? 'extension' : 'webpage'));

/**
 * Initializes this script. When included in the SafariDriver extension, this
 * appends a SCRIPT element to the DOM that loads the page script. When
 * included as the page script, this sends a LOADED message to the injected
 * script, informing it that the page script has been successfully loaded.
 */
safaridriver.inject.page.init = function() {
  var script;
  if (safaridriver.inject.page.EXTENSION) {
    safaridriver.inject.page.LOG_.info('Initializing for extension');

    script = document.createElement('script');
    script.className = safaridriver.inject.page.SCRIPT_CLASS_NAME_;
    script.type = 'text/javascript';
    script.src = safari.extension.baseURI + 'page.js';
    document.documentElement.appendChild(script);
  } else {
    safaridriver.console.init();
    safaridriver.inject.page.LOG_.info('Initializing for page');

    window.addEventListener('message', safaridriver.inject.page.onMessage_,
        true);

    var message = new safaridriver.message.Message(
        safaridriver.message.Type.LOADED);
    safaridriver.inject.page.LOG_.info('Sending ' + message);
    message.send(window);

    script = document.querySelector(
        'script.' + safaridriver.inject.page.SCRIPT_CLASS_NAME_ +
            ':last-child');
    // If we find the script running this script, remove it.
    if (script) {
      goog.dom.removeNode(script);
    }
  }
};

if (!safaridriver.inject.page.EXTENSION) {
  goog.exportSymbol('init', safaridriver.inject.page.init);
}

/**
 * Responds to messages from the injected script.
 * @param {Event} e The message event.
 * @private
 */
safaridriver.inject.page.onMessage_ = function(e) {
  try {
    var message = safaridriver.message.Message.fromEvent(
        (/** @type {!MessageEvent} */e));
  } catch (ex) {
    // Silently ignore parse failure; message may not have origininated from the
    // injected script.
    return;
  }

  // Ignore messages that are from this context. How would we receive our own
  // messages?  Simple - when we post a message to the page, in addition to
  // going to the page, it will be posted back on our own window.
  if (message.getOrigin() === safaridriver.message.ORIGIN) {
    return;
  }

  safaridriver.inject.page.LOG_.info('onMessage: ' + JSON.stringify(message));
  switch (message.getType()) {
    case safaridriver.message.Type.COMMAND:
      safaridriver.inject.page.onCommand_(
          (/** @type {!safaridriver.message.CommandMessage} */message));
      return;

    default:
      return;  // Ignore unknown message type.
  }
};

/**
 * Handles command messages from the injected script.
 * @param {!safaridriver.message.CommandMessage} message The command message.
 * @throws {Error} If the command is not supported by this script.
 * @private
 */
safaridriver.inject.page.onCommand_ = function(message) {
  var command = message.getCommand();
  switch (command.getName()) {
    case webdriver.CommandName.EXECUTE_ASYNC_SCRIPT:
      safaridriver.inject.page.executeAsyncScript_(command);
      break;

    case webdriver.CommandName.EXECUTE_SCRIPT:
      safaridriver.inject.page.executeScript_(command);
      return;

    default:
      throw Error('Unknown command: ' + command.getName());
  }
};

/**
 * Locates an element by XPath.
 * @param {string} xpath The XPath expression.
 * @return {Element} The located element or {@code null}.
 * @private
 */
safaridriver.inject.page.getElementByXpath_ = function(xpath) {
  return document.evaluate(xpath, document, null,
      XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue;
};

/**
 * Computes the canonical XPath locator for an element.
 * @param {!Element} element The element to compute an XPath expression for.
 * @return {string} The element's XPath locator.
 * @private
 */
safaridriver.inject.page.getElementXPath_ = function(element) {
  var path = '';
  for (var current = element; current;
       current = bot.dom.getParentElement(current)) {
    var index = 1;
    for (var sibling = current.previousSibling; sibling;
        sibling = sibling.previousSibling) {
      if (sibling.nodeType == goog.dom.NodeType.ELEMENT &&
          sibling.tagName == current.tagName) {
        index++;
      }
    }
    var tmp = '/' + current.tagName;
    if (index > 1) {
      tmp += '[' + index + ']';
    }
    path = tmp + path;
  }
  return path;
};

/**
 * Key used in an object literal to indicate it is the encoded representation of
 * a DOM element. The corresponding property's value will be a CSS selector for
 * the encoded elmeent.
 * @type {string}
 * @const
 * @private
 */
safaridriver.inject.page.ENCODED_ELEMENT_KEY_ = 'WebElement';

/**
 * Encodes a value so it may be included in a message exchanged between the
 * document and sandboxed injected script. Any DOM element references will
 * be replaced with an object literal whose sole key is t
 * @param {*} value The value to encode.
 * @return {*} The encoded value.
 * @throws {Error} If the value is cannot be encoded (e.g. it is a function, or
 *     an array or object with a cyclical reference).
 */
safaridriver.inject.page.encodeValue = function(value) {
  var type = goog.typeOf(value);
  switch (type) {
    case 'boolean':
    case 'number':
    case 'string':
      return value;

    case 'null':
    case 'undefined':
      return null;

    case 'array':
      return goog.array.map((/** @type {!Array} */value),
          safaridriver.inject.page.encodeValue);

    case 'object':
      if (goog.dom.isElement(value)) {
        // We don't permit elements belong to another document because we
        // wouldn't be able to find them again on the other side when the
        // element was decoded.
        if (value.ownerDocument !== document) {
          throw Error('The element does not belong to this document: ' +
              safaridriver.inject.page.getElementXPath_(
                  (/** @type {!Element} */value)));
        }

        var encoded = {};
        encoded[safaridriver.inject.page.ENCODED_ELEMENT_KEY_] =
            safaridriver.inject.page.getElementXPath_(
                (/** @type {!Element} */value));
        return encoded;
      }
      return goog.object.map((/** @type {!Object} */value),
          safaridriver.inject.page.encodeValue);

    case 'function':
    default:
      throw Error('Invalid value type: ' + type + ' => ' + value);
  }
};

/**
 * Decodes a value. Any object literals whose sole key is
 * {@link safaridriver.inject.page.ENCODED_ELEMENT_KEY_} will be considered an
 * encoded reference to a DOM element. The corresponding value for this key will
 * be used as a CSS selector to locate the element.
 * @param {*} value The value to decode.
 * @return {*} The decoded value.
 * @throws {bot.Error} If an encoded DOM element cannot be located on the page.
 * @throws {Error} If the value is an invalid type, or an array or object with
 *     cyclical references.
 */
safaridriver.inject.page.decodeValue = function(value) {
  var type = goog.typeOf(value);
  switch (type) {
    case 'boolean':
    case 'number':
    case 'string':
      return value;

    case 'null':
    case 'undefined':
      return null;

    case 'array':
      return goog.array.map((/** @type {!Array} */value),
          safaridriver.inject.page.decodeValue);

    case 'object':
      var obj = (/** @type {!Object} */value);
      var keys = Object.keys(obj);
      if (keys.length == 1 &&
          keys[0] === safaridriver.inject.page.ENCODED_ELEMENT_KEY_) {
        var xpath = value[safaridriver.inject.page.ENCODED_ELEMENT_KEY_];
        var element = safaridriver.inject.page.getElementByXpath_(xpath);
        if (!element) {
          throw new bot.Error(bot.ErrorCode.STALE_ELEMENT_REFERENCE,
              'Unable to locate encoded element: ' + xpath);
        }
        return element;
      }
      return goog.object.map(obj, safaridriver.inject.page.decodeValue);

    case 'function':
    default:
      throw Error('Invalid value type: ' + type + ' => ' + value);
  }
};

/**
 * Handles an executeScript command.
 * @param {!safaridriver.Command} command The command to execute.
 * @private
 */
safaridriver.inject.page.executeScript_ = function(command) {
  var response;
  try {
    // TODO: clean-up bot.inject.executeScript so it doesn't pull in so many
    // extra dependencies.
    var fn = new Function(command.getParameter('script'));

    var args = command.getParameter('args');
    args = (/** @type {!Array} */safaridriver.inject.page.decodeValue(args));

    response = fn.apply(window, args);
    console.log('script response', response);
    response = safaridriver.inject.page.encodeValue(response);
    response = {
      'status': bot.ErrorCode.SUCCESS,
      'value': response
    };
  } catch (ex) {
    response = webdriver.error.createResponse(ex);
  }
  var responseMessage = new safaridriver.message.ResponseMessage(
      command.getId(), response);
  safaridriver.inject.page.LOG_.info(
      'Sending executeScript response: ' + responseMessage);
  responseMessage.send(window);
};

/**
 * Handles an executeAsyncScript command.
 * @param {!safaridriver.Command} command The command to execute.
 * @private
 */
safaridriver.inject.page.executeAsyncScript_ = function(command) {
  var response = new webdriver.promise.Deferred();
  // When the response is resolved, we want to wrap it up in a message and
  // send it back to the injected script. This does all that.
  response.
      then(function(value) {
        value = safaridriver.inject.page.encodeValue(value);
        return {
          'status': bot.ErrorCode.SUCCESS,
          'value': value
        };
      }).
      then(null, webdriver.error.createResponse).
      then(function(response) {
        var responseMessage = new safaridriver.message.ResponseMessage(
            command.getId(), response);
        safaridriver.inject.page.LOG_.info(
            'Sending executeAsyncScript response: ' + responseMessage);
        responseMessage.send(window);
      });

  try {
    var script = (/** @type {string} */command.getParameter('script'));
    var scriptFn = new Function(script);

    var args = command.getParameter('args');
    args = (/** @type {!Array} */safaridriver.inject.page.decodeValue(args));
    // The last argument for an async script is the callback that triggers the
    // response.
    args.push(function(value) {
      window.clearTimeout(timeoutId);
      if (response.isPending()) {
        response.resolve(value);
      }
    });

    var startTime = goog.now();
    scriptFn.apply(window, args);

    // Register our timeout *after* the function has been invoked. This will
    // ensure we don't timeout on a function that invokes its callback after a
    // 0-based timeout:
    // var scriptFn = function(callback) {
    //   setTimeout(callback, 0);
    // };
    var timeout = (/** @type {number} */command.getParameter('timeout'));
    var timeoutId = window.setTimeout(function() {
      if (response.isPending()) {
        response.reject(new bot.Error(bot.ErrorCode.SCRIPT_TIMEOUT,
            'Timed out waiting for an asynchronous script result after ' +
                (goog.now() - startTime) +  ' ms'));
      }
    }, Math.max(0, timeout));
  } catch (ex) {
    if (response.isPending()) {
      response.reject(ex);
    }
  }
};
<|endoftext|>


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

cr.define('print_preview', function() {
  'use strict';

  /**
   * Print destination data object that holds data for both local and cloud
   * destinations.
   * @param {string} id ID of the destination.
   * @param {!print_preview.Destination.Type} type Type of the destination.
   * @param {!print_preview.Destination.Origin} origin Origin of the
   *     destination.
   * @param {string} displayName Display name of the destination.
   * @param {boolean} isRecent Whether the destination has been used recently.
   * @param {!print_preview.Destination.ConnectionStatus} connectionStatus
   *     Connection status of the print destination.
   * @param {{tags: Array.<string>,
   *          isOwned: ?boolean,
   *          lastAccessTime: ?number,
   *          isTosAccepted: ?boolean,
   *          cloudID: ?string,
   *          description: ?string}=} opt_params Optional parameters for the
   *     destination.
   * @constructor
   */
  function Destination(id, type, origin, displayName, isRecent,
                       connectionStatus, opt_params) {
    /**
     * ID of the destination.
     * @type {string}
     * @private
     */
    this.id_ = id;

    /**
     * Type of the destination.
     * @type {!print_preview.Destination.Type}
     * @private
     */
    this.type_ = type;

    /**
     * Origin of the destination.
     * @type {!print_preview.Destination.Origin}
     * @private
     */
    this.origin_ = origin;

    /**
     * Display name of the destination.
     * @type {string}
     * @private
     */
    this.displayName_ = displayName || '';

    /**
     * Whether the destination has been used recently.
     * @type {boolean}
     * @private
     */
    this.isRecent_ = isRecent;

    /**
     * Tags associated with the destination.
     * @type {!Array.<string>}
     * @private
     */
    this.tags_ = (opt_params && opt_params.tags) || [];

    /**
     * Print capabilities of the destination.
     * @type {print_preview.Cdd}
     * @private
     */
    this.capabilities_ = null;

    /**
     * Whether the destination is owned by the user.
     * @type {boolean}
     * @private
     */
    this.isOwned_ = (opt_params && opt_params.isOwned) || false;

    /**
     * Cache of destination location fetched from tags.
     * @type {?string}
     * @private
     */
    this.location_ = null;

    /**
     * Printer description.
     * @type {?string}
     * @private
     */
    this.description_ = (opt_params && opt_params.description) || '';

    /**
     * Connection status of the destination.
     * @type {!print_preview.Destination.ConnectionStatus}
     * @private
     */
    this.connectionStatus_ = connectionStatus;

    /**
     * Number of milliseconds since the epoch when the printer was last
     * accessed.
     * @type {number}
     * @private
     */
    this.lastAccessTime_ = (opt_params && opt_params.lastAccessTime) ||
                           Date.now();

    /**
     * Whether the user has accepted the terms-of-service for the print
     * destination. Only applies to the FedEx Office cloud-based printer.
     * {@code} null if terms-of-service does not apply to the print destination.
     * @type {?boolean}
     * @private
     */
    this.isTosAccepted_ = (opt_params && opt_params.isTosAccepted) || false;

    /**
     * Cloud ID for Privet printers.
     * @type {?string}
     * @private
     */
    this.cloudID_ = (opt_params && opt_params.cloudID) || '';
  };

  /**
   * Prefix of the location destination tag.
   * @type {!Array.<string>}
   * @const
   */
  Destination.LOCATION_TAG_PREFIXES = [
    '__cp__location=',
    '__cp__printer-location='
  ];

  /**
   * Enumeration of Google-promoted destination IDs.
   * @enum {string}
   */
  Destination.GooglePromotedId = {
    DOCS: '__google__docs',
    FEDEX: '__google__fedex',
    SAVE_AS_PDF: 'Save as PDF'
  };

  /**
   * Enumeration of the types of destinations.
   * @enum {string}
   */
  Destination.Type = {
    GOOGLE: 'google',
    LOCAL: 'local',
    MOBILE: 'mobile'
  };

  /**
   * Enumeration of the origin types for cloud destinations.
   * @enum {string}
   */
  Destination.Origin = {
    LOCAL: 'local',
    COOKIES: 'cookies',
    PROFILE: 'profile',
    DEVICE: 'device',
    PRIVET: 'privet'
  };

  /**
   * Enumeration of the connection statuses of printer destinations.
   * @enum {string}
   */
  Destination.ConnectionStatus = {
    DORMANT: 'DORMANT',
    OFFLINE: 'OFFLINE',
    ONLINE: 'ONLINE',
    UNKNOWN: 'UNKNOWN',
    UNREGISTERED: 'UNREGISTERED'
  };

  /**
   * Enumeration of relative icon URLs for various types of destinations.
   * @enum {string}
   * @private
   */
  Destination.IconUrl_ = {
    CLOUD: 'images/printer.png',
    CLOUD_SHARED: 'images/printer_shared.png',
    LOCAL: 'images/printer.png',
    MOBILE: 'images/mobile.png',
    MOBILE_SHARED: 'images/mobile_shared.png',
    THIRD_PARTY: 'images/third_party.png',
    PDF: 'images/pdf.png',
    DOCS: 'images/google_doc.png',
    FEDEX: 'images/third_party_fedex.png'
  };

  Destination.prototype = {
    /** @return {string} ID of the destination. */
    get id() {
      return this.id_;
    },

    /** @return {!print_preview.Destination.Type} Type of the destination. */
    get type() {
      return this.type_;
    },

    /**
     * @return {!print_preview.Destination.Origin} Origin of the destination.
     */
    get origin() {
      return this.origin_;
    },

    /** @return {string} Display name of the destination. */
    get displayName() {
      return this.displayName_;
    },

    /** @return {boolean} Whether the destination has been used recently. */
    get isRecent() {
      return this.isRecent_;
    },

    /**
     * @param {boolean} isRecent Whether the destination has been used recently.
     */
    set isRecent(isRecent) {
      this.isRecent_ = isRecent;
    },

    /**
     * @return {boolean} Whether the user owns the destination. Only applies to
     *     cloud-based destinations.
     */
    get isOwned() {
      return this.isOwned_;
    },

    /** @return {boolean} Whether the destination is local or cloud-based. */
    get isLocal() {
      return this.origin_ == Destination.Origin.LOCAL ||
             (this.origin_ == Destination.Origin.PRIVET &&
              this.connectionStatus_ !=
              Destination.ConnectionStatus.UNREGISTERED);
    },

    /** @return {boolean} Whether the destination is a privet local printer */
    get isPrivet() {
      return this.origin_ == Destination.Origin.PRIVET;
    },

    /**
     * @return {string} The location of the destination, or an empty string if
     *     the location is unknown.
     */
    get location() {
      if (this.location_ == null) {
        this.location_ = '';
        this.tags_.some(function(tag) {
          return Destination.LOCATION_TAG_PREFIXES.some(function(prefix) {
            if (tag.indexOf(prefix) == 0) {
              this.location_ = tag.substring(prefix.length) || '';
              return true;
            }
          }, this);
        }, this);
      }
      return this.location_;
    },

    /**
     * @return {string} The description of the destination, or an empty string,
     *     if it was not provided.
     */
    get description() {
      return this.description_;
    },

    /** @return {!Array.<string>} Tags associated with the destination. */
    get tags() {
      return this.tags_.slice(0);
    },

    /** @return {string} Cloud ID associated with the destination */
    get cloudID() {
      return this.cloudID_;
    },

    /** @return {print_preview.Cdd} Print capabilities of the destination. */
    get capabilities() {
      return this.capabilities_;
    },

    /**
     * @param {!print_preview.Cdd} capabilities Print capabilities of the
     *     destination.
     */
    set capabilities(capabilities) {
      this.capabilities_ = capabilities;
    },

    /**
     * @return {!print_preview.Destination.ConnectionStatus} Connection status
     *     of the print destination.
     */
    get connectionStatus() {
      return this.connectionStatus_;
    },

    /**
     * @param {!print_preview.Destination.ConnectionStatus} status Connection
     *     status of the print destination.
     */
    set connectionStatus(status) {
      this.connectionStatus_ = status;
    },

    /** @return {boolean} Whether the destination is considered offline. */
    get isOffline() {
      return arrayContains([print_preview.Destination.ConnectionStatus.OFFLINE,
                            print_preview.Destination.ConnectionStatus.DORMANT],
                            this.connectionStatus_);
    },

    /** @return {string} Human readable status for offline destination. */
    get offlineStatusText() {
      if (!this.isOffline) {
        return '';
      }
      var offlineDurationMs = Date.now() - this.lastAccessTime_;
      var offlineMessageId;
      if (offlineDurationMs > 31622400000.0) {  // One year.
        offlineMessageId = 'offlineForYear';
      } else if (offlineDurationMs > 2678400000.0) {  // One month.
        offlineMessageId = 'offlineForMonth';
      } else if (offlineDurationMs > 604800000.0) {  // One week.
        offlineMessageId = 'offlineForWeek';
      } else {
        offlineMessageId = 'offline';
      }
      return localStrings.getString(offlineMessageId);
    },

    /**
     * @return {number} Number of milliseconds since the epoch when the printer
     *     was last accessed.
     */
    get lastAccessTime() {
      return this.lastAccessTime_;
    },

    /** @return {string} Relative URL of the destination's icon. */
    get iconUrl() {
      if (this.id_ == Destination.GooglePromotedId.DOCS) {
        return Destination.IconUrl_.DOCS;
      } else if (this.id_ == Destination.GooglePromotedId.FEDEX) {
        return Destination.IconUrl_.FEDEX;
      } else if (this.id_ == Destination.GooglePromotedId.SAVE_AS_PDF) {
        return Destination.IconUrl_.PDF;
      } else if (this.isLocal) {
        return Destination.IconUrl_.LOCAL;
      } else if (this.type_ == Destination.Type.MOBILE && this.isOwned_) {
        return Destination.IconUrl_.MOBILE;
      } else if (this.type_ == Destination.Type.MOBILE) {
        return Destination.IconUrl_.MOBILE_SHARED;
      } else if (this.isOwned_) {
        return Destination.IconUrl_.CLOUD;
      } else {
        return Destination.IconUrl_.CLOUD_SHARED;
      }
    },

    /**
     * @return {?boolean} Whether the user has accepted the terms-of-service of
     *     the print destination or {@code null} if a terms-of-service does not
     *     apply.
     */
    get isTosAccepted() {
      return this.isTosAccepted_;
    },

    /**
     * @param {?boolean} Whether the user has accepted the terms-of-service of
     *     the print destination or {@code null} if a terms-of-service does not
     *     apply.
     */
    set isTosAccepted(isTosAccepted) {
      this.isTosAccepted_ = isTosAccepted;
    },

    /**
     * @return {!Array.<string>} Properties (besides display name) to match
     * search queries against.
     */
    get extraPropertiesToMatch() {
      return [this.location, this.description_];
    },

    /**
     * Matches a query against the destination.
     * @param {!RegExp} query Query to match against the destination.
     * @return {boolean} {@code true} if the query matches this destination,
     *     {@code false} otherwise.
     */
    matches: function(query) {
      return this.displayName_.match(query) ||
          this.extraPropertiesToMatch.some(function(property) {
            return property.match(query);
          });
    }
  };

  /**
   * The CDD (Cloud Device Description) describes the capabilities of a print
   * destination.
   *
   * @typedef {{
   *   version: string,
   *   printer: {
   *     vendor_capability: !Array.<{Object}>,
   *     collate: {default: boolean=}=,
   *     color: {
   *       option: !Array.<{
   *         type: string=,
   *         vendor_id: string=,
   *         custom_display_name: string=,
   *         is_default: boolean=
   *       }>
   *     }=,
   *     copies: {default: number=, max: number=}=,
   *     duplex: {option: !Array.<{type: string=, is_default: boolean=}>}=,
   *     page_orientation: {
   *       option: !Array.<{type: string=, is_default: boolean=}>
   *     }=
   *   }
   * }}
   */
  var Cdd = Object;

  // Export
  return {
    Destination: Destination,
    Cdd: Cdd
  };
});
<commit_msg>Address style comments.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('print_preview', function() {
  'use strict';

  /**
   * Print destination data object that holds data for both local and cloud
   * destinations.
   * @param {string} id ID of the destination.
   * @param {!print_preview.Destination.Type} type Type of the destination.
   * @param {!print_preview.Destination.Origin} origin Origin of the
   *     destination.
   * @param {string} displayName Display name of the destination.
   * @param {boolean} isRecent Whether the destination has been used recently.
   * @param {!print_preview.Destination.ConnectionStatus} connectionStatus
   *     Connection status of the print destination.
   * @param {{tags: Array.<string>,
   *          isOwned: ?boolean,
   *          lastAccessTime: ?number,
   *          isTosAccepted: ?boolean,
   *          cloudID: ?string,
   *          description: ?string}=} opt_params Optional parameters for the
   *     destination.
   * @constructor
   */
  function Destination(id, type, origin, displayName, isRecent,
                       connectionStatus, opt_params) {
    /**
     * ID of the destination.
     * @private {string}
     */
    this.id_ = id;

    /**
     * Type of the destination.
     * @private {!print_preview.Destination.Type}
     */
    this.type_ = type;

    /**
     * Origin of the destination.
     * @private {!print_preview.Destination.Origin}
     */
    this.origin_ = origin;

    /**
     * Display name of the destination.
     * @private {string}
     */
    this.displayName_ = displayName || '';

    /**
     * Whether the destination has been used recently.
     * @private {boolean}
     */
    this.isRecent_ = isRecent;

    /**
     * Tags associated with the destination.
     * @private {!Array.<string>}
     */
    this.tags_ = (opt_params && opt_params.tags) || [];

    /**
     * Print capabilities of the destination.
     * @private {print_preview.Cdd}
     */
    this.capabilities_ = null;

    /**
     * Whether the destination is owned by the user.
     * @private {boolean}
     */
    this.isOwned_ = (opt_params && opt_params.isOwned) || false;

    /**
     * Cache of destination location fetched from tags.
     * @private {?string}
     */
    this.location_ = null;

    /**
     * Printer description.
     * @private {string}
     */
    this.description_ = (opt_params && opt_params.description) || '';

    /**
     * Connection status of the destination.
     * @private {!print_preview.Destination.ConnectionStatus}
     */
    this.connectionStatus_ = connectionStatus;

    /**
     * Number of milliseconds since the epoch when the printer was last
     * accessed.
     * @private {number}
     */
    this.lastAccessTime_ = (opt_params && opt_params.lastAccessTime) ||
                           Date.now();

    /**
     * Whether the user has accepted the terms-of-service for the print
     * destination. Only applies to the FedEx Office cloud-based printer.
     * {@code null} if terms-of-service does not apply to the print destination.
     * @private {?boolean}
     */
    this.isTosAccepted_ = opt_params && opt_params.isTosAccepted;

    /**
     * Cloud ID for Privet printers.
     * @private {string}
     */
    this.cloudID_ = (opt_params && opt_params.cloudID) || '';
  };

  /**
   * Prefix of the location destination tag.
   * @type {!Array.<string>}
   * @const
   */
  Destination.LOCATION_TAG_PREFIXES = [
    '__cp__location=',
    '__cp__printer-location='
  ];

  /**
   * Enumeration of Google-promoted destination IDs.
   * @enum {string}
   */
  Destination.GooglePromotedId = {
    DOCS: '__google__docs',
    FEDEX: '__google__fedex',
    SAVE_AS_PDF: 'Save as PDF'
  };

  /**
   * Enumeration of the types of destinations.
   * @enum {string}
   */
  Destination.Type = {
    GOOGLE: 'google',
    LOCAL: 'local',
    MOBILE: 'mobile'
  };

  /**
   * Enumeration of the origin types for cloud destinations.
   * @enum {string}
   */
  Destination.Origin = {
    LOCAL: 'local',
    COOKIES: 'cookies',
    PROFILE: 'profile',
    DEVICE: 'device',
    PRIVET: 'privet'
  };

  /**
   * Enumeration of the connection statuses of printer destinations.
   * @enum {string}
   */
  Destination.ConnectionStatus = {
    DORMANT: 'DORMANT',
    OFFLINE: 'OFFLINE',
    ONLINE: 'ONLINE',
    UNKNOWN: 'UNKNOWN',
    UNREGISTERED: 'UNREGISTERED'
  };

  /**
   * Enumeration of relative icon URLs for various types of destinations.
   * @enum {string}
   * @private
   */
  Destination.IconUrl_ = {
    CLOUD: 'images/printer.png',
    CLOUD_SHARED: 'images/printer_shared.png',
    LOCAL: 'images/printer.png',
    MOBILE: 'images/mobile.png',
    MOBILE_SHARED: 'images/mobile_shared.png',
    THIRD_PARTY: 'images/third_party.png',
    PDF: 'images/pdf.png',
    DOCS: 'images/google_doc.png',
    FEDEX: 'images/third_party_fedex.png'
  };

  Destination.prototype = {
    /** @return {string} ID of the destination. */
    get id() {
      return this.id_;
    },

    /** @return {!print_preview.Destination.Type} Type of the destination. */
    get type() {
      return this.type_;
    },

    /**
     * @return {!print_preview.Destination.Origin} Origin of the destination.
     */
    get origin() {
      return this.origin_;
    },

    /** @return {string} Display name of the destination. */
    get displayName() {
      return this.displayName_;
    },

    /** @return {boolean} Whether the destination has been used recently. */
    get isRecent() {
      return this.isRecent_;
    },

    /**
     * @param {boolean} isRecent Whether the destination has been used recently.
     */
    set isRecent(isRecent) {
      this.isRecent_ = isRecent;
    },

    /**
     * @return {boolean} Whether the user owns the destination. Only applies to
     *     cloud-based destinations.
     */
    get isOwned() {
      return this.isOwned_;
    },

    /** @return {boolean} Whether the destination is local or cloud-based. */
    get isLocal() {
      return this.origin_ == Destination.Origin.LOCAL ||
             (this.origin_ == Destination.Origin.PRIVET &&
              this.connectionStatus_ !=
              Destination.ConnectionStatus.UNREGISTERED);
    },

    /** @return {boolean} Whether the destination is a Privet local printer */
    get isPrivet() {
      return this.origin_ == Destination.Origin.PRIVET;
    },

    /**
     * @return {string} The location of the destination, or an empty string if
     *     the location is unknown.
     */
    get location() {
      if (this.location_ == null) {
        this.location_ = '';
        this.tags_.some(function(tag) {
          return Destination.LOCATION_TAG_PREFIXES.some(function(prefix) {
            if (tag.indexOf(prefix) == 0) {
              this.location_ = tag.substring(prefix.length) || '';
              return true;
            }
          }, this);
        }, this);
      }
      return this.location_;
    },

    /**
     * @return {string} The description of the destination, or an empty string,
     *     if it was not provided.
     */
    get description() {
      return this.description_;
    },

    /** @return {!Array.<string>} Tags associated with the destination. */
    get tags() {
      return this.tags_.slice(0);
    },

    /** @return {string} Cloud ID associated with the destination */
    get cloudID() {
      return this.cloudID_;
    },

    /** @return {print_preview.Cdd} Print capabilities of the destination. */
    get capabilities() {
      return this.capabilities_;
    },

    /**
     * @param {!print_preview.Cdd} capabilities Print capabilities of the
     *     destination.
     */
    set capabilities(capabilities) {
      this.capabilities_ = capabilities;
    },

    /**
     * @return {!print_preview.Destination.ConnectionStatus} Connection status
     *     of the print destination.
     */
    get connectionStatus() {
      return this.connectionStatus_;
    },

    /**
     * @param {!print_preview.Destination.ConnectionStatus} status Connection
     *     status of the print destination.
     */
    set connectionStatus(status) {
      this.connectionStatus_ = status;
    },

    /** @return {boolean} Whether the destination is considered offline. */
    get isOffline() {
      return arrayContains([print_preview.Destination.ConnectionStatus.OFFLINE,
                            print_preview.Destination.ConnectionStatus.DORMANT],
                            this.connectionStatus_);
    },

    /** @return {string} Human readable status for offline destination. */
    get offlineStatusText() {
      if (!this.isOffline) {
        return '';
      }
      var offlineDurationMs = Date.now() - this.lastAccessTime_;
      var offlineMessageId;
      if (offlineDurationMs > 31622400000.0) {  // One year.
        offlineMessageId = 'offlineForYear';
      } else if (offlineDurationMs > 2678400000.0) {  // One month.
        offlineMessageId = 'offlineForMonth';
      } else if (offlineDurationMs > 604800000.0) {  // One week.
        offlineMessageId = 'offlineForWeek';
      } else {
        offlineMessageId = 'offline';
      }
      return localStrings.getString(offlineMessageId);
    },

    /**
     * @return {number} Number of milliseconds since the epoch when the printer
     *     was last accessed.
     */
    get lastAccessTime() {
      return this.lastAccessTime_;
    },

    /** @return {string} Relative URL of the destination's icon. */
    get iconUrl() {
      if (this.id_ == Destination.GooglePromotedId.DOCS) {
        return Destination.IconUrl_.DOCS;
      } else if (this.id_ == Destination.GooglePromotedId.FEDEX) {
        return Destination.IconUrl_.FEDEX;
      } else if (this.id_ == Destination.GooglePromotedId.SAVE_AS_PDF) {
        return Destination.IconUrl_.PDF;
      } else if (this.isLocal) {
        return Destination.IconUrl_.LOCAL;
      } else if (this.type_ == Destination.Type.MOBILE && this.isOwned_) {
        return Destination.IconUrl_.MOBILE;
      } else if (this.type_ == Destination.Type.MOBILE) {
        return Destination.IconUrl_.MOBILE_SHARED;
      } else if (this.isOwned_) {
        return Destination.IconUrl_.CLOUD;
      } else {
        return Destination.IconUrl_.CLOUD_SHARED;
      }
    },

    /**
     * @return {?boolean} Whether the user has accepted the terms-of-service of
     *     the print destination or {@code null} if a terms-of-service does not
     *     apply.
     */
    get isTosAccepted() {
      return this.isTosAccepted_;
    },

    /**
     * @param {?boolean} Whether the user has accepted the terms-of-service of
     *     the print destination or {@code null} if a terms-of-service does not
     *     apply.
     */
    set isTosAccepted(isTosAccepted) {
      this.isTosAccepted_ = isTosAccepted;
    },

    /**
     * @return {!Array.<string>} Properties (besides display name) to match
     *     search queries against.
     */
    get extraPropertiesToMatch() {
      return [this.location, this.description];
    },

    /**
     * Matches a query against the destination.
     * @param {!RegExp} query Query to match against the destination.
     * @return {boolean} {@code true} if the query matches this destination,
     *     {@code false} otherwise.
     */
    matches: function(query) {
      return this.displayName_.match(query) ||
          this.extraPropertiesToMatch.some(function(property) {
            return property.match(query);
          });
    }
  };

  /**
   * The CDD (Cloud Device Description) describes the capabilities of a print
   * destination.
   *
   * @typedef {{
   *   version: string,
   *   printer: {
   *     vendor_capability: !Array.<{Object}>,
   *     collate: {default: boolean=}=,
   *     color: {
   *       option: !Array.<{
   *         type: string=,
   *         vendor_id: string=,
   *         custom_display_name: string=,
   *         is_default: boolean=
   *       }>
   *     }=,
   *     copies: {default: number=, max: number=}=,
   *     duplex: {option: !Array.<{type: string=, is_default: boolean=}>}=,
   *     page_orientation: {
   *       option: !Array.<{type: string=, is_default: boolean=}>
   *     }=
   *   }
   * }}
   */
  var Cdd = Object;

  // Export
  return {
    Destination: Destination,
    Cdd: Cdd
  };
});
<|endoftext|>


<commit_before>/**
* for graphical presentation of GEBVs of a trait.
* With capability for zooming in for selected area.
* Double clicking zooms in by 50%.
* @author Isaak Y Tecle <iyt2@cornell.edu>
*
*/

jQuery(document).ready( function() {
        plotGebvScatter();
    });

function plotGebvScatter() {

    var popId          = jQuery('#model_id').val();
    var traitId        = jQuery('#trait_id').val();
    var comboPopsId    = jQuery('#combo_pops_id').val();
    var popsList       = jQuery('#pops_list').val();
    var selectionPopId = jQuery('#selection_pop_id').val();

    var params;

    if(popId && !selectionPopId) {
        params = 'pop_id=' + popId + '&trait_id=' + traitId;
    } else if (comboPopsId)  {
        params = 'combo_pops_id=' + comboPopsId + '&trait_id='  + traitId + '&selection_pop_id=' + selectionPopId;
    } else if (selectionPopId) {
        params = 'pop_id=' + popId + '&trait_id=' + traitId + '&selection_pop_id=' + selectionPopId;
    }

    var action = '/solgs/trait/gebv/graph';

    var graphArray      = [];
    graphArray[0]       = [];
    var xAxisTickValues = [];
    var yAxisTickValues = [];
    var yValues         = [];

    var gebvDataRenderer = function() {
        jQuery.ajax({
                async: false,
                url: action,
                dataType:"json",
                data: params,
                success: function(data) {
                    var gebvData  = data.gebv_data;
                    var state     = data.status;

                    if (state == 'success') {
                        for (var i=0; i < gebvData.length; i++) {
                            var xD = gebvData[i][0];
                            xD     = xD.toString();
                            var yD = gebvData[i][1];
                            yD     = yD.replace(/\s/, '');
                            yD     = Number(yD);

                            xAxisTickValues.push([i, xD]);
                            yAxisTickValues.push([i, yD]);
                            yValues.push(yD);

                            graphArray[0][i]    = [];
                            graphArray[0][i][0] = xD;
                            graphArray[0][i][1] = yD;

                        }
                    }
                }
            });

        return {'bothAxisValues' : graphArray,
                'xAxisValues'    : xAxisTickValues,
                'yAxisValues'    : yAxisTickValues,
                'yValues'        : yValues
                };

    };

    var allData     = gebvDataRenderer();

    var xAxisValues = allData.xAxisValues;
    var yAxisValues = allData.yAxisValues;
    var yValues     = allData.yValues;
    var minY        = Math.min.apply(Math, yValues);
    var maxY        = Math.max.apply(Math, yValues);
    var minYLabel   = minY - (0.2*minY);
    var maxYLabel   = maxY + (0.2*maxY);
    var plotData    = allData.bothAxisValues;

    if (plotData == 'undefined') {
        var message = 'There is no GEBV data to plot. Please report this problem';
        jQuery('#gebvPlot2').append(message).show();
    } else {
        var options = {
            series: {
                lines: {
                    show: true
                },
                points: {
                    show: true
                },
            },
            grid: {
                show: true,
                clickable: true,
                hoverable: true,
            },
            selection: {
                mode: 'xy',
                color: '#0066CC',
            },
            xaxis:{
                mode: 'categories',
                ticks: '',
                tickColor: '#ffffff',
                axisLabel: 'Clones',
                position: 'bottom',
                axisLabelPadding: 20,
                color: '#0066CC',
            },

            yaxis: {
                min: null,
                max: null,
                axisLabel: 'GEBVs',
                position: 'left',
                color: '#0066CC',
            },
            zoom: {
                interactive: true,
                amount: 1.5,
                trigger: 'dblclick',
            },
            pan: {
                interactive: false,
            },
        };

        var plot        = jQuery.plot('#gebvPlot2', plotData, options);

        var overview = jQuery.plot(jQuery("#gebvPlotOverview"), plotData, {
                series: {
                    lines: {
                        show: true,
                        lineWidth: 2
                    },
                    shadowSize: 0
                },
                xaxis: {
                    ticks: [],
                    mode: "categories",
                    label: 'Genotypes',
                },
                selection: {
                    mode: "xy",
                },
                colors: ["#cc0000", "#0066CC"],
            });

        jQuery("#gebvPlot2").bind("plotselected", function (event, ranges) {
                //zoom in
                plot = jQuery.plot(jQuery("#gebvPlot2"), plotData,
                                   jQuery.extend(true, {}, options, {
                                           xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
                                           yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to },
                                       }));

                plot.setSelection(ranges, true);
                overview.setSelection(ranges, true);
            });

        //highlight selected area on the overview plot
        jQuery("#gebvPlotOverview").bind("plotselected", function (event, ranges) {
                plot.setSelection(ranges);
            });

        //reset zooming. Need to figure out zooming out
        jQuery("#gebvzoom-reset").click(function (e) {
                // var plot = jQuery.plot("#gebvPlot2", plotData, options);
                location.reload();
                // plot.zoomOut();
            });

        //given datapoint position and content, displays a styled tooltip
        var showTooltip = function(lt, tp, content) {
            jQuery('
' + content + '
').css({
                    position: 'absolute',
                    display: 'none',
                    'font-weight': 'bold',
                    top: tp + 10,
                    left: lt + 10,
                    border: '1px solid #ffaf55',
                    padding: '2px'
                }).appendTo("body").show();
        };

        var zoomHelp = function (lt, tp) {

            var help_txt = '
' +
                            'To zoom in, select an area ' +
                            'on the plot and release or double click at any ' +
                            'point on the plot.' +
                           '
';

            jQuery('
' + help_txt  + '
').css({
                    position: 'absolute',
                        display: 'none',
                        'font-weight': 'bold',
                        top: tp + 35,
                        left: lt + 30,
                        border: '1px solid #C9BE62',
                        padding: '2px'
                        }).appendTo("body").show();
        };

        //calls the tooltip display function and binds the 'plotover' event to
        //the plot
        var previousPoint = null;
             var useTooltip = jQuery("#gebvPlot2").bind("plothover", function (event, pos, item) {
                     if (item) {
                         if (previousPoint != item.dataIndex) {
                             previousPoint = item.dataIndex;

                             jQuery("#tooltip").remove();
                             jQuery("#tooltip_zoom").remove();

                             var x = item.datapoint[0];
                             var y = item.datapoint[1].toFixed(2);
                             var content = xAxisTickValues[x][1] + ', ' + y;

                             showTooltip(item.pageX, item.pageY, content);
                             zoomHelp(item.pageX, item.pageY);
                         }
                     }
                     else {
                         jQuery("#tooltip").remove();
                         jQuery("#tooltip_zoom").remove();
                         previousPoint = null;
                     }
                 });

             //inverse plot: plots genotypes from high to low gebv values or vice versa
             var inverseOptions = {
                 series: {
                     lines: {
                         show: true
                     },
                     points: {
                         show: true
                     },
                 },
                 grid: {
                     show: true,
                     clickable: true,
                     hoverable: true,
                 },
                 selection: {
                     mode: 'xy',
                     color: '#0066CC',
                 },
                 xaxis:{
                     mode: 'categories',
                     ticks: '',
                     tickColor: '#ffffff',
                     axisLabel: 'Genotypes',
                     position: 'bottom',
                     axisLabelPadding: 20,
                     color: '#0066CC',
                     transform: function (v) { return -v; },
                     inverseTransform: function (v) { return -v; }
                 },

                 yaxis: {
                     min: null,
                     max: null,
                     axisLabel: 'Trait GEBVs',
                     position: 'left',
                     color: '#0066CC',
                 },
                 zoom: {
                     interactive: true,
                     amount: 1.5,
                     trigger: 'dblclick',
                 },
                 pan: {
                     interactive: false,
                 },
             };

             function normalPlot() {
                 jQuery.plot('#gebvPlot2', plotData, options);
             }

             function inversePlot() {
                 jQuery.plot('#gebvPlot2', plotData, inverseOptions);
             }

             jQuery("#inverse_gebv_plot").alternateFunctions(normalPlot, inversePlot);

    }
}

<commit_msg>add geno protocol args<commit_after>/**
* for graphical presentation of GEBVs of a trait.
* With capability for zooming in for selected area.
* Double clicking zooms in by 50%.
* @author Isaak Y Tecle <iyt2@cornell.edu>
*
*/

jQuery(document).ready( function() {
        plotGebvScatter();
    });

function plotGebvScatter() {

    var popId          = jQuery('#model_id').val();
    var traitId        = jQuery('#trait_id').val();
    var comboPopsId    = jQuery('#combo_pops_id').val();
    var popsList       = jQuery('#pops_list').val();
    var selectionPopId = jQuery('#selection_pop_id').val();
    var protocolId     = jQuery('#genotyping_protocol_id').val();

    var params = {
	'training_pop_id': popId,
	'combo_pops_id': comboPopsId,
	'selection_pop_id': selectionPopId,
	'genotyping_protocol_id': protocolId,
	'trait_id': traitId
    };

    var action = '/solgs/trait/gebv/graph';

    var graphArray      = [];
    graphArray[0]       = [];
    var xAxisTickValues = [];
    var yAxisTickValues = [];
    var yValues         = [];

    var gebvDataRenderer = function() {
        jQuery.ajax({
                async: false,
                url: action,
                dataType:"json",
                data: params,
                success: function(data) {
                    var gebvData  = data.gebv_data;
                    var state     = data.status;

                    if (state == 'success') {
                        for (var i=0; i < gebvData.length; i++) {
                            var xD = gebvData[i][0];
                            xD     = xD.toString();
                            var yD = gebvData[i][1];
                            yD     = yD.replace(/\s/, '');
                            yD     = Number(yD);

                            xAxisTickValues.push([i, xD]);
                            yAxisTickValues.push([i, yD]);
                            yValues.push(yD);

                            graphArray[0][i]    = [];
                            graphArray[0][i][0] = xD;
                            graphArray[0][i][1] = yD;

                        }
                    }
                }
            });

        return {'bothAxisValues' : graphArray,
                'xAxisValues'    : xAxisTickValues,
                'yAxisValues'    : yAxisTickValues,
                'yValues'        : yValues
                };

    };

    var allData     = gebvDataRenderer();

    var xAxisValues = allData.xAxisValues;
    var yAxisValues = allData.yAxisValues;
    var yValues     = allData.yValues;
    var minY        = Math.min.apply(Math, yValues);
    var maxY        = Math.max.apply(Math, yValues);
    var minYLabel   = minY - (0.2*minY);
    var maxYLabel   = maxY + (0.2*maxY);
    var plotData    = allData.bothAxisValues;

    if (plotData == 'undefined') {
        var message = 'There is no GEBV data to plot. Please report this problem';
        jQuery('#gebvPlot2').append(message).show();
    } else {
        var options = {
            series: {
                lines: {
                    show: true
                },
                points: {
                    show: true
                },
            },
            grid: {
                show: true,
                clickable: true,
                hoverable: true,
            },
            selection: {
                mode: 'xy',
                color: '#0066CC',
            },
            xaxis:{
                mode: 'categories',
                ticks: '',
                tickColor: '#ffffff',
                axisLabel: 'Clones',
                position: 'bottom',
                axisLabelPadding: 20,
                color: '#0066CC',
            },

            yaxis: {
                min: null,
                max: null,
                axisLabel: 'GEBVs',
                position: 'left',
                color: '#0066CC',
            },
            zoom: {
                interactive: true,
                amount: 1.5,
                trigger: 'dblclick',
            },
            pan: {
                interactive: false,
            },
        };

        var plot        = jQuery.plot('#gebvPlot2', plotData, options);

        var overview = jQuery.plot(jQuery("#gebvPlotOverview"), plotData, {
                series: {
                    lines: {
                        show: true,
                        lineWidth: 2
                    },
                    shadowSize: 0
                },
                xaxis: {
                    ticks: [],
                    mode: "categories",
                    label: 'Genotypes',
                },
                selection: {
                    mode: "xy",
                },
                colors: ["#cc0000", "#0066CC"],
            });

        jQuery("#gebvPlot2").bind("plotselected", function (event, ranges) {
                //zoom in
                plot = jQuery.plot(jQuery("#gebvPlot2"), plotData,
                                   jQuery.extend(true, {}, options, {
                                           xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
                                           yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to },
                                       }));

                plot.setSelection(ranges, true);
                overview.setSelection(ranges, true);
            });

        //highlight selected area on the overview plot
        jQuery("#gebvPlotOverview").bind("plotselected", function (event, ranges) {
                plot.setSelection(ranges);
            });

        //reset zooming. Need to figure out zooming out
        jQuery("#gebvzoom-reset").click(function (e) {
                // var plot = jQuery.plot("#gebvPlot2", plotData, options);
                location.reload();
                // plot.zoomOut();
            });

        //given datapoint position and content, displays a styled tooltip
        var showTooltip = function(lt, tp, content) {
            jQuery('
' + content + '
').css({
                    position: 'absolute',
                    display: 'none',
                    'font-weight': 'bold',
                    top: tp + 10,
                    left: lt + 10,
                    border: '1px solid #ffaf55',
                    padding: '2px'
                }).appendTo("body").show();
        };

        var zoomHelp = function (lt, tp) {

            var help_txt = '
' +
                            'To zoom in, select an area ' +
                            'on the plot and release or double click at any ' +
                            'point on the plot.' +
                           '
';

            jQuery('
' + help_txt  + '
').css({
                    position: 'absolute',
                        display: 'none',
                        'font-weight': 'bold',
                        top: tp + 35,
                        left: lt + 30,
                        border: '1px solid #C9BE62',
                        padding: '2px'
                        }).appendTo("body").show();
        };

        //calls the tooltip display function and binds the 'plotover' event to
        //the plot
        var previousPoint = null;
             var useTooltip = jQuery("#gebvPlot2").bind("plothover", function (event, pos, item) {
                     if (item) {
                         if (previousPoint != item.dataIndex) {
                             previousPoint = item.dataIndex;

                             jQuery("#tooltip").remove();
                             jQuery("#tooltip_zoom").remove();

                             var x = item.datapoint[0];
                             var y = item.datapoint[1].toFixed(2);
                             var content = xAxisTickValues[x][1] + ', ' + y;

                             showTooltip(item.pageX, item.pageY, content);
                             zoomHelp(item.pageX, item.pageY);
                         }
                     }
                     else {
                         jQuery("#tooltip").remove();
                         jQuery("#tooltip_zoom").remove();
                         previousPoint = null;
                     }
                 });

             //inverse plot: plots genotypes from high to low gebv values or vice versa
             var inverseOptions = {
                 series: {
                     lines: {
                         show: true
                     },
                     points: {
                         show: true
                     },
                 },
                 grid: {
                     show: true,
                     clickable: true,
                     hoverable: true,
                 },
                 selection: {
                     mode: 'xy',
                     color: '#0066CC',
                 },
                 xaxis:{
                     mode: 'categories',
                     ticks: '',
                     tickColor: '#ffffff',
                     axisLabel: 'Genotypes',
                     position: 'bottom',
                     axisLabelPadding: 20,
                     color: '#0066CC',
                     transform: function (v) { return -v; },
                     inverseTransform: function (v) { return -v; }
                 },

                 yaxis: {
                     min: null,
                     max: null,
                     axisLabel: 'Trait GEBVs',
                     position: 'left',
                     color: '#0066CC',
                 },
                 zoom: {
                     interactive: true,
                     amount: 1.5,
                     trigger: 'dblclick',
                 },
                 pan: {
                     interactive: false,
                 },
             };

             function normalPlot() {
                 jQuery.plot('#gebvPlot2', plotData, options);
             }

             function inversePlot() {
                 jQuery.plot('#gebvPlot2', plotData, inverseOptions);
             }

             jQuery("#inverse_gebv_plot").alternateFunctions(normalPlot, inversePlot);

    }
}

<|endoftext|>


<commit_before>exports.config = {

	//
	// =================
	// Service Providers
	// =================
	// WebdriverIO supports Sauce Labs, Browserstack and Testing Bot (other cloud providers
	// should work too though). These services define specific user and key (or access key)
	// values you need to put in here in order to connect to these services.
	//
	user: process.env.SAUCE_USERNAME,
	key: process.env.SAUCE_ACCESS_KEY,

	//
	// If you are using Sauce Labs, WebdriverIO takes care to update the job information
	// once the test is done. This option is set to `true` by default.
	//
	updateJob: false, // will get overwritten

	//
	// ==================
	// Specify Test Files
	// ==================
	// Define which test specs should run. The pattern is relative to the directory
	// from which `wdio` was called. Notice that, if you are calling `wdio` from an
	// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
	// directory is where your package.json resides, so `wdio` will be called from there.
	//
	specs: [
        './js/tests/integration_tests/drilldown_tests.js'
	],
	// Patterns to exclude.
	exclude: [
        // 'path/to/excluded/files'
	],
	//
	// ============
	// Capabilities
	// ============
	// Define your capabilities here. WebdriverIO can run multiple capabilties at the same
	// time. Depending on the number of capabilities, WebdriverIO launches several test
	// sessions. Within your capabilities you can overwrite the spec and exclude option in
	// order to group specific specs to a specific capability.
	//
	// If you have trouble getting all important capabilities together, check out the
	// Sauce Labs platform configurator - a great tool to configure your capabilities:
	// https://docs.saucelabs.com/reference/platforms-configurator
	//
	capabilities: [{
		browserName: 'chrome',
		platform: "Windows 7",
		name: "Drilldown - Chrome",
		tags: ["Drilldown", "Chrome"],
		build: process.env.TRAVIS_BUILD_NUMBER,
		'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER
	}],
	//
	// ===================
	// Test Configurations
	// ===================
	// Define all options that are relevant for the WebdriverIO instance here
	//
	// Level of logging verbosity.
	logLevel: 'silent',
	//
	// Enables colors for log output.
	coloredLogs: true,
	//
	// Saves a screenshot to a given path if a command fails.
	screenshotPath: './',
	//
	// Set a base URL in order to shorten url command calls. If your url parameter starts
	// with "/", the base url gets prepended.
	baseUrl: 'http://webdriverjs.christian-bromann.com',
	//
	// Default timeout for all waitForXXX commands.
	waitforTimeout: 10000,
	//
	// Initialize the browser instance with a WebdriverIO plugin. The object should have the
	// plugin name as key and the desired plugin options as property. Make sure you have
	// the plugin installed before running any tests. The following plugins are currently
	// available:
	// WebdriverCSS: https://github.com/webdriverio/webdrivercss
	// WebdriverRTC: https://github.com/webdriverio/webdriverrtc
	// Browserevent: https://github.com/webdriverio/browserevent
	// plugins: {
	//     webdrivercss: {
	//         screenshotRoot: 'my-shots',
	//         failedComparisonsRoot: 'diffs',
	//         misMatchTolerance: 0.05,
	//         screenWidth: [320,480,640,1024]
	//     },
	//     webdriverrtc: {},
	//     browserevent: {}
	// },
	//
	// Framework you want to run your specs with.
	// The following are supported: mocha, jasmine and cucumber
	// see also: http://webdriver.io/guide/testrunner/frameworks.html
	//
	// Make sure you have the node package for the specific framework installed before running
	// any tests. If not please install the following package:
	// Mocha: `$ npm install mocha`
	// Jasmine: `$ npm install jasmine`
	// Cucumber: `$ npm install cucumber`
	framework: 'mocha', // gets overwritten in gruntfile
	//
	// Test reporter for stdout.
	// The following are supported: dot (default), spec and xunit
	// see also: http://webdriver.io/guide/testrunner/reporters.html
	reporter: 'spec',

	//
	// Options to be passed to Mocha.
	// See the full list at http://mochajs.org/
	mochaOpts: {
		ui: 'bdd'
	},

	//
	// =====
	// Hooks
	// =====
	// Run functions before or after the test. If one of them returns with a promise, WebdriverIO
	// will wait until that promise got resolved to continue.
	// see also: http://webdriver.io/guide/testrunner/hooks.html
	//
	// Gets executed before all workers get launched.
	onPrepare: function () {
		// do something
	},
	//
	// Gets executed before test execution begins. At this point you will have access to all global
	// variables like `browser`. It is the perfect place to define custom commands.
	before: function () {
		// do something
	},
	//
	// Gets executed after all tests are done. You still have access to all global variables from
	// the test.
	after: function () {
		// do something
	},
	//
	// Gets executed after all workers got shut down and the process is about to exit. It is not
	// possible to defer the end of the process using a promise.
	onComplete: function () {
		// do something
	}
};<commit_msg>Update test config<commit_after>exports.config = {

	//
	// =================
	// Service Providers
	// =================
	// WebdriverIO supports Sauce Labs, Browserstack and Testing Bot (other cloud providers
	// should work too though). These services define specific user and key (or access key)
	// values you need to put in here in order to connect to these services.
	//
	user: process.env.SAUCE_USERNAME,
	key: process.env.SAUCE_ACCESS_KEY,

	//
	// If you are using Sauce Labs, WebdriverIO takes care to update the job information
	// once the test is done. This option is set to `true` by default.
	//
	updateJob: false, // will get overwritten

	//
	// ==================
	// Specify Test Files
	// ==================
	// Define which test specs should run. The pattern is relative to the directory
	// from which `wdio` was called. Notice that, if you are calling `wdio` from an
	// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
	// directory is where your package.json resides, so `wdio` will be called from there.
	//
	specs: [
        './js/tests/integration_tests/drilldown_tests.js'
	],
	// Patterns to exclude.
	exclude: [
        // 'path/to/excluded/files'
	],
	//
	// ============
	// Capabilities
	// ============
	// Define your capabilities here. WebdriverIO can run multiple capabilties at the same
	// time. Depending on the number of capabilities, WebdriverIO launches several test
	// sessions. Within your capabilities you can overwrite the spec and exclude option in
	// order to group specific specs to a specific capability.
	//
	// If you have trouble getting all important capabilities together, check out the
	// Sauce Labs platform configurator - a great tool to configure your capabilities:
	// https://docs.saucelabs.com/reference/platforms-configurator
	//
	capabilities: [{
		browserName: 'chrome',
		platform: "Windows 7",
		name: "Drilldown - Chrome",
		tags: ["Drilldown", "Chrome"],
		build: process.env.TRAVIS_BUILD_NUMBER
	}],
	//
	// ===================
	// Test Configurations
	// ===================
	// Define all options that are relevant for the WebdriverIO instance here
	//
	// Level of logging verbosity.
	logLevel: 'silent',
	//
	// Enables colors for log output.
	coloredLogs: true,
	//
	// Saves a screenshot to a given path if a command fails.
	screenshotPath: './',
	//
	// Set a base URL in order to shorten url command calls. If your url parameter starts
	// with "/", the base url gets prepended.
	baseUrl: 'http://webdriverjs.christian-bromann.com',
	//
	// Default timeout for all waitForXXX commands.
	waitforTimeout: 10000,
	//
	// Initialize the browser instance with a WebdriverIO plugin. The object should have the
	// plugin name as key and the desired plugin options as property. Make sure you have
	// the plugin installed before running any tests. The following plugins are currently
	// available:
	// WebdriverCSS: https://github.com/webdriverio/webdrivercss
	// WebdriverRTC: https://github.com/webdriverio/webdriverrtc
	// Browserevent: https://github.com/webdriverio/browserevent
	// plugins: {
	//     webdrivercss: {
	//         screenshotRoot: 'my-shots',
	//         failedComparisonsRoot: 'diffs',
	//         misMatchTolerance: 0.05,
	//         screenWidth: [320,480,640,1024]
	//     },
	//     webdriverrtc: {},
	//     browserevent: {}
	// },
	//
	// Framework you want to run your specs with.
	// The following are supported: mocha, jasmine and cucumber
	// see also: http://webdriver.io/guide/testrunner/frameworks.html
	//
	// Make sure you have the node package for the specific framework installed before running
	// any tests. If not please install the following package:
	// Mocha: `$ npm install mocha`
	// Jasmine: `$ npm install jasmine`
	// Cucumber: `$ npm install cucumber`
	framework: 'mocha', // gets overwritten in gruntfile
	//
	// Test reporter for stdout.
	// The following are supported: dot (default), spec and xunit
	// see also: http://webdriver.io/guide/testrunner/reporters.html
	reporter: 'spec',

	//
	// Options to be passed to Mocha.
	// See the full list at http://mochajs.org/
	mochaOpts: {
		ui: 'bdd'
	},

	//
	// =====
	// Hooks
	// =====
	// Run functions before or after the test. If one of them returns with a promise, WebdriverIO
	// will wait until that promise got resolved to continue.
	// see also: http://webdriver.io/guide/testrunner/hooks.html
	//
	// Gets executed before all workers get launched.
	onPrepare: function () {
		// do something
	},
	//
	// Gets executed before test execution begins. At this point you will have access to all global
	// variables like `browser`. It is the perfect place to define custom commands.
	before: function () {
		// do something
	},
	//
	// Gets executed after all tests are done. You still have access to all global variables from
	// the test.
	after: function () {
		// do something
	},
	//
	// Gets executed after all workers got shut down and the process is about to exit. It is not
	// possible to defer the end of the process using a promise.
	onComplete: function () {
		// do something
	}
};<|endoftext|>


<commit_before>import {apiKey} from 'model/apiKey';
import {items} from 'model/gw2Data/items';
import {characters} from 'model/gw2Data/characters';
import {materials} from 'model/gw2Data/materials';
import {vault} from 'model/gw2Data/vault';
import {bank} from 'model/gw2Data/bank';
import {accountInventory} from 'model/gw2Data/accountInventory';

let dataRef;
let materialRef;

export const inventory = {
  get() {
    return dataRef;
  },
  load() {
    const loadDeferred = new $.Deferred();
    const params = {
      access_token: apiKey.getKey(),
      lang: 'en',
      page: 0
    };
    const waiting = [];

    // 載入材料分類表
    waiting.push(materials.load());

    // 載入角色包包與物品資料
    waiting.push(characters.load());

    // 載入倉庫與物品資料
    waiting.push(vault.load());

    //載入銀行
    waiting.push(bank.load());

    //載入 shared inventory slots
    waiting.push(accountInventory.load());

    $.when.apply($.when, waiting).done(() => {
      const waitingLoadItems = [];
      //載入銀行物品資料
      waitingLoadItems.push(items.loadByBankList(bank.get()));
      waitingLoadItems.push(items.loadByVaultList(vault.get()));
      waitingLoadItems.push(items.loadByAccountInventoryList(accountInventory.get()));

      //全部載入完畢後才 merge
      $.when.apply($.when, waitingLoadItems).done(() => {

          dataRef = [];
          materialRef = {};

          const vaultDataRef = vault.get().map((material, index) => {
            if (material) {
              materialRef[material.id] = materials.get(material.category).name;
              const itemInfo = items.get(material.id);
              const position = 'Vault|' + (index + 1);
              const item = new Item(position, material, itemInfo);
              return item.toJSON();
            }
          });
          $.merge(dataRef, vaultDataRef);

          const characterDataRef = [];
          characters.get().forEach((character) => {
            character._data.equipment.forEach((equipmentItem) => {
              if (equipmentItem) {
                const itemInfo = items.get(equipmentItem.id);
                const position = character.name + `<br /><span class='small light'>(equipped)</span>`;
                equipmentItem.count = 1;
                const item = new Item(position, equipmentItem, itemInfo);
                characterDataRef.push( item.toJSON() );
              }
            });
            character._data.bags.forEach((bag) => {
              if (bag) {
                bag.inventory.forEach((bagItem) => {
                  if (bagItem) {
                    const itemInfo = items.get(bagItem.id);
                    const position = character.name + `<br /><span class='small light'>(bag)</span>`;
                    const item = new Item(position, bagItem, itemInfo);
                    characterDataRef.push( item.toJSON() );
                  }
                });
              }
            });
          });
          $.merge(dataRef, characterDataRef);

          const bankDataRef = bank.get().map((bankItem, index) => {
            if (bankItem) {
              const itemInfo = items.get(bankItem.id);
              const position = 'Bank|' + (index + 1);
              const item = new Item(position, bankItem, itemInfo);
              return item.toJSON();
            }
          });
          $.merge(dataRef, bankDataRef);

          const accountInventoryDataRef = accountInventory.get().map((accountInventoryItem, index) => {
            if (accountInventoryItem) {
              const itemInfo = items.get(accountInventoryItem.id);
              const position = 'Shared|' + (index + 1);
              const item = new Item(position, accountInventoryItem, itemInfo);
              return item.toJSON();
            }
          });
          $.merge(dataRef, accountInventoryDataRef);

          loadDeferred.resolve(dataRef);

      });

    });
    return loadDeferred;
  }
};

class Item {
  constructor(position, data, itemInfo) {
    this._data = data || {};
    this._data.position = position || '';
    this._ref = itemInfo || {};
    return this;
  }
  toJSON() {
    const result = {};
    const keys = [ 'icon', 'name', 'count', 'type', 'level', 'rarity', 'position', 'binding', 'id', 'category' ];
    keys.forEach((key) => {
      result[key] = this[key];
    });
    //Object.keys(this._data).forEach((key) => {
    //  result[key] = this[key];
    //});
    //Object.keys(this._ref).forEach((key) => {
    //  result[key] = this[key];
    //});
    return result;
  }
  get id() {
    return this._data.id || '';
  }
  get icon() {
    const icon = this._ref.icon || '';
    const rarity = this._ref.rarity || '';
    const description = getToolTipHtml(this._ref);
    return `<img class='large solo item icon ${rarity}' data-toggle='tooltip' data-html='true' data-placement='right' title='${description}' src='${icon}' />`;
  }
  get name() {
    const name = this._ref.name || '';
    const rarity = this._ref.rarity || '';
    return `<span class='bold ${rarity}'>${name}</span>`;
  }
  get count() {
    return parseInt(this._data.count,10);
  }
  get type() {
    let type = this._ref.type || '';
    if ( type == 'UpgradeComponent' ) {
      type = 'Upgrades';
    } else if ( type == 'CraftingMaterial' ) {
      type = 'Material';
    }
    return type;
  }
  get level() {
    return this._ref.level || '';
  }
  get rarity() {
    return this._ref.rarity || '';
  }
  get position() {
    let html = this._data.position || ''
    if (this._data.category) {
      const category = materials.get(this._data.category).name || '';
      return html += `<br /><span class='small light'>${category}</span>`;
    } else {
      return html;
    }
  }
  get binding() {
    const binding = this._data.binding;
    const bound_to = this._data.bound_to;
    if (binding) {
      if (bound_to) {
        return bound_to;
      } else {
        return binding;
      }
    } else {
      return '';
    }
  }
  get description() {
    return this._ref.description || '';
  }
  get category() {
    if (materialRef[this._data.id]) {
      return materialRef[this._data.id] || '';
    } else {
      return '';
    }
  }
}

function escapeHtml(data) {

  const entityMap = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': '"',
    "'": ''',
    "/": '/'
  };

  return String(data).replace(/[&<>"'\/]/g, function (s) {
    return entityMap[s];
  });

  let escape = document.createElement('textarea');
  escape.textContent = data;
  let html = escape.innerHTML;
  html = html.replace(/(?:\r\n|\r|\n)/g, '
').replace();

}

function getToolTipHtml(item) {
  let html = '';
  if (item.details) {
    if (item.details.infix_upgrade) {
      if (item.details.infix_upgrade.attributes) {
        item.details.infix_upgrade.attributes.forEach((attribute) => {
          html += `${attribute.attribute}: ${attribute.modifier}<br />`;
        });
      }
      if (item.details.infix_upgrade.buff) {
//        item.details.infix_upgrade.buff.forEach((skill) => {
//          const description = skill.description || '';
//          html += escapeHtml(description);
//        });
      }
    }
    if (item.details.stat_choices) {

    }
    if (item.details.description) {
      const description = item.details.description || '';
      html += escapeHtml(description);
    } else if (item.description) {
      const description = item.description || '';
      html += escapeHtml(description);
    }
  } else if (item.description) {
    const description = item.description || '';
    html += escapeHtml(description);
  }
  return html;
}
<commit_msg>add equipped bag to inventory tab<commit_after>import {apiKey} from 'model/apiKey';
import {items} from 'model/gw2Data/items';
import {characters} from 'model/gw2Data/characters';
import {materials} from 'model/gw2Data/materials';
import {vault} from 'model/gw2Data/vault';
import {bank} from 'model/gw2Data/bank';
import {accountInventory} from 'model/gw2Data/accountInventory';

let dataRef;
let materialRef;

export const inventory = {
  get() {
    return dataRef;
  },
  load() {
    const loadDeferred = new $.Deferred();
    const params = {
      access_token: apiKey.getKey(),
      lang: 'en',
      page: 0
    };
    const waiting = [];

    // 載入材料分類表
    waiting.push(materials.load());

    // 載入角色包包與物品資料
    waiting.push(characters.load());

    // 載入倉庫與物品資料
    waiting.push(vault.load());

    //載入銀行
    waiting.push(bank.load());

    //載入 shared inventory slots
    waiting.push(accountInventory.load());

    $.when.apply($.when, waiting).done(() => {
      const waitingLoadItems = [];
      //載入銀行物品資料
      waitingLoadItems.push(items.loadByBankList(bank.get()));
      waitingLoadItems.push(items.loadByVaultList(vault.get()));
      waitingLoadItems.push(items.loadByAccountInventoryList(accountInventory.get()));

      //全部載入完畢後才 merge
      $.when.apply($.when, waitingLoadItems).done(() => {

          dataRef = [];
          materialRef = {};

          const vaultDataRef = vault.get().map((material, index) => {
            if (material) {
              materialRef[material.id] = materials.get(material.category).name;
              const itemInfo = items.get(material.id);
              const position = 'Vault|' + (index + 1);
              const item = new Item(position, material, itemInfo);
              return item.toJSON();
            }
          });
          $.merge(dataRef, vaultDataRef);

          const characterDataRef = [];
          characters.get().forEach((character) => {
            character._data.equipment.forEach((equipmentItem) => {
              if (equipmentItem) {
                const itemInfo = items.get(equipmentItem.id);
                const position = character.name + `<br /><span class='small light'>(equipped)</span>`;
                equipmentItem.count = 1;
                const item = new Item(position, equipmentItem, itemInfo);
                characterDataRef.push( item.toJSON() );
              }
            });
            character._data.bags.forEach((bag) => {
              if (bag) {
                const itemInfo = items.get(bag.id);
                const position = character.name + `<br /><span class='small light'>(equipped)</span>`;
                bag.count = 1;
                const item = new Item(position, bag, itemInfo);
                characterDataRef.push( item.toJSON() );

                bag.inventory.forEach((bagItem) => {
                  if (bagItem) {
                    const itemInfo = items.get(bagItem.id);
                    const position = character.name + `<br /><span class='small light'>(bag)</span>`;
                    const item = new Item(position, bagItem, itemInfo);
                    characterDataRef.push( item.toJSON() );
                  }
                });
              }
            });
          });
          $.merge(dataRef, characterDataRef);

          const bankDataRef = bank.get().map((bankItem, index) => {
            if (bankItem) {
              const itemInfo = items.get(bankItem.id);
              const position = 'Bank|' + (index + 1);
              const item = new Item(position, bankItem, itemInfo);
              return item.toJSON();
            }
          });
          $.merge(dataRef, bankDataRef);

          const accountInventoryDataRef = accountInventory.get().map((accountInventoryItem, index) => {
            if (accountInventoryItem) {
              const itemInfo = items.get(accountInventoryItem.id);
              const position = 'Shared|' + (index + 1);
              const item = new Item(position, accountInventoryItem, itemInfo);
              return item.toJSON();
            }
          });
          $.merge(dataRef, accountInventoryDataRef);

          loadDeferred.resolve(dataRef);

      });

    });
    return loadDeferred;
  }
};

class Item {
  constructor(position, data, itemInfo) {
    this._data = data || {};
    this._data.position = position || '';
    this._ref = itemInfo || {};
    return this;
  }
  toJSON() {
    const result = {};
    const keys = [ 'icon', 'name', 'count', 'type', 'level', 'rarity', 'position', 'binding', 'id', 'category' ];
    keys.forEach((key) => {
      result[key] = this[key];
    });
    //Object.keys(this._data).forEach((key) => {
    //  result[key] = this[key];
    //});
    //Object.keys(this._ref).forEach((key) => {
    //  result[key] = this[key];
    //});
    return result;
  }
  get id() {
    return this._data.id || '';
  }
  get icon() {
    const icon = this._ref.icon || '';
    const rarity = this._ref.rarity || '';
    const description = getToolTipHtml(this._ref);
    return `<img class='large solo item icon ${rarity}' data-toggle='tooltip' data-html='true' data-placement='right' title='${description}' src='${icon}' />`;
  }
  get name() {
    const name = this._ref.name || '';
    const rarity = this._ref.rarity || '';
    return `<span class='bold ${rarity}'>${name}</span>`;
  }
  get count() {
    return parseInt(this._data.count,10);
  }
  get type() {
    let type = this._ref.type || '';
    if ( type == 'UpgradeComponent' ) {
      type = 'Upgrades';
    } else if ( type == 'CraftingMaterial' ) {
      type = 'Material';
    }
    return type;
  }
  get level() {
    return this._ref.level || '';
  }
  get rarity() {
    return this._ref.rarity || '';
  }
  get position() {
    let html = this._data.position || ''
    if (this._data.category) {
      const category = materials.get(this._data.category).name || '';
      return html += `<br /><span class='small light'>${category}</span>`;
    } else {
      return html;
    }
  }
  get binding() {
    const binding = this._data.binding;
    const bound_to = this._data.bound_to;
    if (binding) {
      if (bound_to) {
        return bound_to;
      } else {
        return binding;
      }
    } else {
      return '';
    }
  }
  get description() {
    return this._ref.description || '';
  }
  get category() {
    if (materialRef[this._data.id]) {
      return materialRef[this._data.id] || '';
    } else {
      return '';
    }
  }
}

function escapeHtml(data) {

  const entityMap = {
    "&": "&",
    "<": "<",
    ">": ">",
    '"': '"',
    "'": ''',
    "/": '/'
  };

  return String(data).replace(/[&<>"'\/]/g, function (s) {
    return entityMap[s];
  });

  let escape = document.createElement('textarea');
  escape.textContent = data;
  let html = escape.innerHTML;
  html = html.replace(/(?:\r\n|\r|\n)/g, '
').replace();

}

function getToolTipHtml(item) {
  let html = '';
  if (item.details) {
    if (item.details.infix_upgrade) {
      if (item.details.infix_upgrade.attributes) {
        item.details.infix_upgrade.attributes.forEach((attribute) => {
          html += `${attribute.attribute}: ${attribute.modifier}<br />`;
        });
      }
      if (item.details.infix_upgrade.buff) {
//        item.details.infix_upgrade.buff.forEach((skill) => {
//          const description = skill.description || '';
//          html += escapeHtml(description);
//        });
      }
    }
    if (item.details.stat_choices) {

    }
    if (item.details.description) {
      const description = item.details.description || '';
      html += escapeHtml(description);
    } else if (item.description) {
      const description = item.description || '';
      html += escapeHtml(description);
    }
  } else if (item.description) {
    const description = item.description || '';
    html += escapeHtml(description);
  }
  return html;
}
<|endoftext|>